From 1b965270a9c273d4cf70e8808e9d28b9ada7844f Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Wed, 22 May 2024 10:41:21 +0000 Subject: [PATCH] deps: update undici to 6.18.1 PR-URL: https://github.com/nodejs/node/pull/53073 Reviewed-By: Marco Ippolito Reviewed-By: Matthew Aitken Reviewed-By: Trivikram Kamat Reviewed-By: Rafael Gonzaga Reviewed-By: Matteo Collina --- deps/undici/src/README.md | 20 ++-- deps/undici/src/lib/web/fetch/headers.js | 15 ++- deps/undici/src/lib/web/websocket/receiver.js | 2 +- deps/undici/src/lib/web/websocket/sender.js | 105 +++++++++++------- deps/undici/src/package-lock.json | 58 +++++----- deps/undici/src/package.json | 2 +- deps/undici/undici.js | 99 ++++++++++------- src/undici_version.h | 2 +- 8 files changed, 175 insertions(+), 128 deletions(-) diff --git a/deps/undici/src/README.md b/deps/undici/src/README.md index 049c5112ee7d6a..4336ef0683612e 100644 --- a/deps/undici/src/README.md +++ b/deps/undici/src/README.md @@ -123,7 +123,7 @@ This section documents our most commonly used API methods. Additional APIs are d Arguments: * **url** `string | URL | UrlObject` -* **options** [`RequestOptions`](./docs/api/Dispatcher.md#parameter-requestoptions) +* **options** [`RequestOptions`](./docs/docs/api/Dispatcher.md#parameter-requestoptions) * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher) * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET` * **maxRedirections** `Integer` - Default: `0` @@ -132,14 +132,14 @@ Returns a promise with the result of the `Dispatcher.request` method. Calls `options.dispatcher.request(options)`. -See [Dispatcher.request](./docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details, and [request examples](./examples/README.md) for examples. +See [Dispatcher.request](./docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback) for more details, and [request examples](./examples/README.md) for examples. ### `undici.stream([url, options, ]factory): Promise` Arguments: * **url** `string | URL | UrlObject` -* **options** [`StreamOptions`](./docs/api/Dispatcher.md#parameter-streamoptions) +* **options** [`StreamOptions`](./docs/docs/api/Dispatcher.md#parameter-streamoptions) * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher) * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET` * **maxRedirections** `Integer` - Default: `0` @@ -149,14 +149,14 @@ Returns a promise with the result of the `Dispatcher.stream` method. Calls `options.dispatcher.stream(options, factory)`. -See [Dispatcher.stream](./docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details. +See [Dispatcher.stream](./docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback) for more details. ### `undici.pipeline([url, options, ]handler): Duplex` Arguments: * **url** `string | URL | UrlObject` -* **options** [`PipelineOptions`](./docs/api/Dispatcher.md#parameter-pipelineoptions) +* **options** [`PipelineOptions`](./docs/docs/api/Dispatcher.md#parameter-pipelineoptions) * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher) * **method** `String` - Default: `PUT` if `options.body`, otherwise `GET` * **maxRedirections** `Integer` - Default: `0` @@ -166,7 +166,7 @@ Returns: `stream.Duplex` Calls `options.dispatch.pipeline(options, handler)`. -See [Dispatcher.pipeline](./docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details. +See [Dispatcher.pipeline](./docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler) for more details. ### `undici.connect([url, options]): Promise` @@ -175,7 +175,7 @@ Starts two-way communications with the requested resource using [HTTP CONNECT](h Arguments: * **url** `string | URL | UrlObject` -* **options** [`ConnectOptions`](./docs/api/Dispatcher.md#parameter-connectoptions) +* **options** [`ConnectOptions`](./docs/docs/api/Dispatcher.md#parameter-connectoptions) * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher) * **maxRedirections** `Integer` - Default: `0` * **callback** `(err: Error | null, data: ConnectData | null) => void` (optional) @@ -184,7 +184,7 @@ Returns a promise with the result of the `Dispatcher.connect` method. Calls `options.dispatch.connect(options)`. -See [Dispatcher.connect](./docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details. +See [Dispatcher.connect](./docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback) for more details. ### `undici.fetch(input[, init]): Promise` @@ -335,7 +335,7 @@ Upgrade to a different protocol. See [MDN - HTTP - Protocol upgrade mechanism](h Arguments: * **url** `string | URL | UrlObject` -* **options** [`UpgradeOptions`](./docs/api/Dispatcher.md#parameter-upgradeoptions) +* **options** [`UpgradeOptions`](./docs/docs/api/Dispatcher.md#parameter-upgradeoptions) * **dispatcher** `Dispatcher` - Default: [getGlobalDispatcher](#undicigetglobaldispatcher) * **maxRedirections** `Integer` - Default: `0` * **callback** `(error: Error | null, data: UpgradeData) => void` (optional) @@ -344,7 +344,7 @@ Returns a promise with the result of the `Dispatcher.upgrade` method. Calls `options.dispatcher.upgrade(options)`. -See [Dispatcher.upgrade](./docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details. +See [Dispatcher.upgrade](./docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback) for more details. ### `undici.setGlobalDispatcher(dispatcher)` diff --git a/deps/undici/src/lib/web/fetch/headers.js b/deps/undici/src/lib/web/fetch/headers.js index 5e2321b82927a5..679cbbf9ef0be6 100644 --- a/deps/undici/src/lib/web/fetch/headers.js +++ b/deps/undici/src/lib/web/fetch/headers.js @@ -626,10 +626,6 @@ Reflect.deleteProperty(Headers, 'setHeadersGuard') Reflect.deleteProperty(Headers, 'getHeadersList') Reflect.deleteProperty(Headers, 'setHeadersList') -Object.defineProperty(Headers.prototype, util.inspect.custom, { - enumerable: false -}) - iteratorMixin('Headers', Headers, kHeadersSortedMap, 0, 1) Object.defineProperties(Headers.prototype, { @@ -642,6 +638,17 @@ Object.defineProperties(Headers.prototype, { [Symbol.toStringTag]: { value: 'Headers', configurable: true + }, + [util.inspect.custom]: { + enumerable: false + }, + // Compatibility for global headers + [Symbol('headers list')]: { + configurable: false, + enumerable: false, + get: function () { + return getHeadersList(this) + } } }) diff --git a/deps/undici/src/lib/web/websocket/receiver.js b/deps/undici/src/lib/web/websocket/receiver.js index 3a8b2abb611157..581c251074c740 100644 --- a/deps/undici/src/lib/web/websocket/receiver.js +++ b/deps/undici/src/lib/web/websocket/receiver.js @@ -240,8 +240,8 @@ class ByteParser extends Writable { this.#loop = true this.#state = parserStates.INFO - this.run(callback) this.#fragments.length = 0 + this.run(callback) }) this.#loop = false diff --git a/deps/undici/src/lib/web/websocket/sender.js b/deps/undici/src/lib/web/websocket/sender.js index b9fc7a723649fe..1b1468d4ab900e 100644 --- a/deps/undici/src/lib/web/websocket/sender.js +++ b/deps/undici/src/lib/web/websocket/sender.js @@ -2,15 +2,30 @@ const { WebsocketFrameSend } = require('./frame') const { opcodes, sendHints } = require('./constants') +const FixedQueue = require('../../dispatcher/fixed-queue') -/** @type {Uint8Array} */ +/** @type {typeof Uint8Array} */ const FastBuffer = Buffer[Symbol.species] +/** + * @typedef {object} SendQueueNode + * @property {Promise | null} promise + * @property {((...args: any[]) => any)} callback + * @property {Buffer | null} frame + */ + class SendQueue { - #queued = new Set() - #size = 0 + /** + * @type {FixedQueue} + */ + #queue = new FixedQueue() + + /** + * @type {boolean} + */ + #running = false - /** @type {import('net').Socket} */ + /** @type {import('node:net').Socket} */ #socket constructor (socket) { @@ -19,58 +34,62 @@ class SendQueue { add (item, cb, hint) { if (hint !== sendHints.blob) { - const data = clone(item, hint) - - if (this.#size === 0) { - this.#dispatch(data, cb, hint) + const frame = createFrame(item, hint) + if (!this.#running) { + // fast-path + this.#socket.write(frame, cb) } else { - this.#queued.add([data, cb, true, hint]) - this.#size++ - - this.#run() + /** @type {SendQueueNode} */ + const node = { + promise: null, + callback: cb, + frame + } + this.#queue.push(node) } - return } - const promise = item.arrayBuffer() - const queue = [null, cb, false, hint] - promise.then((ab) => { - queue[0] = clone(ab, hint) - queue[2] = true + /** @type {SendQueueNode} */ + const node = { + promise: item.arrayBuffer().then((ab) => { + node.promise = null + node.frame = createFrame(ab, hint) + }), + callback: cb, + frame: null + } - this.#run() - }) + this.#queue.push(node) - this.#queued.add(queue) - this.#size++ + if (!this.#running) { + this.#run() + } } - #run () { - for (const queued of this.#queued) { - const [data, cb, done, hint] = queued - - if (!done) return - - this.#queued.delete(queued) - this.#size-- - - this.#dispatch(data, cb, hint) + async #run () { + this.#running = true + const queue = this.#queue + while (!queue.isEmpty()) { + const node = queue.shift() + // wait pending promise + if (node.promise !== null) { + await node.promise + } + // write + this.#socket.write(node.frame, node.callback) + // cleanup + node.callback = node.frame = null } + this.#running = false } +} - #dispatch (data, cb, hint) { - const frame = new WebsocketFrameSend() - const opcode = hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY - - frame.frameData = data - const buffer = frame.createFrame(opcode) - - this.#socket.write(buffer, cb) - } +function createFrame (data, hint) { + return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY) } -function clone (data, hint) { +function toBuffer (data, hint) { switch (hint) { case sendHints.string: return Buffer.from(data) @@ -78,7 +97,7 @@ function clone (data, hint) { case sendHints.blob: return new FastBuffer(data) case sendHints.typedArray: - return Buffer.copyBytesFrom(data) + return new FastBuffer(data.buffer, data.byteOffset, data.byteLength) } } diff --git a/deps/undici/src/package-lock.json b/deps/undici/src/package-lock.json index 21b597bd2d8e7a..749223975c1965 100644 --- a/deps/undici/src/package-lock.json +++ b/deps/undici/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "undici", - "version": "6.18.0", + "version": "6.18.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "undici", - "version": "6.18.0", + "version": "6.18.1", "license": "MIT", "devDependencies": { "@fastify/busboy": "2.1.1", @@ -1577,9 +1577,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "license": "MIT", "dependencies": { @@ -2334,13 +2334,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -2563,9 +2563,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001620", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", - "integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", + "version": "1.0.30001621", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz", + "integrity": "sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==", "dev": true, "funding": [ { @@ -3183,9 +3183,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.4.774", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.774.tgz", - "integrity": "sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==", + "version": "1.4.777", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.777.tgz", + "integrity": "sha512-n02NCwLJ3wexLfK/yQeqfywCblZqLcXphzmid5e8yVPdtEcida7li0A5WQKghHNG0FeOMCzeFOzEbtAh5riXFw==", "dev": true, "license": "ISC" }, @@ -4416,9 +4416,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { @@ -4676,14 +4676,14 @@ } }, "node_modules/glob": { - "version": "10.3.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", - "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", + "version": "10.3.16", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", + "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", + "jackspeak": "^3.1.2", "minimatch": "^9.0.1", "minipass": "^7.0.4", "path-scurry": "^1.11.0" @@ -5690,9 +5690,9 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -6944,13 +6944,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { diff --git a/deps/undici/src/package.json b/deps/undici/src/package.json index ee4d7e6308f30b..e15bc719e0f90e 100644 --- a/deps/undici/src/package.json +++ b/deps/undici/src/package.json @@ -1,6 +1,6 @@ { "name": "undici", - "version": "6.18.0", + "version": "6.18.1", "description": "An HTTP/1.1 client, written from scratch for Node.js", "homepage": "https://undici.nodejs.org", "bugs": { diff --git a/deps/undici/undici.js b/deps/undici/undici.js index d81806148a2b5a..b8627ad785ee4f 100644 --- a/deps/undici/undici.js +++ b/deps/undici/undici.js @@ -8631,9 +8631,6 @@ var require_headers = __commonJS({ Reflect.deleteProperty(Headers, "setHeadersGuard"); Reflect.deleteProperty(Headers, "getHeadersList"); Reflect.deleteProperty(Headers, "setHeadersList"); - Object.defineProperty(Headers.prototype, util.inspect.custom, { - enumerable: false - }); iteratorMixin("Headers", Headers, kHeadersSortedMap, 0, 1); Object.defineProperties(Headers.prototype, { append: kEnumerableProperty, @@ -8645,6 +8642,17 @@ var require_headers = __commonJS({ [Symbol.toStringTag]: { value: "Headers", configurable: true + }, + [util.inspect.custom]: { + enumerable: false + }, + // Compatibility for global headers + [Symbol("headers list")]: { + configurable: false, + enumerable: false, + get: function() { + return getHeadersList(this); + } } }); webidl.converters.HeadersInit = function(V, prefix, argument) { @@ -11962,8 +11970,8 @@ var require_receiver = __commonJS({ websocketMessageReceived(this.ws, this.#info.binaryType, Buffer.concat(this.#fragments)); this.#loop = true; this.#state = parserStates.INFO; - this.run(callback); this.#fragments.length = 0; + this.run(callback); }); this.#loop = false; break; @@ -12099,59 +12107,72 @@ var require_sender = __commonJS({ "use strict"; var { WebsocketFrameSend } = require_frame(); var { opcodes, sendHints } = require_constants4(); + var FixedQueue = require_fixed_queue(); var FastBuffer = Buffer[Symbol.species]; var SendQueue = class { static { __name(this, "SendQueue"); } - #queued = /* @__PURE__ */ new Set(); - #size = 0; - /** @type {import('net').Socket} */ + /** + * @type {FixedQueue} + */ + #queue = new FixedQueue(); + /** + * @type {boolean} + */ + #running = false; + /** @type {import('node:net').Socket} */ #socket; constructor(socket) { this.#socket = socket; } add(item, cb, hint) { if (hint !== sendHints.blob) { - const data = clone(item, hint); - if (this.#size === 0) { - this.#dispatch(data, cb, hint); + const frame = createFrame(item, hint); + if (!this.#running) { + this.#socket.write(frame, cb); } else { - this.#queued.add([data, cb, true, hint]); - this.#size++; - this.#run(); + const node2 = { + promise: null, + callback: cb, + frame + }; + this.#queue.push(node2); } return; } - const promise = item.arrayBuffer(); - const queue = [null, cb, false, hint]; - promise.then((ab) => { - queue[0] = clone(ab, hint); - queue[2] = true; + const node = { + promise: item.arrayBuffer().then((ab) => { + node.promise = null; + node.frame = createFrame(ab, hint); + }), + callback: cb, + frame: null + }; + this.#queue.push(node); + if (!this.#running) { this.#run(); - }); - this.#queued.add(queue); - this.#size++; - } - #run() { - for (const queued of this.#queued) { - const [data, cb, done, hint] = queued; - if (!done) - return; - this.#queued.delete(queued); - this.#size--; - this.#dispatch(data, cb, hint); } } - #dispatch(data, cb, hint) { - const frame = new WebsocketFrameSend(); - const opcode = hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY; - frame.frameData = data; - const buffer = frame.createFrame(opcode); - this.#socket.write(buffer, cb); + async #run() { + this.#running = true; + const queue = this.#queue; + while (!queue.isEmpty()) { + const node = queue.shift(); + if (node.promise !== null) { + await node.promise; + } + this.#socket.write(node.frame, node.callback); + node.callback = node.frame = null; + } + this.#running = false; } }; - function clone(data, hint) { + function createFrame(data, hint) { + return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY); + } + __name(createFrame, "createFrame"); + function toBuffer(data, hint) { switch (hint) { case sendHints.string: return Buffer.from(data); @@ -12159,10 +12180,10 @@ var require_sender = __commonJS({ case sendHints.blob: return new FastBuffer(data); case sendHints.typedArray: - return Buffer.copyBytesFrom(data); + return new FastBuffer(data.buffer, data.byteOffset, data.byteLength); } } - __name(clone, "clone"); + __name(toBuffer, "toBuffer"); module2.exports = { SendQueue }; } }); diff --git a/src/undici_version.h b/src/undici_version.h index a222f658fd466b..62d31ebe99d9fd 100644 --- a/src/undici_version.h +++ b/src/undici_version.h @@ -2,5 +2,5 @@ // Refer to tools/dep_updaters/update-undici.sh #ifndef SRC_UNDICI_VERSION_H_ #define SRC_UNDICI_VERSION_H_ -#define UNDICI_VERSION "6.18.0" +#define UNDICI_VERSION "6.18.1" #endif // SRC_UNDICI_VERSION_H_