Skip to content

Commit

Permalink
Merge branch 'main' into feat/retry-ifmatch
Browse files Browse the repository at this point in the history
  • Loading branch information
metcoder95 committed Apr 25, 2024
2 parents 917f5ae + f6f0787 commit a3eda82
Show file tree
Hide file tree
Showing 41 changed files with 450 additions and 492 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ jobs:
repo
})
if (versionTag !== releases[0]?.tag_name) {
const previousRelease = releases.find((r) => r.tag_name.startsWith('v6'))
if (versionTag !== previousRelease?.tag_name) {
return versionTag
}
Expand Down
13 changes: 10 additions & 3 deletions build/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
FROM node:21-alpine@sha256:6d0f18a1c67dc218c4af50c21256616286a53c09e500fadf025b6d342e1c90ae
FROM node:21-alpine3.19@sha256:db8772d9f5796ac4e8c47508038c413ea1478da010568a2e48672f19a8b80cd2

ARG UID=1000
ARG GID=1000
ARG BINARYEN_VERSION=116

RUN apk add -U clang lld wasi-sdk
RUN mkdir /home/node/undici

WORKDIR /home/node/undici

RUN wget https://github.com/WebAssembly/binaryen/releases/download/version_$BINARYEN_VERSION/binaryen-version_$BINARYEN_VERSION-x86_64-linux.tar.gz && \
tar -zxvf binaryen-version_$BINARYEN_VERSION-x86_64-linux.tar.gz binaryen-version_$BINARYEN_VERSION/bin/wasm-opt && \
mv binaryen-version_$BINARYEN_VERSION/bin/wasm-opt ./ && \
rm binaryen-version_$BINARYEN_VERSION-x86_64-linux.tar.gz && \
rm -rf binaryen-version_$BINARYEN_VERSION && \
chmod +x ./wasm-opt

COPY package.json .

COPY build build
COPY deps deps
COPY lib lib

RUN npm i

USER node
94 changes: 45 additions & 49 deletions build/wasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const { execSync } = require('node:child_process')
const { writeFileSync, readFileSync } = require('node:fs')
const { join, resolve, basename } = require('node:path')
const { join, resolve } = require('node:path')

const ROOT = resolve(__dirname, '../')
const WASM_SRC = resolve(__dirname, '../deps/llhttp')
Expand All @@ -15,19 +15,40 @@ let WASM_CFLAGS = process.env.WASM_CFLAGS || '--sysroot=/usr/share/wasi-sysroot
let WASM_LDFLAGS = process.env.WASM_LDFLAGS || ''
const WASM_LDLIBS = process.env.WASM_LDLIBS || ''

const EXTERNAL_PATH = process.env.EXTERNAL_PATH

// These are relevant for undici and should not be overridden
WASM_CFLAGS += ' -Ofast -fno-exceptions -fvisibility=hidden -mexec-model=reactor'
WASM_LDFLAGS += ' -Wl,-error-limit=0 -Wl,-O3 -Wl,--lto-O3 -Wl,--strip-all'
WASM_LDFLAGS += ' -Wl,--allow-undefined -Wl,--export-dynamic -Wl,--export-table'
WASM_LDFLAGS += ' -Wl,--export=malloc -Wl,--export=free -Wl,--no-entry'

const WASM_OPT_FLAGS = '-O4 --converge --strip-debug --strip-dwarf --strip-producers'

const writeWasmChunk = (path, dest) => {
const base64 = readFileSync(join(WASM_OUT, path)).toString('base64')
writeFileSync(join(WASM_OUT, dest), `'use strict'
const { Buffer } = require('node:buffer')
module.exports = Buffer.from('${base64}', 'base64')
`)
}

let platform = process.env.WASM_PLATFORM
if (!platform && process.argv[2]) {
platform = execSync('docker info -f "{{.OSType}}/{{.Architecture}}"').toString().trim()
}

if (process.argv[2] === '--rm') {
const cmd = 'docker image rm llhttp_wasm_builder'

console.log(`> ${cmd}\n\n`)
try {
execSync(cmd, { stdio: 'inherit' })
} catch (e) {}

process.exit(0)
}

if (process.argv[2] === '--prebuild') {
const cmd = `docker build --platform=${platform.toString().trim()} -t llhttp_wasm_builder -f ${DOCKERFILE} ${ROOT}`

Expand Down Expand Up @@ -59,50 +80,25 @@ if (hasApk) {
console.log('Failed to generate build environment information')
process.exit(-1)
}
writeFileSync(join(WASM_OUT, 'wasm_build_env.txt'), buildInfo)
}

const writeWasmChunk = EXTERNAL_PATH
? (path, dest) => {
const base64 = readFileSync(join(WASM_OUT, path)).toString('base64')
writeFileSync(join(WASM_OUT, dest), `
const { Buffer } = require('node:buffer')
module.exports = Buffer.from('${base64}', 'base64')
`)
}
: (path, dest) => {
writeFileSync(join(WASM_OUT, dest), `
const { fs } = require('node:fs')
module.exports = fs.readFileSync(require.resolve('./${basename(path)}'))
`)
}

// Build wasm binary
execSync(`${WASM_CC} ${WASM_CFLAGS} ${WASM_LDFLAGS} \
${join(WASM_SRC, 'src')}/*.c \
-I${join(WASM_SRC, 'include')} \
-o ${join(WASM_OUT, 'llhttp.wasm')} \
${WASM_LDLIBS}`, { stdio: 'inherit' })

writeWasmChunk('llhttp.wasm', 'llhttp-wasm.js')

// Build wasm simd binary
execSync(`${WASM_CC} ${WASM_CFLAGS} -msimd128 ${WASM_LDFLAGS} \
${join(WASM_SRC, 'src')}/*.c \
-I${join(WASM_SRC, 'include')} \
-o ${join(WASM_OUT, 'llhttp_simd.wasm')} \
${WASM_LDLIBS}`, { stdio: 'inherit' })

writeWasmChunk('llhttp_simd.wasm', 'llhttp_simd-wasm.js')

if (EXTERNAL_PATH) {
writeFileSync(join(ROOT, 'loader.js'), `
'use strict'
globalThis.__UNDICI_IS_NODE__ = true
module.exports = require('node:module').createRequire('${EXTERNAL_PATH}/loader.js')('./index-fetch.js')
delete globalThis.__UNDICI_IS_NODE__
`)
console.log(buildInfo)

// Build wasm binary
execSync(`${WASM_CC} ${WASM_CFLAGS} ${WASM_LDFLAGS} \
${join(WASM_SRC, 'src')}/*.c \
-I${join(WASM_SRC, 'include')} \
-o ${join(WASM_OUT, 'llhttp.wasm')} \
${WASM_LDLIBS}`, { stdio: 'inherit' })

execSync(`./wasm-opt ${WASM_OPT_FLAGS} -o ${join(WASM_OUT, 'llhttp.wasm')} ${join(WASM_OUT, 'llhttp.wasm')}`, { stdio: 'inherit' })
writeWasmChunk('llhttp.wasm', 'llhttp-wasm.js')

// Build wasm simd binary
execSync(`${WASM_CC} ${WASM_CFLAGS} -msimd128 ${WASM_LDFLAGS} \
${join(WASM_SRC, 'src')}/*.c \
-I${join(WASM_SRC, 'include')} \
-o ${join(WASM_OUT, 'llhttp_simd.wasm')} \
${WASM_LDLIBS}`, { stdio: 'inherit' })

execSync(`./wasm-opt ${WASM_OPT_FLAGS} --enable-simd -o ${join(WASM_OUT, 'llhttp_simd.wasm')} ${join(WASM_OUT, 'llhttp_simd.wasm')}`, { stdio: 'inherit' })
writeWasmChunk('llhttp_simd.wasm', 'llhttp_simd-wasm.js')
}
2 changes: 1 addition & 1 deletion docs/docs/api/Client.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Returns: `Client`
* **headersTimeout** `number | null` (optional) - Default: `300e3` - The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 300 seconds.
* **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout`, in milliseconds, when overridden by *keep-alive* hints from the server. Defaults to 10 minutes.
* **keepAliveTimeout** `number | null` (optional) - Default: `4e3` - The timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server. See [MDN: HTTP - Headers - Keep-Alive directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive#directives) for more details. Defaults to 4 seconds.
* **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `1e3` - A number of milliseconds subtracted from server *keep-alive* hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 1 second.
* **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `2e3` - A number of milliseconds subtracted from server *keep-alive* hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 2 seconds.
* **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB.
* **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable.
* **pipelining** `number | null` (optional) - Default: `1` - The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections.
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/api/EnvHttpProxyAgent.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ Stability: Experimental.

Extends: `undici.Dispatcher`

EnvHttpProxyAgent automatically reads the proxy configuration from the environment variables `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` and sets up the proxy agents accordingly. When `HTTP_PROXY` and `HTTPS_PROXY` are set, `HTTP_PROXY` is used for HTTP requests and `HTTPS_PROXY` is used for HTTPS requests. If only `HTTP_PROXY` is set, `HTTP_PROXY` is used for both HTTP and HTTPS requests. If only `HTTPS_PROXY` is set, it is only used for HTTPS requests.
EnvHttpProxyAgent automatically reads the proxy configuration from the environment variables `http_proxy`, `https_proxy`, and `no_proxy` and sets up the proxy agents accordingly. When `http_proxy` and `https_proxy` are set, `http_proxy` is used for HTTP requests and `https_proxy` is used for HTTPS requests. If only `http_proxy` is set, `http_proxy` is used for both HTTP and HTTPS requests. If only `https_proxy` is set, it is only used for HTTPS requests.

`NO_PROXY` is a comma or space-separated list of hostnames that should not be proxied. The list may contain leading wildcard characters (`*`). If `NO_PROXY` is set, the EnvHttpProxyAgent will bypass the proxy for requests to hosts that match the list. If `NO_PROXY` is set to `"*"`, the EnvHttpProxyAgent will bypass the proxy for all requests.
`no_proxy` is a comma or space-separated list of hostnames that should not be proxied. The list may contain leading wildcard characters (`*`). If `no_proxy` is set, the EnvHttpProxyAgent will bypass the proxy for requests to hosts that match the list. If `no_proxy` is set to `"*"`, the EnvHttpProxyAgent will bypass the proxy for all requests.

Lower case environment variables are also supported: `http_proxy`, `https_proxy`, and `no_proxy`. However, if both the lower case and upper case environment variables are set, the lower case environment variables will be ignored.
Uppercase environment variables are also supported: `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`. However, if both the lowercase and uppercase environment variables are set, the uppercase environment variables will be ignored.

## `new EnvHttpProxyAgent([options])`

Expand Down
7 changes: 7 additions & 0 deletions index-fetch.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict'

const { getGlobalDispatcher, setGlobalDispatcher } = require('./lib/global')
const EnvHttpProxyAgent = require('./lib/dispatcher/env-http-proxy-agent')
const fetchImpl = require('./lib/web/fetch').fetch

module.exports.fetch = function fetch (resource, init = undefined) {
Expand All @@ -19,3 +21,8 @@ module.exports.WebSocket = require('./lib/web/websocket/websocket').WebSocket
module.exports.MessageEvent = require('./lib/web/websocket/events').MessageEvent

module.exports.EventSource = require('./lib/web/eventsource/eventsource').EventSource

// Expose the fetch implementation to be enabled in Node.js core via a flag
module.exports.EnvHttpProxyAgent = EnvHttpProxyAgent
module.exports.getGlobalDispatcher = getGlobalDispatcher
module.exports.setGlobalDispatcher = setGlobalDispatcher
6 changes: 2 additions & 4 deletions lib/dispatcher/client-h2.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,9 @@ function onHttp2SessionEnd () {
* This is the root cause of #3011
* We need to handle GOAWAY frames properly, and trigger the session close
* along with the socket right away
* Find a way to trigger the close cycle from here on.
*/
function onHTTP2GoAway (code) {
const err = new InformationalError(`HTTP/2: "GOAWAY" frame received with code ${code}`)
const err = new RequestAbortedError(`HTTP/2: "GOAWAY" frame received with code ${code}`)

// We need to trigger the close cycle right away
// We need to destroy the session and the socket
Expand All @@ -220,8 +219,7 @@ function onHTTP2GoAway (code) {
this[kClient][kOnError](err)

this.unref()
// We send the GOAWAY frame response as no error
this.destroy()

util.destroy(this[kSocket], err)
}

Expand Down
3 changes: 2 additions & 1 deletion lib/dispatcher/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ class Client extends DispatcherBase {
this[kMaxHeadersSize] = maxHeaderSize || http.maxHeaderSize
this[kKeepAliveDefaultTimeout] = keepAliveTimeout == null ? 4e3 : keepAliveTimeout
this[kKeepAliveMaxTimeout] = keepAliveMaxTimeout == null ? 600e3 : keepAliveMaxTimeout
this[kKeepAliveTimeoutThreshold] = keepAliveTimeoutThreshold == null ? 1e3 : keepAliveTimeoutThreshold
this[kKeepAliveTimeoutThreshold] = keepAliveTimeoutThreshold == null ? 2e3 : keepAliveTimeoutThreshold
this[kKeepAliveTimeoutValue] = this[kKeepAliveDefaultTimeout]
this[kServerName] = null
this[kLocalAddress] = localAddress != null ? localAddress : null
Expand Down Expand Up @@ -376,6 +376,7 @@ function onError (client, err) {
assert(client[kPendingIdx] === client[kRunningIdx])

const requests = client[kQueue].splice(client[kRunningIdx])

for (let i = 0; i < requests.length; i++) {
const request = requests[i]
util.errorRequest(client, request, err)
Expand Down
6 changes: 3 additions & 3 deletions lib/dispatcher/env-http-proxy-agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ class EnvHttpProxyAgent extends DispatcherBase {

this[kNoProxyAgent] = new Agent(agentOpts)

const HTTP_PROXY = httpProxy ?? process.env.HTTP_PROXY ?? process.env.http_proxy
const HTTP_PROXY = httpProxy ?? process.env.http_proxy ?? process.env.HTTP_PROXY
if (HTTP_PROXY) {
this[kHttpProxyAgent] = new ProxyAgent({ ...agentOpts, uri: HTTP_PROXY })
} else {
this[kHttpProxyAgent] = this[kNoProxyAgent]
}

const HTTPS_PROXY = httpsProxy ?? process.env.HTTPS_PROXY ?? process.env.https_proxy
const HTTPS_PROXY = httpsProxy ?? process.env.https_proxy ?? process.env.HTTPS_PROXY
if (HTTPS_PROXY) {
this[kHttpsProxyAgent] = new ProxyAgent({ ...agentOpts, uri: HTTPS_PROXY })
} else {
Expand Down Expand Up @@ -153,7 +153,7 @@ class EnvHttpProxyAgent extends DispatcherBase {
}

get #noProxyEnv () {
return process.env.NO_PROXY ?? process.env.no_proxy ?? ''
return process.env.no_proxy ?? process.env.NO_PROXY ?? ''
}
}

Expand Down
Empty file added lib/llhttp/.gitkeep
Empty file.
Loading

0 comments on commit a3eda82

Please sign in to comment.