Compare commits

...
8 Commits
Author SHA1 Message Date
Raj Chaudhuri 525d517fe1 Add release v2.8.7 2024-05-05 11:28:31 +05:30
Raj Chaudhuri a3d5e0710e Removed ppc64le and s390x arch support
A crucial dependency does not support them any more
2024-05-05 10:52:45 +05:30
Raj Chaudhuri 3fd1ae5bb3 Upgraded a dependency
Details in reweave/CHANGELOG.md
2024-05-04 16:10:28 +05:30
Raj Chaudhuri f8d0d8e82b Scan results on 2024-05-04 2024-05-04 15:18:21 +05:30
Raj Chaudhuri 46cf7c460e Documentation corrections 2024-04-10 16:06:14 +05:30
Raj Chaudhuri afd62f23e5 Corrected plugin publish tool 2024-04-10 15:10:45 +05:30
Raj Chaudhuri e42bc298a4 Add release v2.8.6 2024-04-10 14:23:57 +05:30
Raj Chaudhuri 78f596fbda Provisioned for Docker V2 plugin on multiple architectures, blocked CHECKPOINT, updated docs 2024-04-09 19:01:34 +05:30
201 changed files with 1513 additions and 696 deletions

No files matched your search

+15
View File
@@ -1,3 +1,18 @@
## Release 2.8.7
This release has upgraded dependencies based on CVE recommendations.
Also, this release does not provides images for the `ppc64le` and `s390x` architectures, because a crucial upstream dependency (libpcap-0.8, from the debian buster respository) does not provide binaries for those architectures any more.
## Release 2.8.6
From this release onwards, the Docker V2 Plugin will be published for all supported architectures. It will be named using the pattern `rajchaudhuri/net-plugin:<version>-<arch>`. The latest version will also be published as `rajchaudhuri/net-plugin:latest_release-<arch>`. Supported architectures are `amd64`,`arm`,`arm64`,`ppc64le` and `s390x`. It can be installed on a Swarm-mode-enabled Docker host as follows:
```bash
ARCH=$(docker version -f '{{.Server.Arch}}')
docker plugin install rajchaudhuri/net-plugin:latest_release-$ARCH
```
## Release 2.8.5 ## Release 2.8.5
This release has upgraded dependencies based on CVE recommendations. This release has upgraded dependencies based on CVE recommendations.
+2 -2
View File
@@ -14,10 +14,10 @@ The history of the ReWeave effort can be found in [HISTORY.md](HISTORY.md).
On a newly created Kubernetes cluster, the Weave Net CNI pluging can be installed by running the following command: On a newly created Kubernetes cluster, the Weave Net CNI pluging can be installed by running the following command:
``` ```
kubectl apply -f https://reweave.azurewebsites.net/k8s/v1.28/net.yaml kubectl apply -f https://reweave.azurewebsites.net/k8s/v1.29/net.yaml
``` ```
Replace `v1.28` with the version on Kubernetes on your cluster. Replace `v1.29` with the version on Kubernetes on your cluster.
That endpoint is provided by the companion project [weave-endpoint](https://github.com/rajch/weave-endpoint). That endpoint is provided by the companion project [weave-endpoint](https://github.com/rajch/weave-endpoint).
+4 -4
View File
@@ -30,8 +30,8 @@ require (
github.com/weaveworks/go-checkpoint v0.0.0-20170503165305-ebbb8b0518ab github.com/weaveworks/go-checkpoint v0.0.0-20170503165305-ebbb8b0518ab
github.com/weaveworks/go-odp v0.0.0-20181017121109-6b0aa22550d9 github.com/weaveworks/go-odp v0.0.0-20181017121109-6b0aa22550d9
github.com/weaveworks/mesh v0.0.0-20191105120815-58dbcc3e8e63 github.com/weaveworks/mesh v0.0.0-20191105120815-58dbcc3e8e63
golang.org/x/crypto v0.17.0 golang.org/x/crypto v0.21.0
golang.org/x/sys v0.15.0 golang.org/x/sys v0.18.0
golang.org/x/tools v0.10.0 golang.org/x/tools v0.10.0
k8s.io/api v0.26.2 k8s.io/api v0.26.2
k8s.io/apimachinery v0.26.2 k8s.io/apimachinery v0.26.2
@@ -92,9 +92,9 @@ require (
github.com/safchain/ethtool v0.2.0 // indirect github.com/safchain/ethtool v0.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/mod v0.11.0 // indirect golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.17.0 // indirect golang.org/x/net v0.23.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/term v0.15.0 // indirect golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
+8 -8
View File
@@ -391,8 +391,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -460,8 +460,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -533,12 +533,12 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+8
View File
@@ -97,6 +97,14 @@
"value" "value"
], ],
"value": "" "value": ""
},
{
"description": "Whether to use the version check feature of Weave Net",
"name": "CHECKPOINT_DISABLE",
"settable": [
"value"
],
"value": "1"
} }
], ],
"network": { "network": {
+39
View File
@@ -2,8 +2,46 @@
All changes made to the weave net codebase during the reweave effort will be documented in this file. All changes made to the weave net codebase during the reweave effort will be documented in this file.
## 2.8.7
### Changed
* Changed version in `reweave/Makefile` to 2.8.6
* Modified reweave and main CHANGELOG.md
## 2.8.7-beta1
### Changed
* Upgraded golang.org/x/net to v0.23.0, which:
* Upgraded golang.org/x/term to v0.18.0
* Upgraded golang.org/x/sys to v0.18.0
* Upgraded golang.org/x/crypto to v0.21.0
* Ran `go mod tidy` and `go mod vendor`
* Removed support for ppc64le and s90x architectures, because libpcap-0.8 in the debian buster repository does not provide binaries for those architectures any more
## 2.8.6
### Changed
* Changed version in `reweave/Makefile` to 2.8.6
* Modified reweave and main CHANGELOG.md
* Modified `reweave/Makefile` to ensure that `make build-plugin` builds the plugin only for the host architecture
* Made some documentation changes
## 2.8.6-beta1
### Changed
* Changed the V2 plugin build and clean tools to build (and clean) multiple architecture-specific plugins per version, since Docker Managed Plugins are not multi-architecture capable
* Changed the V2 plugin configuration `prog/net-plugin/config.json` to include `CHECKPOINT_DISABLE` as an option, set to `1` by default to disable latest version checking.
* Changed the `weave` script to include `export CHECKPOINT_DISABLE=`. This will disable latest version checking for the legacy plugin as well.
* Changed the documentation in the `website` directory as appropriate.
## 2.8.5 ## 2.8.5
### Changed
* Changed version in `reweave/Makefile` to 2.8.5 * Changed version in `reweave/Makefile` to 2.8.5
* Modified reweave and main CHANGELOG.md * Modified reweave and main CHANGELOG.md
@@ -237,6 +275,7 @@ A new build process was created here.
* Multi-stage Dockerfile, and a Makefile for the second stage, in `reweave/build` * Multi-stage Dockerfile, and a Makefile for the second stage, in `reweave/build`
### Changed ### Changed
* Included image building targets in Makefile directly under `reweave` * Included image building targets in Makefile directly under `reweave`
* Changed `IMAGE_VERSION` to `2.8.2-beta0` in Makefile directly under `reweave` * Changed `IMAGE_VERSION` to `2.8.2-beta0` in Makefile directly under `reweave`
* The Alpine base image for all weave net images was set to `alpine:3.17.2` via the multi-stage Dockerfile * The Alpine base image for all weave net images was set to `alpine:3.17.2` via the multi-stage Dockerfile
+4 -1
View File
@@ -1,4 +1,4 @@
IMAGE_VERSION ?= 2.8.5 IMAGE_VERSION ?= 2.8.7
REGISTRY_USER ?= rajchaudhuri REGISTRY_USER ?= rajchaudhuri
ALPINE_BASEIMAGE ?= alpine:3.19.1 ALPINE_BASEIMAGE ?= alpine:3.19.1
@@ -15,6 +15,7 @@ build-images: submodule
.PHONY: build-plugin .PHONY: build-plugin
build-plugin: ../prog/net-plugin/launch.sh ../prog/net-plugin/config.json build-plugin: ../prog/net-plugin/launch.sh ../prog/net-plugin/config.json
REGISTRY_USER=${REGISTRY_USER} IMAGE_VERSION=${IMAGE_VERSION} \ REGISTRY_USER=${REGISTRY_USER} IMAGE_VERSION=${IMAGE_VERSION} \
PLATFORMS="$$(docker version -f '{{ .Server.Arch }}')" \
tools/build-plugin.sh tools/build-plugin.sh
.PHONY: build .PHONY: build
@@ -23,6 +24,7 @@ build: build-images build-plugin
.PHONY: publish-images .PHONY: publish-images
publish-images: submodule publish-images: submodule
REGISTRY_USER=${REGISTRY_USER} IMAGE_VERSION=${IMAGE_VERSION} \ REGISTRY_USER=${REGISTRY_USER} IMAGE_VERSION=${IMAGE_VERSION} \
PLATFORMS="linux/amd64,linux/arm,linux/arm64" \
ALPINE_BASEIMAGE=$(ALPINE_BASEIMAGE) \ ALPINE_BASEIMAGE=$(ALPINE_BASEIMAGE) \
PUBLISH=true \ PUBLISH=true \
tools/build-images.sh tools/build-images.sh
@@ -30,6 +32,7 @@ publish-images: submodule
.PHONY: publish-plugin .PHONY: publish-plugin
publish-plugin: publish-plugin:
REGISTRY_USER=${REGISTRY_USER} IMAGE_VERSION=${IMAGE_VERSION} \ REGISTRY_USER=${REGISTRY_USER} IMAGE_VERSION=${IMAGE_VERSION} \
PLATFORMS="amd64,arm,arm64" \
PUBLISH=true \ PUBLISH=true \
tools/build-plugin.sh tools/build-plugin.sh
+10 -3
View File
@@ -14,12 +14,19 @@ ENV GOARM 7
# The names of the architectures that we fetch cross-compilers for # The names of the architectures that we fetch cross-compilers for
# from the Debian packages. # from the Debian packages.
# Disabling support for ppc64el and s390x architectures, as the
# debian buster repository does not host binaries for libpcap-0.8
# on those architectures any more.
# ENV DEB_CROSSPLATFORMS \
# amd64 \
# armhf \
# arm64 \
# ppc64el \
# s390x
ENV DEB_CROSSPLATFORMS \ ENV DEB_CROSSPLATFORMS \
amd64 \ amd64 \
armhf \ armhf \
arm64 \ arm64
ppc64el \
s390x
# Install the build-essential and crossbuild-essential-ARCH packages # Install the build-essential and crossbuild-essential-ARCH packages
RUN <<EOCROSSCOMPILERS RUN <<EOCROSSCOMPILERS
+11 -7
View File
@@ -11,7 +11,11 @@ BUILDARCH?=amd64
# naming platforms. # naming platforms.
# All supported architectures must have an "ifeq" block below that # All supported architectures must have an "ifeq" block below that
# selects the corrsponding Debian platform architecture in CCARCH # selects the corrsponding Debian platform architecture in CCARCH
ALL_ARCHITECTURES=amd64 arm arm64 ppc64le s390x # Disabling support for ppc64le and s390x architectures, as the
# debian buster repository does not host binaries for libpcap-0.8
# on those architectures any more.
# ALL_ARCHITECTURES=amd64 arm arm64 ppc64le s390x
ALL_ARCHITECTURES=amd64 arm arm64
# We need the Debian platform architecture format, for # We need the Debian platform architecture format, for
# crosscompilation # crosscompilation
@@ -24,12 +28,12 @@ endif
ifeq ($(ARCH),arm64) ifeq ($(ARCH),arm64)
CCARCH=aarch64-linux-gnu CCARCH=aarch64-linux-gnu
endif endif
ifeq ($(ARCH),ppc64le) # ifeq ($(ARCH),ppc64le)
CCARCH=powerpc64le-linux-gnu # CCARCH=powerpc64le-linux-gnu
endif # endif
ifeq ($(ARCH),s390x) # ifeq ($(ARCH),s390x)
CCARCH=s390x-linux-gnu # CCARCH=s390x-linux-gnu
endif # endif
# Set the architecture-specific C compiler and library # Set the architecture-specific C compiler and library
# load path. # load path.
+1 -1
View File
@@ -1 +1 @@
{"schemaVersion": 1, "label": "Vulnerabilty count", "message": "17", "color": "orange"} {"schemaVersion": 1, "label": "Vulnerabilty count", "message": "18", "color": "orange"}
+15 -12
View File
@@ -1,29 +1,29 @@
# Vulnerability Report # Vulnerability Report
``` ```
Report date: 2024-04-06 Report date: 2024-05-05
Unique vulnerability count: 17 Unique vulnerability count: 18
Images version: 2.8.5 Images version: 2.8.7
``` ```
## Scanner Details ## Scanner Details
``` ```
Application: grype Application: grype
Version: 0.74.7 Version: 0.77.2
BuildDate: 2024-02-26T18:24:14Z BuildDate: 2024-05-01T16:20:45Z
GitCommit: 987238519b8d6e302130ab715f20daed6634da68 GitCommit: bd16101ad0ed30c38e95d0992d0ad53f709dc5df
GitDescription: v0.74.7 GitDescription: v0.77.2
Platform: linux/amd64 Platform: linux/amd64
GoVersion: go1.21.7 GoVersion: go1.21.9
Compiler: gc Compiler: gc
Syft Version: v0.105.1 Syft Version: v1.3.0
Supported DB Schema: 5 Supported DB Schema: 5
``` ```
## Vulnerabilities ## Vulnerabilities
### weave-kube: (17) ### weave-kube: (18)
``` ```
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
@@ -40,6 +40,7 @@ curl 8.5.0-r0 apk CVE-2024-2466 Unknown
curl 8.5.0-r0 apk CVE-2024-2398 Unknown curl 8.5.0-r0 apk CVE-2024-2398 Unknown
curl 8.5.0-r0 apk CVE-2024-2004 Unknown curl 8.5.0-r0 apk CVE-2024-2004 Unknown
libuv 1.47.0-r0 apk CVE-2024-24806 High libuv 1.47.0-r0 apk CVE-2024-24806 High
nghttp2-libs 1.58.0-r0 apk CVE-2024-28182 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42366 Medium ssl_client 1.36.1-r15 apk CVE-2023-42366 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42365 Medium ssl_client 1.36.1-r15 apk CVE-2023-42365 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42364 Medium ssl_client 1.36.1-r15 apk CVE-2023-42364 Medium
@@ -64,7 +65,7 @@ ssl_client 1.36.1-r15 apk CVE-2023-42364 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42363 Medium ssl_client 1.36.1-r15 apk CVE-2023-42363 Medium
``` ```
### weave: (17) ### weave: (18)
``` ```
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
@@ -81,13 +82,14 @@ curl 8.5.0-r0 apk CVE-2024-2466 Unknown
curl 8.5.0-r0 apk CVE-2024-2398 Unknown curl 8.5.0-r0 apk CVE-2024-2398 Unknown
curl 8.5.0-r0 apk CVE-2024-2004 Unknown curl 8.5.0-r0 apk CVE-2024-2004 Unknown
libuv 1.47.0-r0 apk CVE-2024-24806 High libuv 1.47.0-r0 apk CVE-2024-24806 High
nghttp2-libs 1.58.0-r0 apk CVE-2024-28182 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42366 Medium ssl_client 1.36.1-r15 apk CVE-2023-42366 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42365 Medium ssl_client 1.36.1-r15 apk CVE-2023-42365 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42364 Medium ssl_client 1.36.1-r15 apk CVE-2023-42364 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42363 Medium ssl_client 1.36.1-r15 apk CVE-2023-42363 Medium
``` ```
### weaveexec: (17) ### weaveexec: (18)
``` ```
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
@@ -104,6 +106,7 @@ curl 8.5.0-r0 apk CVE-2024-2466 Unknown
curl 8.5.0-r0 apk CVE-2024-2398 Unknown curl 8.5.0-r0 apk CVE-2024-2398 Unknown
curl 8.5.0-r0 apk CVE-2024-2004 Unknown curl 8.5.0-r0 apk CVE-2024-2004 Unknown
libuv 1.47.0-r0 apk CVE-2024-24806 High libuv 1.47.0-r0 apk CVE-2024-24806 High
nghttp2-libs 1.58.0-r0 apk CVE-2024-28182 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42366 Medium ssl_client 1.36.1-r15 apk CVE-2023-42366 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42365 Medium ssl_client 1.36.1-r15 apk CVE-2023-42365 Medium
ssl_client 1.36.1-r15 apk CVE-2023-42364 Medium ssl_client 1.36.1-r15 apk CVE-2023-42364 Medium
+69 -22
View File
@@ -6,33 +6,80 @@ set -e
: "${IMAGE_VERSION:=}" : "${IMAGE_VERSION:=}"
: "${REGISTRY_USER:=}" : "${REGISTRY_USER:=}"
# This variable is used to flag plugin publishing
: "${PUBLISH:=}"
if [ -z "${IMAGE_VERSION}" ] || [ -z "${REGISTRY_USER}" ] ; then if [ -z "${IMAGE_VERSION}" ] || [ -z "${REGISTRY_USER}" ] ; then
>&2 echo "Please provide valid values for IMAGE_VERSION and REGISTRY_USER." >&2 echo "Please provide valid values for IMAGE_VERSION and REGISTRY_USER."
exit 1 exit 1
fi fi
# To build the plugin for multiple architecture, the
# local Docker engine needs to have the containerd
# snapshotter enabled.
if [ "overlayfs" != "$(docker info -f '{{.Driver}}')" ]; then
echo "To ensure that the plugin can be built for multiple architectures, the"
echo "Docker Engine needs to have the containerd image store feature enabled."
echo "Please see https://docs.docker.com/storage/containerd/ to find out how."
echo "Run this build again after enabling it."
exit 1
fi
# This variable is used to control the architectures for
# which the plugin will be built.
: "${PLATFORMS:=amd64,arm,arm64,ppc64le,s390x}"
# This variable is used to flag plugin publishing
: "${PUBLISH:=}"
WEAVER_IMAGE=${REGISTRY_USER}/weave:${IMAGE_VERSION} WEAVER_IMAGE=${REGISTRY_USER}/weave:${IMAGE_VERSION}
PLUGIN_IMAGE=${REGISTRY_USER}/net-plugin:${IMAGE_VERSION}
PLUGIN_BUILD_IMG="plugin-builder"
PLUGIN_PARENT_DIR="../prog/net-plugin"
PLUGIN_WORK_DIR="${PLUGIN_PARENT_DIR}/rootfs"
docker container rm -f ${PLUGIN_BUILD_IMG} 2>/dev/null # $1 - Architecture to build for
docker container create --name=${PLUGIN_BUILD_IMG} "${WEAVER_IMAGE}" true build_plugin() {
rm -rf ${PLUGIN_WORK_DIR} PLUGIN_ARCH=${1:-amd64}
mkdir ${PLUGIN_WORK_DIR} PLUGIN_IMAGE=${REGISTRY_USER}/net-plugin:${IMAGE_VERSION}-${PLUGIN_ARCH}
docker export ${PLUGIN_BUILD_IMG} | tar -x -C ${PLUGIN_WORK_DIR} PLUGIN_BUILD_IMG="plugin-builder-${PLUGIN_ARCH}"
docker container rm -f ${PLUGIN_BUILD_IMG} PLUGIN_PARENT_DIR="../prog/net-plugin"
cp "${PLUGIN_PARENT_DIR}/launch.sh" "${PLUGIN_WORK_DIR}/home/weave/launch.sh" PLUGIN_WORK_DIR="${PLUGIN_PARENT_DIR}/rootfs"
set +e
docker plugin disable "${PLUGIN_IMAGE}" 2>/dev/null
docker plugin rm "${PLUGIN_IMAGE}" 2>/dev/null
set -e
docker plugin create "${PLUGIN_IMAGE}" "${PLUGIN_PARENT_DIR}"
if [ "${PUBLISH}" = "true" ]; then echo "Building ${PLUGIN_IMAGE}..."
docker plugin push "${PLUGIN_IMAGE}"
fi docker container rm -f "${PLUGIN_BUILD_IMG}" 2>/dev/null
docker container create --platform="${PLUGIN_ARCH}" --name="${PLUGIN_BUILD_IMG}" "${WEAVER_IMAGE}" true
rm -rf ${PLUGIN_WORK_DIR}
mkdir ${PLUGIN_WORK_DIR}
docker export "${PLUGIN_BUILD_IMG}" | tar -x -C ${PLUGIN_WORK_DIR}
docker container rm -f "${PLUGIN_BUILD_IMG}"
cp "${PLUGIN_PARENT_DIR}/launch.sh" "${PLUGIN_WORK_DIR}/home/weave/launch.sh"
set +e
docker plugin disable "${PLUGIN_IMAGE}" 2>/dev/null
docker plugin rm "${PLUGIN_IMAGE}" 2>/dev/null
set -e
docker plugin create "${PLUGIN_IMAGE}" "${PLUGIN_PARENT_DIR}"
if [ "${PUBLISH}" = "true" ]; then
echo "Pushing ${PLUGIN_IMAGE}..."
docker plugin push "${PLUGIN_IMAGE}"
# If this is not a beta release, also create a "latest-release-$ARCH"
# plugin.
case "$IMAGE_VERSION" in
*-beta*) IS_BETA=1 ;;
*) IS_BETA= ;;
esac
if [ -z ${IS_BETA} ]; then
docker plugin rm "${PLUGIN_IMAGE}" 2>/dev/null
echo "Tagging ${PLUGIN_IMAGE} as latest_release..."
LATEST_IMAGE=${REGISTRY_USER}/net-plugin:latest_release-${PLUGIN_ARCH}
docker plugin create "${LATEST_IMAGE}" "${PLUGIN_PARENT_DIR}"
echo "Pushing ${LATEST_IMAGE}..."
docker plugin push "${LATEST_IMAGE}"
fi
fi
echo "Done."
}
for i in $( echo "$PLATFORMS" | tr ',' ' '); do
build_plugin "$i"
done
+18 -2
View File
@@ -11,6 +11,22 @@ if [ -z "${IMAGE_VERSION}" ] || [ -z "${REGISTRY_USER}" ] ; then
exit 1 exit 1
fi fi
PLUGIN_IMAGE=${REGISTRY_USER}/net-plugin:${IMAGE_VERSION} # This variable is used to control the architectures for
# which the plugin will be built.
: "${PLATFORMS:=amd64,arm,arm64,ppc64le,s390x}"
docker plugin rm "${PLUGIN_IMAGE}" 2>/dev/null # $1 - Architecture to build for
delete_plugin() {
PLUGIN_ARCH=${1:-amd64}
PLUGIN_IMAGE=${REGISTRY_USER}/net-plugin:${IMAGE_VERSION}-${PLUGIN_ARCH}
echo "Deleting plugin ${PLUGIN_IMAGE}..."
set +e
docker plugin rm "${PLUGIN_IMAGE}" 2>/dev/null
set -e
}
for i in $( echo "$PLATFORMS" | tr ',' ' '); do
delete_plugin "$i"
done
rm -rf "../prog/net-plugin/rootfs"
-39
View File
@@ -1,39 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.13
package poly1305
// Generic fallbacks for the math/bits intrinsics, copied from
// src/math/bits/bits.go. They were added in Go 1.12, but Add64 and Sum64 had
// variable time fallbacks until Go 1.13.
func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) {
sum = x + y + carry
carryOut = ((x & y) | ((x | y) &^ sum)) >> 63
return
}
func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) {
diff = x - y - borrow
borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63
return
}
func bitsMul64(x, y uint64) (hi, lo uint64) {
const mask32 = 1<<32 - 1
x0 := x & mask32
x1 := x >> 32
y0 := y & mask32
y1 := y >> 32
w0 := x0 * y0
t := x1*y0 + w0>>32
w1 := t & mask32
w2 := t >> 32
w1 += x0 * y1
hi = x1*y1 + w2 + w1>>32
lo = x * y
return
}
-21
View File
@@ -1,21 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.13
package poly1305
import "math/bits"
func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) {
return bits.Add64(x, y, carry)
}
func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) {
return bits.Sub64(x, y, borrow)
}
func bitsMul64(x, y uint64) (hi, lo uint64) {
return bits.Mul64(x, y)
}
+23 -20
View File
@@ -7,7 +7,10 @@
package poly1305 package poly1305
import "encoding/binary" import (
"encoding/binary"
"math/bits"
)
// Poly1305 [RFC 7539] is a relatively simple algorithm: the authentication tag // Poly1305 [RFC 7539] is a relatively simple algorithm: the authentication tag
// for a 64 bytes message is approximately // for a 64 bytes message is approximately
@@ -114,13 +117,13 @@ type uint128 struct {
} }
func mul64(a, b uint64) uint128 { func mul64(a, b uint64) uint128 {
hi, lo := bitsMul64(a, b) hi, lo := bits.Mul64(a, b)
return uint128{lo, hi} return uint128{lo, hi}
} }
func add128(a, b uint128) uint128 { func add128(a, b uint128) uint128 {
lo, c := bitsAdd64(a.lo, b.lo, 0) lo, c := bits.Add64(a.lo, b.lo, 0)
hi, c := bitsAdd64(a.hi, b.hi, c) hi, c := bits.Add64(a.hi, b.hi, c)
if c != 0 { if c != 0 {
panic("poly1305: unexpected overflow") panic("poly1305: unexpected overflow")
} }
@@ -155,8 +158,8 @@ func updateGeneric(state *macState, msg []byte) {
// hide leading zeroes. For full chunks, that's 1 << 128, so we can just // hide leading zeroes. For full chunks, that's 1 << 128, so we can just
// add 1 to the most significant (2¹²⁸) limb, h2. // add 1 to the most significant (2¹²⁸) limb, h2.
if len(msg) >= TagSize { if len(msg) >= TagSize {
h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0) h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0)
h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(msg[8:16]), c) h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(msg[8:16]), c)
h2 += c + 1 h2 += c + 1
msg = msg[TagSize:] msg = msg[TagSize:]
@@ -165,8 +168,8 @@ func updateGeneric(state *macState, msg []byte) {
copy(buf[:], msg) copy(buf[:], msg)
buf[len(msg)] = 1 buf[len(msg)] = 1
h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0) h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0)
h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(buf[8:16]), c) h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(buf[8:16]), c)
h2 += c h2 += c
msg = nil msg = nil
@@ -219,9 +222,9 @@ func updateGeneric(state *macState, msg []byte) {
m3 := h2r1 m3 := h2r1
t0 := m0.lo t0 := m0.lo
t1, c := bitsAdd64(m1.lo, m0.hi, 0) t1, c := bits.Add64(m1.lo, m0.hi, 0)
t2, c := bitsAdd64(m2.lo, m1.hi, c) t2, c := bits.Add64(m2.lo, m1.hi, c)
t3, _ := bitsAdd64(m3.lo, m2.hi, c) t3, _ := bits.Add64(m3.lo, m2.hi, c)
// Now we have the result as 4 64-bit limbs, and we need to reduce it // Now we have the result as 4 64-bit limbs, and we need to reduce it
// modulo 2¹³⁰ - 5. The special shape of this Crandall prime lets us do // modulo 2¹³⁰ - 5. The special shape of this Crandall prime lets us do
@@ -243,14 +246,14 @@ func updateGeneric(state *macState, msg []byte) {
// To add c * 5 to h, we first add cc = c * 4, and then add (cc >> 2) = c. // To add c * 5 to h, we first add cc = c * 4, and then add (cc >> 2) = c.
h0, c = bitsAdd64(h0, cc.lo, 0) h0, c = bits.Add64(h0, cc.lo, 0)
h1, c = bitsAdd64(h1, cc.hi, c) h1, c = bits.Add64(h1, cc.hi, c)
h2 += c h2 += c
cc = shiftRightBy2(cc) cc = shiftRightBy2(cc)
h0, c = bitsAdd64(h0, cc.lo, 0) h0, c = bits.Add64(h0, cc.lo, 0)
h1, c = bitsAdd64(h1, cc.hi, c) h1, c = bits.Add64(h1, cc.hi, c)
h2 += c h2 += c
// h2 is at most 3 + 1 + 1 = 5, making the whole of h at most // h2 is at most 3 + 1 + 1 = 5, making the whole of h at most
@@ -287,9 +290,9 @@ func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) {
// in constant time, we compute t = h - (2¹³⁰ - 5), and select h as the // in constant time, we compute t = h - (2¹³⁰ - 5), and select h as the
// result if the subtraction underflows, and t otherwise. // result if the subtraction underflows, and t otherwise.
hMinusP0, b := bitsSub64(h0, p0, 0) hMinusP0, b := bits.Sub64(h0, p0, 0)
hMinusP1, b := bitsSub64(h1, p1, b) hMinusP1, b := bits.Sub64(h1, p1, b)
_, b = bitsSub64(h2, p2, b) _, b = bits.Sub64(h2, p2, b)
// h = h if h < p else h - p // h = h if h < p else h - p
h0 = select64(b, h0, hMinusP0) h0 = select64(b, h0, hMinusP0)
@@ -301,8 +304,8 @@ func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) {
// //
// by just doing a wide addition with the 128 low bits of h and discarding // by just doing a wide addition with the 128 low bits of h and discarding
// the overflow. // the overflow.
h0, c := bitsAdd64(h0, s[0], 0) h0, c := bits.Add64(h0, s[0], 0)
h1, _ = bitsAdd64(h1, s[1], c) h1, _ = bits.Add64(h1, s[1], c)
binary.LittleEndian.PutUint64(out[0:8], h0) binary.LittleEndian.PutUint64(out[0:8], h0)
binary.LittleEndian.PutUint64(out[8:16], h1) binary.LittleEndian.PutUint64(out[8:16], h1)
+6 -8
View File
@@ -19,15 +19,14 @@
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3, t4, t5) \ #define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3, t4, t5) \
MULLD r0, h0, t0; \ MULLD r0, h0, t0; \
MULLD r0, h1, t4; \
MULHDU r0, h0, t1; \ MULHDU r0, h0, t1; \
MULLD r0, h1, t4; \
MULHDU r0, h1, t5; \ MULHDU r0, h1, t5; \
ADDC t4, t1, t1; \ ADDC t4, t1, t1; \
MULLD r0, h2, t2; \ MULLD r0, h2, t2; \
ADDZE t5; \
MULHDU r1, h0, t4; \ MULHDU r1, h0, t4; \
MULLD r1, h0, h0; \ MULLD r1, h0, h0; \
ADD t5, t2, t2; \ ADDE t5, t2, t2; \
ADDC h0, t1, t1; \ ADDC h0, t1, t1; \
MULLD h2, r1, t3; \ MULLD h2, r1, t3; \
ADDZE t4, h0; \ ADDZE t4, h0; \
@@ -37,13 +36,11 @@
ADDE t5, t3, t3; \ ADDE t5, t3, t3; \
ADDC h0, t2, t2; \ ADDC h0, t2, t2; \
MOVD $-4, t4; \ MOVD $-4, t4; \
MOVD t0, h0; \
MOVD t1, h1; \
ADDZE t3; \ ADDZE t3; \
ANDCC $3, t2, h2; \ RLDICL $0, t2, $62, h2; \
AND t2, t4, t0; \ AND t2, t4, h0; \
ADDC t0, h0, h0; \ ADDC t0, h0, h0; \
ADDE t3, h1, h1; \ ADDE t3, t1, h1; \
SLD $62, t3, t4; \ SLD $62, t3, t4; \
SRD $2, t2; \ SRD $2, t2; \
ADDZE h2; \ ADDZE h2; \
@@ -75,6 +72,7 @@ TEXT ·update(SB), $0-32
loop: loop:
POLY1305_ADD(R4, R8, R9, R10, R20, R21, R22) POLY1305_ADD(R4, R8, R9, R10, R20, R21, R22)
PCALIGN $16
multiply: multiply:
POLY1305_MUL(R8, R9, R10, R11, R12, R16, R17, R18, R14, R20, R21) POLY1305_MUL(R8, R9, R10, R11, R12, R16, R17, R18, R14, R20, R21)
ADD $-16, R5 ADD $-16, R5
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.7 //go:build go1.7
// +build go1.7
package context package context
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.9 //go:build go1.9
// +build go1.9
package context package context
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.7 //go:build !go1.7
// +build !go1.7
package context package context
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.9 //go:build !go1.9
// +build !go1.9
package context package context
+31 -28
View File
@@ -20,41 +20,44 @@ import (
// TODO: Benchmark to determine if the pools are necessary. The GC may have // TODO: Benchmark to determine if the pools are necessary. The GC may have
// improved enough that we can instead allocate chunks like this: // improved enough that we can instead allocate chunks like this:
// make([]byte, max(16<<10, expectedBytesRemaining)) // make([]byte, max(16<<10, expectedBytesRemaining))
var ( var dataChunkPools = [...]sync.Pool{
dataChunkSizeClasses = []int{ {New: func() interface{} { return new([1 << 10]byte) }},
1 << 10, {New: func() interface{} { return new([2 << 10]byte) }},
2 << 10, {New: func() interface{} { return new([4 << 10]byte) }},
4 << 10, {New: func() interface{} { return new([8 << 10]byte) }},
8 << 10, {New: func() interface{} { return new([16 << 10]byte) }},
16 << 10, }
}
dataChunkPools = [...]sync.Pool{
{New: func() interface{} { return make([]byte, 1<<10) }},
{New: func() interface{} { return make([]byte, 2<<10) }},
{New: func() interface{} { return make([]byte, 4<<10) }},
{New: func() interface{} { return make([]byte, 8<<10) }},
{New: func() interface{} { return make([]byte, 16<<10) }},
}
)
func getDataBufferChunk(size int64) []byte { func getDataBufferChunk(size int64) []byte {
i := 0 switch {
for ; i < len(dataChunkSizeClasses)-1; i++ { case size <= 1<<10:
if size <= int64(dataChunkSizeClasses[i]) { return dataChunkPools[0].Get().(*[1 << 10]byte)[:]
break case size <= 2<<10:
} return dataChunkPools[1].Get().(*[2 << 10]byte)[:]
case size <= 4<<10:
return dataChunkPools[2].Get().(*[4 << 10]byte)[:]
case size <= 8<<10:
return dataChunkPools[3].Get().(*[8 << 10]byte)[:]
default:
return dataChunkPools[4].Get().(*[16 << 10]byte)[:]
} }
return dataChunkPools[i].Get().([]byte)
} }
func putDataBufferChunk(p []byte) { func putDataBufferChunk(p []byte) {
for i, n := range dataChunkSizeClasses { switch len(p) {
if len(p) == n { case 1 << 10:
dataChunkPools[i].Put(p) dataChunkPools[0].Put((*[1 << 10]byte)(p))
return case 2 << 10:
} dataChunkPools[1].Put((*[2 << 10]byte)(p))
case 4 << 10:
dataChunkPools[2].Put((*[4 << 10]byte)(p))
case 8 << 10:
dataChunkPools[3].Put((*[8 << 10]byte)(p))
case 16 << 10:
dataChunkPools[4].Put((*[16 << 10]byte)(p))
default:
panic(fmt.Sprintf("unexpected buffer len=%v", len(p)))
} }
panic(fmt.Sprintf("unexpected buffer len=%v", len(p)))
} }
// dataBuffer is an io.ReadWriter backed by a list of data chunks. // dataBuffer is an io.ReadWriter backed by a list of data chunks.
+36 -6
View File
@@ -1510,13 +1510,12 @@ func (mh *MetaHeadersFrame) checkPseudos() error {
} }
func (fr *Framer) maxHeaderStringLen() int { func (fr *Framer) maxHeaderStringLen() int {
v := fr.maxHeaderListSize() v := int(fr.maxHeaderListSize())
if uint32(int(v)) == v { if v < 0 {
return int(v) // If maxHeaderListSize overflows an int, use no limit (0).
return 0
} }
// They had a crazy big number for MaxHeaderBytes anyway, return v
// so give them unlimited header lengths:
return 0
} }
// readMetaFrame returns 0 or more CONTINUATION frames from fr and // readMetaFrame returns 0 or more CONTINUATION frames from fr and
@@ -1565,6 +1564,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
if size > remainSize { if size > remainSize {
hdec.SetEmitEnabled(false) hdec.SetEmitEnabled(false)
mh.Truncated = true mh.Truncated = true
remainSize = 0
return return
} }
remainSize -= size remainSize -= size
@@ -1577,6 +1577,36 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
var hc headersOrContinuation = hf var hc headersOrContinuation = hf
for { for {
frag := hc.HeaderBlockFragment() frag := hc.HeaderBlockFragment()
// Avoid parsing large amounts of headers that we will then discard.
// If the sender exceeds the max header list size by too much,
// skip parsing the fragment and close the connection.
//
// "Too much" is either any CONTINUATION frame after we've already
// exceeded the max header list size (in which case remainSize is 0),
// or a frame whose encoded size is more than twice the remaining
// header list bytes we're willing to accept.
if int64(len(frag)) > int64(2*remainSize) {
if VerboseLogs {
log.Printf("http2: header list too large")
}
// It would be nice to send a RST_STREAM before sending the GOAWAY,
// but the structure of the server's frame writer makes this difficult.
return nil, ConnectionError(ErrCodeProtocol)
}
// Also close the connection after any CONTINUATION frame following an
// invalid header, since we stop tracking the size of the headers after
// an invalid one.
if invalid != nil {
if VerboseLogs {
log.Printf("http2: invalid header: %v", invalid)
}
// It would be nice to send a RST_STREAM before sending the GOAWAY,
// but the structure of the server's frame writer makes this difficult.
return nil, ConnectionError(ErrCodeProtocol)
}
if _, err := hdec.Write(frag); err != nil { if _, err := hdec.Write(frag); err != nil {
return nil, ConnectionError(ErrCodeCompression) return nil, ConnectionError(ErrCodeCompression)
} }
-30
View File
@@ -1,30 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.11
// +build go1.11
package http2
import (
"net/http/httptrace"
"net/textproto"
)
func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool {
return trace != nil && trace.WroteHeaderField != nil
}
func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {
if trace != nil && trace.WroteHeaderField != nil {
trace.WroteHeaderField(k, []string{v})
}
}
func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
if trace != nil {
return trace.Got1xxResponse
}
return nil
}
-27
View File
@@ -1,27 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.15
// +build go1.15
package http2
import (
"context"
"crypto/tls"
)
// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS
// connection.
func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) {
dialer := &tls.Dialer{
Config: cfg,
}
cn, err := dialer.DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed
return tlsCn, nil
}
-17
View File
@@ -1,17 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
package http2
import (
"crypto/tls"
"net"
)
func tlsUnderlyingConn(tc *tls.Conn) net.Conn {
return tc.NetConn()
}
-21
View File
@@ -1,21 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.11
// +build !go1.11
package http2
import (
"net/http/httptrace"
"net/textproto"
)
func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false }
func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {}
func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
return nil
}
-31
View File
@@ -1,31 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.15
// +build !go1.15
package http2
import (
"context"
"crypto/tls"
)
// dialTLSWithContext opens a TLS connection.
func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) {
cn, err := tls.Dial(network, addr, cfg)
if err != nil {
return nil, err
}
if err := cn.Handshake(); err != nil {
return nil, err
}
if cfg.InsecureSkipVerify {
return cn, nil
}
if err := cn.VerifyHostname(cfg.ServerName); err != nil {
return nil, err
}
return cn, nil
}
-17
View File
@@ -1,17 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
package http2
import (
"crypto/tls"
"net"
)
func tlsUnderlyingConn(tc *tls.Conn) net.Conn {
return nil
}
+10 -1
View File
@@ -77,7 +77,10 @@ func (p *pipe) Read(d []byte) (n int, err error) {
} }
} }
var errClosedPipeWrite = errors.New("write on closed buffer") var (
errClosedPipeWrite = errors.New("write on closed buffer")
errUninitializedPipeWrite = errors.New("write on uninitialized buffer")
)
// Write copies bytes from p into the buffer and wakes a reader. // Write copies bytes from p into the buffer and wakes a reader.
// It is an error to write more data than the buffer can hold. // It is an error to write more data than the buffer can hold.
@@ -91,6 +94,12 @@ func (p *pipe) Write(d []byte) (n int, err error) {
if p.err != nil || p.breakErr != nil { if p.err != nil || p.breakErr != nil {
return 0, errClosedPipeWrite return 0, errClosedPipeWrite
} }
// pipe.setBuffer is never invoked, leaving the buffer uninitialized.
// We shouldn't try to write to an uninitialized pipe,
// but returning an error is better than panicking.
if p.b == nil {
return 0, errUninitializedPipeWrite
}
return p.b.Write(d) return p.b.Write(d)
} }
+11 -26
View File
@@ -124,6 +124,7 @@ type Server struct {
// IdleTimeout specifies how long until idle clients should be // IdleTimeout specifies how long until idle clients should be
// closed with a GOAWAY frame. PING frames are not considered // closed with a GOAWAY frame. PING frames are not considered
// activity for the purposes of IdleTimeout. // activity for the purposes of IdleTimeout.
// If zero or negative, there is no timeout.
IdleTimeout time.Duration IdleTimeout time.Duration
// MaxUploadBufferPerConnection is the size of the initial flow // MaxUploadBufferPerConnection is the size of the initial flow
@@ -434,7 +435,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
// passes the connection off to us with the deadline already set. // passes the connection off to us with the deadline already set.
// Write deadlines are set per stream in serverConn.newStream. // Write deadlines are set per stream in serverConn.newStream.
// Disarm the net.Conn write deadline here. // Disarm the net.Conn write deadline here.
if sc.hs.WriteTimeout != 0 { if sc.hs.WriteTimeout > 0 {
sc.conn.SetWriteDeadline(time.Time{}) sc.conn.SetWriteDeadline(time.Time{})
} }
@@ -924,7 +925,7 @@ func (sc *serverConn) serve() {
sc.setConnState(http.StateActive) sc.setConnState(http.StateActive)
sc.setConnState(http.StateIdle) sc.setConnState(http.StateIdle)
if sc.srv.IdleTimeout != 0 { if sc.srv.IdleTimeout > 0 {
sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer) sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer)
defer sc.idleTimer.Stop() defer sc.idleTimer.Stop()
} }
@@ -1637,7 +1638,7 @@ func (sc *serverConn) closeStream(st *stream, err error) {
delete(sc.streams, st.id) delete(sc.streams, st.id)
if len(sc.streams) == 0 { if len(sc.streams) == 0 {
sc.setConnState(http.StateIdle) sc.setConnState(http.StateIdle)
if sc.srv.IdleTimeout != 0 { if sc.srv.IdleTimeout > 0 {
sc.idleTimer.Reset(sc.srv.IdleTimeout) sc.idleTimer.Reset(sc.srv.IdleTimeout)
} }
if h1ServerKeepAlivesDisabled(sc.hs) { if h1ServerKeepAlivesDisabled(sc.hs) {
@@ -2017,7 +2018,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
// similar to how the http1 server works. Here it's // similar to how the http1 server works. Here it's
// technically more like the http1 Server's ReadHeaderTimeout // technically more like the http1 Server's ReadHeaderTimeout
// (in Go 1.8), though. That's a more sane option anyway. // (in Go 1.8), though. That's a more sane option anyway.
if sc.hs.ReadTimeout != 0 { if sc.hs.ReadTimeout > 0 {
sc.conn.SetReadDeadline(time.Time{}) sc.conn.SetReadDeadline(time.Time{})
st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout) st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout)
} }
@@ -2038,7 +2039,7 @@ func (sc *serverConn) upgradeRequest(req *http.Request) {
// Disable any read deadline set by the net/http package // Disable any read deadline set by the net/http package
// prior to the upgrade. // prior to the upgrade.
if sc.hs.ReadTimeout != 0 { if sc.hs.ReadTimeout > 0 {
sc.conn.SetReadDeadline(time.Time{}) sc.conn.SetReadDeadline(time.Time{})
} }
@@ -2116,7 +2117,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
st.flow.conn = &sc.flow // link to conn-level counter st.flow.conn = &sc.flow // link to conn-level counter
st.flow.add(sc.initialStreamSendWindowSize) st.flow.add(sc.initialStreamSendWindowSize)
st.inflow.init(sc.srv.initialStreamRecvWindowSize()) st.inflow.init(sc.srv.initialStreamRecvWindowSize())
if sc.hs.WriteTimeout != 0 { if sc.hs.WriteTimeout > 0 {
st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout) st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout)
} }
@@ -2549,7 +2550,6 @@ type responseWriterState struct {
wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet.
sentHeader bool // have we sent the header frame? sentHeader bool // have we sent the header frame?
handlerDone bool // handler has finished handlerDone bool // handler has finished
dirty bool // a Write failed; don't reuse this responseWriterState
sentContentLen int64 // non-zero if handler set a Content-Length header sentContentLen int64 // non-zero if handler set a Content-Length header
wroteBytes int64 wroteBytes int64
@@ -2669,7 +2669,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
date: date, date: date,
}) })
if err != nil { if err != nil {
rws.dirty = true
return 0, err return 0, err
} }
if endStream { if endStream {
@@ -2690,7 +2689,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
if len(p) > 0 || endStream { if len(p) > 0 || endStream {
// only send a 0 byte DATA frame if we're ending the stream. // only send a 0 byte DATA frame if we're ending the stream.
if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil { if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil {
rws.dirty = true
return 0, err return 0, err
} }
} }
@@ -2702,9 +2700,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
trailers: rws.trailers, trailers: rws.trailers,
endStream: true, endStream: true,
}) })
if err != nil {
rws.dirty = true
}
return len(p), err return len(p), err
} }
return len(p), nil return len(p), nil
@@ -2920,14 +2915,12 @@ func (rws *responseWriterState) writeHeader(code int) {
h.Del("Transfer-Encoding") h.Del("Transfer-Encoding")
} }
if rws.conn.writeHeaders(rws.stream, &writeResHeaders{ rws.conn.writeHeaders(rws.stream, &writeResHeaders{
streamID: rws.stream.id, streamID: rws.stream.id,
httpResCode: code, httpResCode: code,
h: h, h: h,
endStream: rws.handlerDone && !rws.hasTrailers(), endStream: rws.handlerDone && !rws.hasTrailers(),
}) != nil { })
rws.dirty = true
}
return return
} }
@@ -2992,19 +2985,10 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int,
func (w *responseWriter) handlerDone() { func (w *responseWriter) handlerDone() {
rws := w.rws rws := w.rws
dirty := rws.dirty
rws.handlerDone = true rws.handlerDone = true
w.Flush() w.Flush()
w.rws = nil w.rws = nil
if !dirty { responseWriterStatePool.Put(rws)
// Only recycle the pool if all prior Write calls to
// the serverConn goroutine completed successfully. If
// they returned earlier due to resets from the peer
// there might still be write goroutines outstanding
// from the serverConn referencing the rws memory. See
// issue 20704.
responseWriterStatePool.Put(rws)
}
} }
// Push errors. // Push errors.
@@ -3187,6 +3171,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) {
panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err)) panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err))
} }
sc.curHandlers++
go sc.runHandler(rw, req, sc.handler.ServeHTTP) go sc.runHandler(rw, req, sc.handler.ServeHTTP)
return promisedID, nil return promisedID, nil
} }
+331
View File
@@ -0,0 +1,331 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package http2
import (
"context"
"sync"
"time"
)
// testSyncHooks coordinates goroutines in tests.
//
// For example, a call to ClientConn.RoundTrip involves several goroutines, including:
// - the goroutine running RoundTrip;
// - the clientStream.doRequest goroutine, which writes the request; and
// - the clientStream.readLoop goroutine, which reads the response.
//
// Using testSyncHooks, a test can start a RoundTrip and identify when all these goroutines
// are blocked waiting for some condition such as reading the Request.Body or waiting for
// flow control to become available.
//
// The testSyncHooks also manage timers and synthetic time in tests.
// This permits us to, for example, start a request and cause it to time out waiting for
// response headers without resorting to time.Sleep calls.
type testSyncHooks struct {
// active/inactive act as a mutex and condition variable.
//
// - neither chan contains a value: testSyncHooks is locked.
// - active contains a value: unlocked, and at least one goroutine is not blocked
// - inactive contains a value: unlocked, and all goroutines are blocked
active chan struct{}
inactive chan struct{}
// goroutine counts
total int // total goroutines
condwait map[*sync.Cond]int // blocked in sync.Cond.Wait
blocked []*testBlockedGoroutine // otherwise blocked
// fake time
now time.Time
timers []*fakeTimer
// Transport testing: Report various events.
newclientconn func(*ClientConn)
newstream func(*clientStream)
}
// testBlockedGoroutine is a blocked goroutine.
type testBlockedGoroutine struct {
f func() bool // blocked until f returns true
ch chan struct{} // closed when unblocked
}
func newTestSyncHooks() *testSyncHooks {
h := &testSyncHooks{
active: make(chan struct{}, 1),
inactive: make(chan struct{}, 1),
condwait: map[*sync.Cond]int{},
}
h.inactive <- struct{}{}
h.now = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
return h
}
// lock acquires the testSyncHooks mutex.
func (h *testSyncHooks) lock() {
select {
case <-h.active:
case <-h.inactive:
}
}
// waitInactive waits for all goroutines to become inactive.
func (h *testSyncHooks) waitInactive() {
for {
<-h.inactive
if !h.unlock() {
break
}
}
}
// unlock releases the testSyncHooks mutex.
// It reports whether any goroutines are active.
func (h *testSyncHooks) unlock() (active bool) {
// Look for a blocked goroutine which can be unblocked.
blocked := h.blocked[:0]
unblocked := false
for _, b := range h.blocked {
if !unblocked && b.f() {
unblocked = true
close(b.ch)
} else {
blocked = append(blocked, b)
}
}
h.blocked = blocked
// Count goroutines blocked on condition variables.
condwait := 0
for _, count := range h.condwait {
condwait += count
}
if h.total > condwait+len(blocked) {
h.active <- struct{}{}
return true
} else {
h.inactive <- struct{}{}
return false
}
}
// goRun starts a new goroutine.
func (h *testSyncHooks) goRun(f func()) {
h.lock()
h.total++
h.unlock()
go func() {
defer func() {
h.lock()
h.total--
h.unlock()
}()
f()
}()
}
// blockUntil indicates that a goroutine is blocked waiting for some condition to become true.
// It waits until f returns true before proceeding.
//
// Example usage:
//
// h.blockUntil(func() bool {
// // Is the context done yet?
// select {
// case <-ctx.Done():
// default:
// return false
// }
// return true
// })
// // Wait for the context to become done.
// <-ctx.Done()
//
// The function f passed to blockUntil must be non-blocking and idempotent.
func (h *testSyncHooks) blockUntil(f func() bool) {
if f() {
return
}
ch := make(chan struct{})
h.lock()
h.blocked = append(h.blocked, &testBlockedGoroutine{
f: f,
ch: ch,
})
h.unlock()
<-ch
}
// broadcast is sync.Cond.Broadcast.
func (h *testSyncHooks) condBroadcast(cond *sync.Cond) {
h.lock()
delete(h.condwait, cond)
h.unlock()
cond.Broadcast()
}
// broadcast is sync.Cond.Wait.
func (h *testSyncHooks) condWait(cond *sync.Cond) {
h.lock()
h.condwait[cond]++
h.unlock()
}
// newTimer creates a new fake timer.
func (h *testSyncHooks) newTimer(d time.Duration) timer {
h.lock()
defer h.unlock()
t := &fakeTimer{
hooks: h,
when: h.now.Add(d),
c: make(chan time.Time),
}
h.timers = append(h.timers, t)
return t
}
// afterFunc creates a new fake AfterFunc timer.
func (h *testSyncHooks) afterFunc(d time.Duration, f func()) timer {
h.lock()
defer h.unlock()
t := &fakeTimer{
hooks: h,
when: h.now.Add(d),
f: f,
}
h.timers = append(h.timers, t)
return t
}
func (h *testSyncHooks) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(ctx)
t := h.afterFunc(d, cancel)
return ctx, func() {
t.Stop()
cancel()
}
}
func (h *testSyncHooks) timeUntilEvent() time.Duration {
h.lock()
defer h.unlock()
var next time.Time
for _, t := range h.timers {
if next.IsZero() || t.when.Before(next) {
next = t.when
}
}
if d := next.Sub(h.now); d > 0 {
return d
}
return 0
}
// advance advances time and causes synthetic timers to fire.
func (h *testSyncHooks) advance(d time.Duration) {
h.lock()
defer h.unlock()
h.now = h.now.Add(d)
timers := h.timers[:0]
for _, t := range h.timers {
t := t // remove after go.mod depends on go1.22
t.mu.Lock()
switch {
case t.when.After(h.now):
timers = append(timers, t)
case t.when.IsZero():
// stopped timer
default:
t.when = time.Time{}
if t.c != nil {
close(t.c)
}
if t.f != nil {
h.total++
go func() {
defer func() {
h.lock()
h.total--
h.unlock()
}()
t.f()
}()
}
}
t.mu.Unlock()
}
h.timers = timers
}
// A timer wraps a time.Timer, or a synthetic equivalent in tests.
// Unlike time.Timer, timer is single-use: The timer channel is closed when the timer expires.
type timer interface {
C() <-chan time.Time
Stop() bool
Reset(d time.Duration) bool
}
// timeTimer implements timer using real time.
type timeTimer struct {
t *time.Timer
c chan time.Time
}
// newTimeTimer creates a new timer using real time.
func newTimeTimer(d time.Duration) timer {
ch := make(chan time.Time)
t := time.AfterFunc(d, func() {
close(ch)
})
return &timeTimer{t, ch}
}
// newTimeAfterFunc creates an AfterFunc timer using real time.
func newTimeAfterFunc(d time.Duration, f func()) timer {
return &timeTimer{
t: time.AfterFunc(d, f),
}
}
func (t timeTimer) C() <-chan time.Time { return t.c }
func (t timeTimer) Stop() bool { return t.t.Stop() }
func (t timeTimer) Reset(d time.Duration) bool { return t.t.Reset(d) }
// fakeTimer implements timer using fake time.
type fakeTimer struct {
hooks *testSyncHooks
mu sync.Mutex
when time.Time // when the timer will fire
c chan time.Time // closed when the timer fires; mutually exclusive with f
f func() // called when the timer fires; mutually exclusive with c
}
func (t *fakeTimer) C() <-chan time.Time { return t.c }
func (t *fakeTimer) Stop() bool {
t.mu.Lock()
defer t.mu.Unlock()
stopped := t.when.IsZero()
t.when = time.Time{}
return stopped
}
func (t *fakeTimer) Reset(d time.Duration) bool {
if t.c != nil || t.f == nil {
panic("fakeTimer only supports Reset on AfterFunc timers")
}
t.mu.Lock()
defer t.mu.Unlock()
t.hooks.lock()
defer t.hooks.unlock()
active := !t.when.IsZero()
t.when = t.hooks.now.Add(d)
if !active {
t.hooks.timers = append(t.hooks.timers, t)
}
return active
}
+278 -62
View File
@@ -147,6 +147,12 @@ type Transport struct {
// waiting for their turn. // waiting for their turn.
StrictMaxConcurrentStreams bool StrictMaxConcurrentStreams bool
// IdleConnTimeout is the maximum amount of time an idle
// (keep-alive) connection will remain idle before closing
// itself.
// Zero means no limit.
IdleConnTimeout time.Duration
// ReadIdleTimeout is the timeout after which a health check using ping // ReadIdleTimeout is the timeout after which a health check using ping
// frame will be carried out if no frame is received on the connection. // frame will be carried out if no frame is received on the connection.
// Note that a ping response will is considered a received frame, so if // Note that a ping response will is considered a received frame, so if
@@ -178,6 +184,8 @@ type Transport struct {
connPoolOnce sync.Once connPoolOnce sync.Once
connPoolOrDef ClientConnPool // non-nil version of ConnPool connPoolOrDef ClientConnPool // non-nil version of ConnPool
syncHooks *testSyncHooks
} }
func (t *Transport) maxHeaderListSize() uint32 { func (t *Transport) maxHeaderListSize() uint32 {
@@ -302,7 +310,7 @@ type ClientConn struct {
readerErr error // set before readerDone is closed readerErr error // set before readerDone is closed
idleTimeout time.Duration // or 0 for never idleTimeout time.Duration // or 0 for never
idleTimer *time.Timer idleTimer timer
mu sync.Mutex // guards following mu sync.Mutex // guards following
cond *sync.Cond // hold mu; broadcast on flow/closed changes cond *sync.Cond // hold mu; broadcast on flow/closed changes
@@ -344,6 +352,60 @@ type ClientConn struct {
werr error // first write error that has occurred werr error // first write error that has occurred
hbuf bytes.Buffer // HPACK encoder writes into this hbuf bytes.Buffer // HPACK encoder writes into this
henc *hpack.Encoder henc *hpack.Encoder
syncHooks *testSyncHooks // can be nil
}
// Hook points used for testing.
// Outside of tests, cc.syncHooks is nil and these all have minimal implementations.
// Inside tests, see the testSyncHooks function docs.
// goRun starts a new goroutine.
func (cc *ClientConn) goRun(f func()) {
if cc.syncHooks != nil {
cc.syncHooks.goRun(f)
return
}
go f()
}
// condBroadcast is cc.cond.Broadcast.
func (cc *ClientConn) condBroadcast() {
if cc.syncHooks != nil {
cc.syncHooks.condBroadcast(cc.cond)
}
cc.cond.Broadcast()
}
// condWait is cc.cond.Wait.
func (cc *ClientConn) condWait() {
if cc.syncHooks != nil {
cc.syncHooks.condWait(cc.cond)
}
cc.cond.Wait()
}
// newTimer creates a new time.Timer, or a synthetic timer in tests.
func (cc *ClientConn) newTimer(d time.Duration) timer {
if cc.syncHooks != nil {
return cc.syncHooks.newTimer(d)
}
return newTimeTimer(d)
}
// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests.
func (cc *ClientConn) afterFunc(d time.Duration, f func()) timer {
if cc.syncHooks != nil {
return cc.syncHooks.afterFunc(d, f)
}
return newTimeAfterFunc(d, f)
}
func (cc *ClientConn) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {
if cc.syncHooks != nil {
return cc.syncHooks.contextWithTimeout(ctx, d)
}
return context.WithTimeout(ctx, d)
} }
// clientStream is the state for a single HTTP/2 stream. One of these // clientStream is the state for a single HTTP/2 stream. One of these
@@ -425,7 +487,7 @@ func (cs *clientStream) abortStreamLocked(err error) {
// TODO(dneil): Clean up tests where cs.cc.cond is nil. // TODO(dneil): Clean up tests where cs.cc.cond is nil.
if cs.cc.cond != nil { if cs.cc.cond != nil {
// Wake up writeRequestBody if it is waiting on flow control. // Wake up writeRequestBody if it is waiting on flow control.
cs.cc.cond.Broadcast() cs.cc.condBroadcast()
} }
} }
@@ -435,7 +497,7 @@ func (cs *clientStream) abortRequestBodyWrite() {
defer cc.mu.Unlock() defer cc.mu.Unlock()
if cs.reqBody != nil && cs.reqBodyClosed == nil { if cs.reqBody != nil && cs.reqBodyClosed == nil {
cs.closeReqBodyLocked() cs.closeReqBodyLocked()
cc.cond.Broadcast() cc.condBroadcast()
} }
} }
@@ -445,10 +507,10 @@ func (cs *clientStream) closeReqBodyLocked() {
} }
cs.reqBodyClosed = make(chan struct{}) cs.reqBodyClosed = make(chan struct{})
reqBodyClosed := cs.reqBodyClosed reqBodyClosed := cs.reqBodyClosed
go func() { cs.cc.goRun(func() {
cs.reqBody.Close() cs.reqBody.Close()
close(reqBodyClosed) close(reqBodyClosed)
}() })
} }
type stickyErrWriter struct { type stickyErrWriter struct {
@@ -537,15 +599,6 @@ func authorityAddr(scheme string, authority string) (addr string) {
return net.JoinHostPort(host, port) return net.JoinHostPort(host, port)
} }
var retryBackoffHook func(time.Duration) *time.Timer
func backoffNewTimer(d time.Duration) *time.Timer {
if retryBackoffHook != nil {
return retryBackoffHook(d)
}
return time.NewTimer(d)
}
// RoundTripOpt is like RoundTrip, but takes options. // RoundTripOpt is like RoundTrip, but takes options.
func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) { func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) { if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) {
@@ -573,13 +626,27 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
backoff := float64(uint(1) << (uint(retry) - 1)) backoff := float64(uint(1) << (uint(retry) - 1))
backoff += backoff * (0.1 * mathrand.Float64()) backoff += backoff * (0.1 * mathrand.Float64())
d := time.Second * time.Duration(backoff) d := time.Second * time.Duration(backoff)
timer := backoffNewTimer(d) var tm timer
if t.syncHooks != nil {
tm = t.syncHooks.newTimer(d)
t.syncHooks.blockUntil(func() bool {
select {
case <-tm.C():
case <-req.Context().Done():
default:
return false
}
return true
})
} else {
tm = newTimeTimer(d)
}
select { select {
case <-timer.C: case <-tm.C():
t.vlogf("RoundTrip retrying after failure: %v", roundTripErr) t.vlogf("RoundTrip retrying after failure: %v", roundTripErr)
continue continue
case <-req.Context().Done(): case <-req.Context().Done():
timer.Stop() tm.Stop()
err = req.Context().Err() err = req.Context().Err()
} }
} }
@@ -658,6 +725,9 @@ func canRetryError(err error) bool {
} }
func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) { func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) {
if t.syncHooks != nil {
return t.newClientConn(nil, singleUse, t.syncHooks)
}
host, _, err := net.SplitHostPort(addr) host, _, err := net.SplitHostPort(addr)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -666,7 +736,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b
if err != nil { if err != nil {
return nil, err return nil, err
} }
return t.newClientConn(tconn, singleUse) return t.newClientConn(tconn, singleUse, nil)
} }
func (t *Transport) newTLSConfig(host string) *tls.Config { func (t *Transport) newTLSConfig(host string) *tls.Config {
@@ -732,10 +802,10 @@ func (t *Transport) maxEncoderHeaderTableSize() uint32 {
} }
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
return t.newClientConn(c, t.disableKeepAlives()) return t.newClientConn(c, t.disableKeepAlives(), nil)
} }
func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHooks) (*ClientConn, error) {
cc := &ClientConn{ cc := &ClientConn{
t: t, t: t,
tconn: c, tconn: c,
@@ -750,10 +820,15 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
wantSettingsAck: true, wantSettingsAck: true,
pings: make(map[[8]byte]chan struct{}), pings: make(map[[8]byte]chan struct{}),
reqHeaderMu: make(chan struct{}, 1), reqHeaderMu: make(chan struct{}, 1),
syncHooks: hooks,
}
if hooks != nil {
hooks.newclientconn(cc)
c = cc.tconn
} }
if d := t.idleConnTimeout(); d != 0 { if d := t.idleConnTimeout(); d != 0 {
cc.idleTimeout = d cc.idleTimeout = d
cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout) cc.idleTimer = cc.afterFunc(d, cc.onIdleTimeout)
} }
if VerboseLogs { if VerboseLogs {
t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr())
@@ -818,7 +893,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
return nil, cc.werr return nil, cc.werr
} }
go cc.readLoop() cc.goRun(cc.readLoop)
return cc, nil return cc, nil
} }
@@ -826,7 +901,7 @@ func (cc *ClientConn) healthCheck() {
pingTimeout := cc.t.pingTimeout() pingTimeout := cc.t.pingTimeout()
// We don't need to periodically ping in the health check, because the readLoop of ClientConn will // We don't need to periodically ping in the health check, because the readLoop of ClientConn will
// trigger the healthCheck again if there is no frame received. // trigger the healthCheck again if there is no frame received.
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout) ctx, cancel := cc.contextWithTimeout(context.Background(), pingTimeout)
defer cancel() defer cancel()
cc.vlogf("http2: Transport sending health check") cc.vlogf("http2: Transport sending health check")
err := cc.Ping(ctx) err := cc.Ping(ctx)
@@ -1018,7 +1093,7 @@ func (cc *ClientConn) forceCloseConn() {
if !ok { if !ok {
return return
} }
if nc := tlsUnderlyingConn(tc); nc != nil { if nc := tc.NetConn(); nc != nil {
nc.Close() nc.Close()
} }
} }
@@ -1056,7 +1131,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
// Wait for all in-flight streams to complete or connection to close // Wait for all in-flight streams to complete or connection to close
done := make(chan struct{}) done := make(chan struct{})
cancelled := false // guarded by cc.mu cancelled := false // guarded by cc.mu
go func() { cc.goRun(func() {
cc.mu.Lock() cc.mu.Lock()
defer cc.mu.Unlock() defer cc.mu.Unlock()
for { for {
@@ -1068,9 +1143,9 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
if cancelled { if cancelled {
break break
} }
cc.cond.Wait() cc.condWait()
} }
}() })
shutdownEnterWaitStateHook() shutdownEnterWaitStateHook()
select { select {
case <-done: case <-done:
@@ -1080,7 +1155,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
cc.mu.Lock() cc.mu.Lock()
// Free the goroutine above // Free the goroutine above
cancelled = true cancelled = true
cc.cond.Broadcast() cc.condBroadcast()
cc.mu.Unlock() cc.mu.Unlock()
return ctx.Err() return ctx.Err()
} }
@@ -1118,7 +1193,7 @@ func (cc *ClientConn) closeForError(err error) {
for _, cs := range cc.streams { for _, cs := range cc.streams {
cs.abortStreamLocked(err) cs.abortStreamLocked(err)
} }
cc.cond.Broadcast() cc.condBroadcast()
cc.mu.Unlock() cc.mu.Unlock()
cc.closeConn() cc.closeConn()
} }
@@ -1215,6 +1290,10 @@ func (cc *ClientConn) decrStreamReservationsLocked() {
} }
func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
return cc.roundTrip(req, nil)
}
func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) (*http.Response, error) {
ctx := req.Context() ctx := req.Context()
cs := &clientStream{ cs := &clientStream{
cc: cc, cc: cc,
@@ -1229,9 +1308,23 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
respHeaderRecv: make(chan struct{}), respHeaderRecv: make(chan struct{}),
donec: make(chan struct{}), donec: make(chan struct{}),
} }
go cs.doRequest(req) cc.goRun(func() {
cs.doRequest(req)
})
waitDone := func() error { waitDone := func() error {
if cc.syncHooks != nil {
cc.syncHooks.blockUntil(func() bool {
select {
case <-cs.donec:
case <-ctx.Done():
case <-cs.reqCancel:
default:
return false
}
return true
})
}
select { select {
case <-cs.donec: case <-cs.donec:
return nil return nil
@@ -1292,7 +1385,24 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
return err return err
} }
if streamf != nil {
streamf(cs)
}
for { for {
if cc.syncHooks != nil {
cc.syncHooks.blockUntil(func() bool {
select {
case <-cs.respHeaderRecv:
case <-cs.abort:
case <-ctx.Done():
case <-cs.reqCancel:
default:
return false
}
return true
})
}
select { select {
case <-cs.respHeaderRecv: case <-cs.respHeaderRecv:
return handleResponseHeaders() return handleResponseHeaders()
@@ -1348,6 +1458,21 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
if cc.reqHeaderMu == nil { if cc.reqHeaderMu == nil {
panic("RoundTrip on uninitialized ClientConn") // for tests panic("RoundTrip on uninitialized ClientConn") // for tests
} }
var newStreamHook func(*clientStream)
if cc.syncHooks != nil {
newStreamHook = cc.syncHooks.newstream
cc.syncHooks.blockUntil(func() bool {
select {
case cc.reqHeaderMu <- struct{}{}:
<-cc.reqHeaderMu
case <-cs.reqCancel:
case <-ctx.Done():
default:
return false
}
return true
})
}
select { select {
case cc.reqHeaderMu <- struct{}{}: case cc.reqHeaderMu <- struct{}{}:
case <-cs.reqCancel: case <-cs.reqCancel:
@@ -1372,6 +1497,10 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
} }
cc.mu.Unlock() cc.mu.Unlock()
if newStreamHook != nil {
newStreamHook(cs)
}
// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
if !cc.t.disableCompression() && if !cc.t.disableCompression() &&
req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Accept-Encoding") == "" &&
@@ -1452,15 +1581,30 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
var respHeaderTimer <-chan time.Time var respHeaderTimer <-chan time.Time
var respHeaderRecv chan struct{} var respHeaderRecv chan struct{}
if d := cc.responseHeaderTimeout(); d != 0 { if d := cc.responseHeaderTimeout(); d != 0 {
timer := time.NewTimer(d) timer := cc.newTimer(d)
defer timer.Stop() defer timer.Stop()
respHeaderTimer = timer.C respHeaderTimer = timer.C()
respHeaderRecv = cs.respHeaderRecv respHeaderRecv = cs.respHeaderRecv
} }
// Wait until the peer half-closes its end of the stream, // Wait until the peer half-closes its end of the stream,
// or until the request is aborted (via context, error, or otherwise), // or until the request is aborted (via context, error, or otherwise),
// whichever comes first. // whichever comes first.
for { for {
if cc.syncHooks != nil {
cc.syncHooks.blockUntil(func() bool {
select {
case <-cs.peerClosed:
case <-respHeaderTimer:
case <-respHeaderRecv:
case <-cs.abort:
case <-ctx.Done():
case <-cs.reqCancel:
default:
return false
}
return true
})
}
select { select {
case <-cs.peerClosed: case <-cs.peerClosed:
return nil return nil
@@ -1609,7 +1753,7 @@ func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error {
return nil return nil
} }
cc.pendingRequests++ cc.pendingRequests++
cc.cond.Wait() cc.condWait()
cc.pendingRequests-- cc.pendingRequests--
select { select {
case <-cs.abort: case <-cs.abort:
@@ -1871,10 +2015,26 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
cs.flow.take(take) cs.flow.take(take)
return take, nil return take, nil
} }
cc.cond.Wait() cc.condWait()
} }
} }
func validateHeaders(hdrs http.Header) string {
for k, vv := range hdrs {
if !httpguts.ValidHeaderFieldName(k) {
return fmt.Sprintf("name %q", k)
}
for _, v := range vv {
if !httpguts.ValidHeaderFieldValue(v) {
// Don't include the value in the error,
// because it may be sensitive.
return fmt.Sprintf("value for header %q", k)
}
}
}
return ""
}
var errNilRequestURL = errors.New("http2: Request.URI is nil") var errNilRequestURL = errors.New("http2: Request.URI is nil")
// requires cc.wmu be held. // requires cc.wmu be held.
@@ -1912,19 +2072,14 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
} }
} }
// Check for any invalid headers and return an error before we // Check for any invalid headers+trailers and return an error before we
// potentially pollute our hpack state. (We want to be able to // potentially pollute our hpack state. (We want to be able to
// continue to reuse the hpack encoder for future requests) // continue to reuse the hpack encoder for future requests)
for k, vv := range req.Header { if err := validateHeaders(req.Header); err != "" {
if !httpguts.ValidHeaderFieldName(k) { return nil, fmt.Errorf("invalid HTTP header %s", err)
return nil, fmt.Errorf("invalid HTTP header name %q", k) }
} if err := validateHeaders(req.Trailer); err != "" {
for _, v := range vv { return nil, fmt.Errorf("invalid HTTP trailer %s", err)
if !httpguts.ValidHeaderFieldValue(v) {
// Don't include the value in the error, because it may be sensitive.
return nil, fmt.Errorf("invalid HTTP header value for header %q", k)
}
}
} }
enumerateHeaders := func(f func(name, value string)) { enumerateHeaders := func(f func(name, value string)) {
@@ -2143,7 +2298,7 @@ func (cc *ClientConn) forgetStreamID(id uint32) {
} }
// Wake up writeRequestBody via clientStream.awaitFlowControl and // Wake up writeRequestBody via clientStream.awaitFlowControl and
// wake up RoundTrip if there is a pending request. // wake up RoundTrip if there is a pending request.
cc.cond.Broadcast() cc.condBroadcast()
closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil
if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 { if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 {
@@ -2231,7 +2386,7 @@ func (rl *clientConnReadLoop) cleanup() {
cs.abortStreamLocked(err) cs.abortStreamLocked(err)
} }
} }
cc.cond.Broadcast() cc.condBroadcast()
cc.mu.Unlock() cc.mu.Unlock()
} }
@@ -2266,10 +2421,9 @@ func (rl *clientConnReadLoop) run() error {
cc := rl.cc cc := rl.cc
gotSettings := false gotSettings := false
readIdleTimeout := cc.t.ReadIdleTimeout readIdleTimeout := cc.t.ReadIdleTimeout
var t *time.Timer var t timer
if readIdleTimeout != 0 { if readIdleTimeout != 0 {
t = time.AfterFunc(readIdleTimeout, cc.healthCheck) t = cc.afterFunc(readIdleTimeout, cc.healthCheck)
defer t.Stop()
} }
for { for {
f, err := cc.fr.ReadFrame() f, err := cc.fr.ReadFrame()
@@ -2684,7 +2838,7 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
}) })
return nil return nil
} }
if !cs.firstByte { if !cs.pastHeaders {
cc.logf("protocol error: received DATA before a HEADERS frame") cc.logf("protocol error: received DATA before a HEADERS frame")
rl.endStreamError(cs, StreamError{ rl.endStreamError(cs, StreamError{
StreamID: f.StreamID, StreamID: f.StreamID,
@@ -2867,7 +3021,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
for _, cs := range cc.streams { for _, cs := range cc.streams {
cs.flow.add(delta) cs.flow.add(delta)
} }
cc.cond.Broadcast() cc.condBroadcast()
cc.initialWindowSize = s.Val cc.initialWindowSize = s.Val
case SettingHeaderTableSize: case SettingHeaderTableSize:
@@ -2911,9 +3065,18 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error {
fl = &cs.flow fl = &cs.flow
} }
if !fl.add(int32(f.Increment)) { if !fl.add(int32(f.Increment)) {
// For stream, the sender sends RST_STREAM with an error code of FLOW_CONTROL_ERROR
if cs != nil {
rl.endStreamError(cs, StreamError{
StreamID: f.StreamID,
Code: ErrCodeFlowControl,
})
return nil
}
return ConnectionError(ErrCodeFlowControl) return ConnectionError(ErrCodeFlowControl)
} }
cc.cond.Broadcast() cc.condBroadcast()
return nil return nil
} }
@@ -2955,24 +3118,38 @@ func (cc *ClientConn) Ping(ctx context.Context) error {
} }
cc.mu.Unlock() cc.mu.Unlock()
} }
errc := make(chan error, 1) var pingError error
go func() { errc := make(chan struct{})
cc.goRun(func() {
cc.wmu.Lock() cc.wmu.Lock()
defer cc.wmu.Unlock() defer cc.wmu.Unlock()
if err := cc.fr.WritePing(false, p); err != nil { if pingError = cc.fr.WritePing(false, p); pingError != nil {
errc <- err close(errc)
return return
} }
if err := cc.bw.Flush(); err != nil { if pingError = cc.bw.Flush(); pingError != nil {
errc <- err close(errc)
return return
} }
}() })
if cc.syncHooks != nil {
cc.syncHooks.blockUntil(func() bool {
select {
case <-c:
case <-errc:
case <-ctx.Done():
case <-cc.readerDone:
default:
return false
}
return true
})
}
select { select {
case <-c: case <-c:
return nil return nil
case err := <-errc: case <-errc:
return err return pingError
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err() return ctx.Err()
case <-cc.readerDone: case <-cc.readerDone:
@@ -3141,9 +3318,17 @@ func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, err
} }
func (t *Transport) idleConnTimeout() time.Duration { func (t *Transport) idleConnTimeout() time.Duration {
// to keep things backwards compatible, we use non-zero values of
// IdleConnTimeout, followed by using the IdleConnTimeout on the underlying
// http1 transport, followed by 0
if t.IdleConnTimeout != 0 {
return t.IdleConnTimeout
}
if t.t1 != nil { if t.t1 != nil {
return t.t1.IdleConnTimeout return t.t1.IdleConnTimeout
} }
return 0 return 0
} }
@@ -3201,3 +3386,34 @@ func traceFirstResponseByte(trace *httptrace.ClientTrace) {
trace.GotFirstResponseByte() trace.GotFirstResponseByte()
} }
} }
func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool {
return trace != nil && trace.WroteHeaderField != nil
}
func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {
if trace != nil && trace.WroteHeaderField != nil {
trace.WroteHeaderField(k, []string{v})
}
}
func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
if trace != nil {
return trace.Got1xxResponse
}
return nil
}
// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS
// connection.
func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) {
dialer := &tls.Dialer{
Config: cfg,
}
cn, err := dialer.DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed
return tlsCn, nil
}
-1
View File
@@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.18 //go:build go1.18
// +build go1.18
package idna package idna
-1
View File
@@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.10 //go:build go1.10
// +build go1.10
// Package idna implements IDNA2008 using the compatibility processing // Package idna implements IDNA2008 using the compatibility processing
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to // defined by UTS (Unicode Technical Standard) #46, which defines a standard to
-1
View File
@@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.10 //go:build !go1.10
// +build !go1.10
// Package idna implements IDNA2008 using the compatibility processing // Package idna implements IDNA2008 using the compatibility processing
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to // defined by UTS (Unicode Technical Standard) #46, which defines a standard to
-1
View File
@@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.18 //go:build !go1.18
// +build !go1.18
package idna package idna
-1
View File
@@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build go1.10 && !go1.13 //go:build go1.10 && !go1.13
// +build go1.10,!go1.13
package idna package idna
-1
View File
@@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build go1.13 && !go1.14 //go:build go1.13 && !go1.14
// +build go1.13,!go1.14
package idna package idna
-1
View File
@@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build go1.14 && !go1.16 //go:build go1.14 && !go1.16
// +build go1.14,!go1.16
package idna package idna
-1
View File
@@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build go1.16 && !go1.21 //go:build go1.16 && !go1.21
// +build go1.16,!go1.21
package idna package idna
-1
View File
@@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build go1.21 //go:build go1.21
// +build go1.21
package idna package idna
-1
View File
@@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build !go1.10 //go:build !go1.10
// +build !go1.10
package idna package idna
-1
View File
@@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.16 //go:build !go1.16
// +build !go1.16
package idna package idna
-1
View File
@@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.16 //go:build go1.16
// +build go1.16
package idna package idna
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd
// +build aix darwin dragonfly freebsd netbsd openbsd
package socket package socket
-2
View File
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (arm || mips || mipsle || 386 || ppc) && linux //go:build (arm || mips || mipsle || 386 || ppc) && linux
// +build arm mips mipsle 386 ppc
// +build linux
package socket package socket
-2
View File
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux
// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x
// +build linux
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build amd64 && solaris //go:build amd64 && solaris
// +build amd64,solaris
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || windows || zos //go:build aix || windows || zos
// +build aix windows zos
package socket package socket
-1
View File
@@ -3,6 +3,5 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin && go1.12 //go:build darwin && go1.12
// +build darwin,go1.12
// This exists solely so we can linkname in symbols from syscall. // This exists solely so we can linkname in symbols from syscall.
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package socket package socket
-2
View File
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (arm || mips || mipsle || 386 || ppc) && (darwin || dragonfly || freebsd || linux || netbsd || openbsd) //go:build (arm || mips || mipsle || 386 || ppc) && (darwin || dragonfly || freebsd || linux || netbsd || openbsd)
// +build arm mips mipsle 386 ppc
// +build darwin dragonfly freebsd linux netbsd openbsd
package socket package socket
-2
View File
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || zos) //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || zos)
// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x
// +build aix darwin dragonfly freebsd linux netbsd openbsd zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build amd64 && solaris //go:build amd64 && solaris
// +build amd64,solaris
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !aix && !linux && !netbsd //go:build !aix && !linux && !netbsd
// +build !aix,!linux,!netbsd
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || linux || netbsd //go:build aix || linux || netbsd
// +build aix linux netbsd
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd
// +build aix darwin dragonfly freebsd netbsd openbsd
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || netbsd //go:build aix || darwin || dragonfly || freebsd || netbsd
// +build aix darwin dragonfly freebsd netbsd
package socket package socket
-2
View File
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (arm || mips || mipsle || 386 || ppc) && linux //go:build (arm || mips || mipsle || 386 || ppc) && linux
// +build arm mips mipsle 386 ppc
// +build linux
package socket package socket
-2
View File
@@ -3,8 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux
// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x
// +build linux
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build amd64 && solaris //go:build amd64 && solaris
// +build amd64,solaris
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build s390x && zos //go:build s390x && zos
// +build s390x,zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !race //go:build !race
// +build !race
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build race //go:build race
// +build race
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux //go:build linux
// +build linux
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !linux //go:build !linux
// +build !linux
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || openbsd || solaris //go:build aix || darwin || dragonfly || freebsd || openbsd || solaris
// +build aix darwin dragonfly freebsd openbsd solaris
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build linux && !s390x && !386 //go:build linux && !s390x && !386
// +build linux,!s390x,!386
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build loong64 //go:build loong64
// +build loong64
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build riscv64 //go:build riscv64
// +build riscv64
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// Added for go1.11 compatibility // Added for go1.11 compatibility
//go:build aix //go:build aix
// +build aix
package socket package socket
-1
View File
@@ -2,7 +2,6 @@
// cgo -godefs defs_linux.go // cgo -godefs defs_linux.go
//go:build loong64 //go:build loong64
// +build loong64
package socket package socket
-1
View File
@@ -2,7 +2,6 @@
// cgo -godefs defs_linux.go // cgo -godefs defs_linux.go
//go:build riscv64 //go:build riscv64
// +build riscv64
package socket package socket
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd
// +build aix darwin dragonfly freebsd netbsd openbsd
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin || linux || solaris //go:build darwin || linux || solaris
// +build darwin linux solaris
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !linux //go:build !linux
// +build !linux
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos
package ipv4 package ipv4
-1
View File
@@ -4,7 +4,6 @@
// Added for go1.11 compatibility // Added for go1.11 compatibility
//go:build aix //go:build aix
// +build aix
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || windows //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || windows
// +build aix darwin dragonfly freebsd netbsd openbsd solaris windows
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !aix && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !windows //go:build !aix && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !windows
// +build !aix,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!windows
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build darwin || freebsd || linux //go:build darwin || freebsd || linux
// +build darwin freebsd linux
package ipv4 package ipv4
-1
View File
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !darwin && !freebsd && !linux //go:build !darwin && !freebsd && !linux
// +build !darwin,!freebsd,!linux
package ipv4 package ipv4
Loaded 100 of 201 files, more files were not shown because too many files have changed in this diff. Show more