Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package fortio for openSUSE:Factory checked 
in at 2022-11-15 13:19:10
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/fortio (Old)
 and      /work/SRC/openSUSE:Factory/.fortio.new.1597 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "fortio"

Tue Nov 15 13:19:10 2022 rev:24 rq:1035795 version:1.38.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/fortio/fortio.changes    2022-10-26 
12:32:01.772342940 +0200
+++ /work/SRC/openSUSE:Factory/.fortio.new.1597/fortio.changes  2022-11-15 
13:21:50.916931824 +0100
@@ -1,0 +2,19 @@
+Tue Nov 15 09:21:44 UTC 2022 - ka...@b1-systems.de
+
+- Update to version 1.38.4:
+  * added test for #652, stepped on #653 while testing the test, added test 
for that too (#654)
+
+-------------------------------------------------------------------
+Tue Nov 15 09:19:08 UTC 2022 - ka...@b1-systems.de
+
+- Update to version 1.38.3:
+  * support User-Agent change and deletion from -H, simplify UI for headers 
(#649)
+  * codeql v2 and a lot of github actions update, sha pinning in dockerfile 
etc (#647)
+  * Bump docker/setup-buildx-action from 1.7.0 to 2.2.1 (#646)
+  * Bump docker/setup-qemu-action from 1.2.0 to 2.1.0 (#645)
+  * Bump docker/login-action from 1.10.0 to 2.1.0 (#644)
+  * Bump docker/build-push-action from 2.5.0 to 3.2.0 (#643)
+  * bump build image (go 1.19.3), added govulncheck, fix permissions in /go - 
also stop using /go/src (#641)
+  * README formatting and links correction (#636)
+
+-------------------------------------------------------------------

Old:
----
  fortio-1.38.2.tar.gz

New:
----
  fortio-1.38.4.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ fortio.spec ++++++
--- /var/tmp/diff_new_pack.8lgUdC/_old  2022-11-15 13:21:51.468934673 +0100
+++ /var/tmp/diff_new_pack.8lgUdC/_new  2022-11-15 13:21:51.476934714 +0100
@@ -19,7 +19,7 @@
 %define __arch_install_post export NO_BRP_STRIP_DEBUG=true
 
 Name:           fortio
-Version:        1.38.2
+Version:        1.38.4
 Release:        0
 Summary:        Load testing library, command line tool, advanced echo server 
and web UI
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.8lgUdC/_old  2022-11-15 13:21:51.508934880 +0100
+++ /var/tmp/diff_new_pack.8lgUdC/_new  2022-11-15 13:21:51.512934900 +0100
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/fortio/fortio</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v1.38.2</param>
+    <param name="revision">v1.38.4</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="changesgenerate">enable</param>
     <param name="versionrewrite-pattern">v(.*)</param>
@@ -17,7 +17,7 @@
     <param name="compression">gz</param>
   </service>
   <service name="go_modules" mode="disabled">
-    <param name="archive">fortio-1.38.2.tar.gz</param>
+    <param name="archive">fortio-1.38.4.tar.gz</param>
   </service>
 </services>
 

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.8lgUdC/_old  2022-11-15 13:21:51.532935004 +0100
+++ /var/tmp/diff_new_pack.8lgUdC/_new  2022-11-15 13:21:51.536935024 +0100
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/fortio/fortio</param>
-              <param 
name="changesrevision">d2a2d42f4f17df4c09e608353546ebd0d443747d</param></service></servicedata>
+              <param 
name="changesrevision">860b2f915597a1c48e6d74e6140ff94998093593</param></service></servicedata>
 (No newline at EOF)
 

++++++ fortio-1.38.2.tar.gz -> fortio-1.38.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/.circleci/config.yml 
new/fortio-1.38.4/.circleci/config.yml
--- old/fortio-1.38.2/.circleci/config.yml      2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/.circleci/config.yml      2022-11-13 00:06:32.000000000 
+0100
@@ -8,8 +8,8 @@
   &defaultEnv
   docker:
     # specify the version
-    - image: docker.io/fortio/fortio.build:v47
-  working_directory: /go/src/fortio.org/fortio
+    - image: 
docker.io/fortio/fortio.build:v50@sha256:fe69c193d8ad40eb0d791984881f3678aead02660b8e3468c757f717892ada4c
+  working_directory: /build/fortio
 
 jobs:
   unit-tests:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/.github/dependabot.yml 
new/fortio-1.38.4/.github/dependabot.yml
--- old/fortio-1.38.2/.github/dependabot.yml    2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/.github/dependabot.yml    2022-11-13 00:06:32.000000000 
+0100
@@ -4,3 +4,7 @@
     directory: /
     schedule:
       interval: daily
+  - package-ecosystem: "github-actions"
+    directory: "/"
+    schedule:
+      interval: "weekly"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/.github/workflows/codeql-analysis.yml 
new/fortio-1.38.4/.github/workflows/codeql-analysis.yml
--- old/fortio-1.38.2/.github/workflows/codeql-analysis.yml     2022-10-24 
22:53:20.000000000 +0200
+++ new/fortio-1.38.4/.github/workflows/codeql-analysis.yml     2022-11-13 
00:06:32.000000000 +0100
@@ -40,11 +40,11 @@
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e # 
pin@v2
+        uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 
pin@v3
 
       # Initializes the CodeQL tools for scanning.
       - name: Initialize CodeQL
-        uses: 
github/codeql-action/init@a6611b86918424d4588efe7d6dbe18fe52d42518 # pin@v1
+        uses: 
github/codeql-action/init@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # pin@v2
         with:
           languages: ${{ matrix.language }}
           # If you wish to specify custom queries, you can do so here or in a 
config file.
@@ -52,10 +52,10 @@
           # Prefix the list here with "+" to use these queries and those in 
the config file.
           # queries: ./path/to/local/query, your-org/your-repo/queries@main
 
-      # Autobuild attempts to build any compiled languages  (C/C++, C#, or 
Java).
-      # If this step fails, then you should remove it and run the build 
manually (see below)
+          # Autobuild attempts to build any compiled languages  (C/C++, C#, or 
Java).
+          # If this step fails, then you should remove it and run the build 
manually (see below)
       - name: Autobuild
-        uses: 
github/codeql-action/autobuild@a6611b86918424d4588efe7d6dbe18fe52d42518 # pin@v1
+        uses: 
github/codeql-action/autobuild@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # pin@v2
 
       # ℹ️ Command-line programs to run using the OS shell.
       # 📚 https://git.io/JvXDl
@@ -66,4 +66,4 @@
       #   make bootstrap
       #   make release
       - name: Perform CodeQL Analysis
-        uses: 
github/codeql-action/analyze@a6611b86918424d4588efe7d6dbe18fe52d42518 # pin@v1
+        uses: 
github/codeql-action/analyze@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # pin@v2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/.github/workflows/main.yml 
new/fortio-1.38.4/.github/workflows/main.yml
--- old/fortio-1.38.2/.github/workflows/main.yml        2022-10-24 
22:53:20.000000000 +0200
+++ new/fortio-1.38.4/.github/workflows/main.yml        2022-11-13 
00:06:32.000000000 +0100
@@ -22,20 +22,20 @@
     # Steps represent a sequence of tasks that will be executed as part of the 
job
     steps:
       # Checks-out your repository under $GITHUB_WORKSPACE, so your job can 
access it
-      - uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e # 
pin@v2
+      - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 
pin@v3
 
       - name: Set up QEMU
-        uses: 
docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # pin@v1
+        uses: 
docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # pin@v2
 
       - name: Set up Docker Buildx
         id: buildx
-        uses: 
docker/setup-buildx-action@f211e3e9ded2d9377c8cadc4489a4e38014bc4c9 # pin@v1
+        uses: 
docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # pin@v2
 
       - name: Available platforms
         run: echo ${{ steps.buildx.outputs.platforms }}
 
       - name: Log in to Docker Hub
-        uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 # 
pin@f054a8b539a109f9f41c372932f1ae047eff08c9
+        uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # 
pin@v2
         with:
           username: ${{ secrets.DOCKER_USER }}
           password: ${{ secrets.DOCKER_TOKEN }}
@@ -46,27 +46,27 @@
           make info
           make release
           VERSION=$(make echo-version)
-          echo ::set-output name=VERSION::${VERSION}
+          echo "VERSION=${VERSION}" >> $GITHUB_ENV
           PACKAGE_VERSION=$(make echo-package-version)
-          echo ::set-output name=PACKAGE_VERSION::${PACKAGE_VERSION}
           echo "Version $VERSION, Package version $PACKAGE_VERSION"
 
       - name: Build and push Docker image
-        uses: 
docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc # 
pin@ad44023a93711e3deb337508980b4b5e9bcdc5dc
+        uses: 
docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 # pin@v3
         with:
           context: .
           platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x
           push: true
-          tags: fortio/fortio:${{ steps.build.outputs.VERSION }}, 
fortio/fortio:latest
+          tags: fortio/fortio:${{ env.VERSION }}, fortio/fortio:latest
 
       - name: Create Release
         id: create_release
+        # Need to find a replacement not using set-output
         uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e 
# pin@v1
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided 
by Actions, you do not need to create your own token
         with:
           tag_name: ${{ github.ref }}
-          release_name: Fortio ${{ steps.build.outputs.VERSION }}
+          release_name: Fortio ${{ env.VERSION }}
           draft: true
 
       - name: Upload release artifacts
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/.github/workflows/manual-build.yml 
new/fortio-1.38.4/.github/workflows/manual-build.yml
--- old/fortio-1.38.2/.github/workflows/manual-build.yml        2022-10-24 
22:53:20.000000000 +0200
+++ new/fortio-1.38.4/.github/workflows/manual-build.yml        2022-11-13 
00:06:32.000000000 +0100
@@ -21,16 +21,16 @@
         run: |
           echo "tag is ${{ github.event.inputs.tag }}"
 
-      - uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e # 
pin@v2
+      - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 
pin@v3
         with:
           ref: ${{ github.event.inputs.tag }}
 
       - name: Set up QEMU
-        uses: 
docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # pin@v1
+        uses: 
docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # pin@v2
 
       - name: Set up Docker Buildx
         id: buildx
-        uses: 
docker/setup-buildx-action@f211e3e9ded2d9377c8cadc4489a4e38014bc4c9 # pin@v1
+        uses: 
docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # pin@v2
 
       - name: Build
         id: build
@@ -38,9 +38,7 @@
           make info
           make release
           VERSION=$(make echo-version)
-          echo ::set-output name=VERSION::${VERSION}
           PACKAGE_VERSION=$(make echo-package-version)
-          echo ::set-output name=PACKAGE_VERSION::${PACKAGE_VERSION}
           echo "Version $VERSION, Package version $PACKAGE_VERSION"
 
       - name: Upload release artifacts
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/Dockerfile new/fortio-1.38.4/Dockerfile
--- old/fortio-1.38.2/Dockerfile        2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/Dockerfile        2022-11-13 00:06:32.000000000 +0100
@@ -1,6 +1,6 @@
 # Build the binaries in larger image
-FROM docker.io/fortio/fortio.build:v47 as build
-WORKDIR /go/src/fortio.org
+FROM 
docker.io/fortio/fortio.build:v50@sha256:fe69c193d8ad40eb0d791984881f3678aead02660b8e3468c757f717892ada4c
 as build
+WORKDIR /build
 COPY . fortio
 ARG MODE=install
 # We moved a lot of the logic into the Makefile so it can be reused in brew
@@ -12,8 +12,8 @@
 # NOTE: the list of files here, if updated, must be changed in 
release/Dockerfile.in too
 COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
 # TODO: get rid of *.bak, *~ and other spurious non source files
-#COPY --from=build /go/src/fortio.org/fortio/ui/static /usr/share/fortio/static
-#COPY --from=build /go/src/fortio.org/fortio/ui/templates 
/usr/share/fortio/templates
+#COPY --from=build /build/fortio/ui/static /usr/share/fortio/static
+#COPY --from=build /build/fortio/ui/templates /usr/share/fortio/templates
 COPY --from=build /build/result/fortio /usr/bin/fortio
 EXPOSE 8078
 EXPOSE 8079
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/Dockerfile.build 
new/fortio-1.38.4/Dockerfile.build
--- old/fortio-1.38.2/Dockerfile.build  2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/Dockerfile.build  2022-11-13 00:06:32.000000000 +0100
@@ -1,5 +1,5 @@
 # Dependencies and linters for build:
-FROM golang:1.19.2
+FROM golang:1.19.3
 # Need gcc for -race test (and some linters though those work with 
CGO_ENABLED=0)
 RUN apt-get -y update && \
   apt-get --no-install-recommends -y upgrade && \
@@ -21,9 +21,12 @@
     apt-get -y update && apt-get install --no-install-recommends -y docker-ce; 
\
     fi
 
+# govulncheck
+RUN go install golang.org/x/vuln/cmd/govulncheck@latest
 WORKDIR /build
 COPY .golangci.yml .
 VOLUME /build
 RUN useradd -m build -d /build
 RUN chown -R build:build /build
+RUN chown -R build:build /go
 USER build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/Dockerfile.echosrv 
new/fortio-1.38.4/Dockerfile.echosrv
--- old/fortio-1.38.2/Dockerfile.echosrv        2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/Dockerfile.echosrv        2022-11-13 00:06:32.000000000 
+0100
@@ -1,10 +1,10 @@
 # Build the binaries in larger image
-FROM docker.io/fortio/fortio.build:v47 as build
-WORKDIR /go/src/fortio.org
+FROM 
docker.io/fortio/fortio.build:v50@sha256:fe69c193d8ad40eb0d791984881f3678aead02660b8e3468c757f717892ada4c
 as build
+WORKDIR /build
 COPY . fortio
-RUN make -C fortio official-build-version BUILD_DIR=/build 
OFFICIAL_TARGET=fortio.org/fortio/echosrv OFFICIAL_BIN=../echosrv.bin
+RUN make -C fortio official-build-version BUILD_DIR=/build 
OFFICIAL_TARGET=fortio.org/fortio/echosrv
 # Minimal image with just the binary
 FROM scratch
-COPY --from=build /go/src/fortio.org/echosrv.bin /usr/bin/echosrv
+COPY --from=build /build/result/echosrv /usr/bin/echosrv
 EXPOSE 8080
 ENTRYPOINT ["/usr/bin/echosrv"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/Dockerfile.fcurl 
new/fortio-1.38.4/Dockerfile.fcurl
--- old/fortio-1.38.2/Dockerfile.fcurl  2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/Dockerfile.fcurl  2022-11-13 00:06:32.000000000 +0100
@@ -1,11 +1,10 @@
 # Build the binaries in larger image
-FROM docker.io/fortio/fortio.build:v47 as build
-WORKDIR /go/src/fortio.org
+FROM 
docker.io/fortio/fortio.build:v50@sha256:fe69c193d8ad40eb0d791984881f3678aead02660b8e3468c757f717892ada4c
 as build
+WORKDIR /build
 COPY . fortio
-# fcurl should not need vendor/no dependencies
-RUN make -C fortio official-build-version BUILD_DIR=/build 
OFFICIAL_TARGET=fortio.org/fortio/fcurl OFFICIAL_BIN=../fcurl.bin
+RUN make -C fortio official-build-version BUILD_DIR=/build 
OFFICIAL_TARGET=fortio.org/fortio/fcurl
 # Minimal image with just the binary and certs
 FROM scratch
 COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
-COPY --from=build /go/src/fortio.org/fcurl.bin /usr/bin/fcurl
+COPY --from=build /build/result/fcurl /usr/bin/fcurl
 ENTRYPOINT ["/usr/bin/fcurl"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/Makefile new/fortio-1.38.4/Makefile
--- old/fortio-1.38.2/Makefile  2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/Makefile  2022-11-13 00:06:32.000000000 +0100
@@ -7,7 +7,7 @@
 IMAGES=echosrv fcurl # plus the combo image / Dockerfile without ext.
 
 DOCKER_PREFIX := docker.io/fortio/fortio
-BUILD_IMAGE_TAG := v47
+BUILD_IMAGE_TAG := 
v50@sha256:fe69c193d8ad40eb0d791984881f3678aead02660b8e3468c757f717892ada4c
 BUILDX_PLATFORMS := linux/amd64,linux/arm64,linux/ppc64le,linux/s390x
 BUILDX_POSTFIX :=
 ifeq '$(shell echo $(BUILDX_PLATFORMS) | awk -F "," "{print NF-1}")' '0'
@@ -57,23 +57,24 @@
 # DEBUG_LINTERS="--debug"
 
 local-lint:
+       govulncheck $(LINT_PACKAGES)
        golangci-lint version
        golangci-lint --timeout 120s $(DEBUG_LINTERS) run $(LINT_PACKAGES)
 
 # Lint everything by default but ok to "make lint LINT_PACKAGES=./fhttp"
 LINT_PACKAGES:=./...
 lint:
-       docker run -v $(CURDIR):/go/src/fortio.org/fortio $(BUILD_IMAGE) bash 
-c \
-               "cd /go/src/fortio.org/fortio \
+       docker run -v $(CURDIR):/build/fortio $(BUILD_IMAGE) bash -c \
+               "cd /build/fortio \
                && time make local-lint DEBUG_LINTERS=\"$(DEBUG_LINTERS)\" 
LINT_PACKAGES=\"$(LINT_PACKAGES)\""
 
 docker-test:
-       docker run -v $(CURDIR):/go/src/fortio.org/fortio $(BUILD_IMAGE) bash 
-c \
-               "cd /go/src/fortio.org/fortio \
+       docker run -v $(CURDIR):/build/fortio $(BUILD_IMAGE) bash -c \
+               "cd /build/fortio \
                && time make test"
 
 shell:
-       docker run -ti -v $(CURDIR):/go/src/fortio.org/fortio $(BUILD_IMAGE)
+       docker run -ti -v $(CURDIR):/build/fortio $(BUILD_IMAGE)
 
 # This really also tests the release process and build on windows,mac,linux
 # and the docker images, not just "web" (ui) stuff that it also exercises.
@@ -108,10 +109,14 @@
        docker buildx create --use
        $(MAKE) docker-push-internal IMAGE=.build TAG=$(BUILD_IMAGE_TAG)
 
+# Get the sha (use after newly building a new build image) to put it back in 
BUILD_IMAGE_TAG
+build-image-sha:
+       docker inspect $(BUILD_IMAGE) | jq -r '.[0].RepoDigests[0]' | sed -e 
"s/^.*@/$(BUILD_IMAGE_TAG)@/"
+
 SED:=sed
 update-build-image-tag:
        @echo 'Need to use gnu sed (brew install gnu-sed; make 
update-build-image-tag SED=gsed)'
-       $(SED) --in-place=.bak -e 
's!$(DOCKER_PREFIX).build:v..!$(BUILD_IMAGE)!g' $(FILES_WITH_IMAGE)
+       $(SED) --in-place=.bak -E -e 's!$(DOCKER_PREFIX).build:v[^ 
]+!$(BUILD_IMAGE)!g' $(FILES_WITH_IMAGE)
 
 docker-default-platform:
        @docker buildx --builder default inspect | tail -1 | sed -e 
"s/Platforms: //" -e "s/,//g" | awk '{print $$1}'
@@ -135,7 +140,7 @@
 
 .PHONY: all docker-internal docker-push-internal docker-version test 
dependencies
 
-.PHONY: go-install lint install-linters coverage webtest release-test 
update-build-image
+.PHONY: go-install lint install-linters coverage webtest release-test 
update-build-image build-image-sha
 
 .PHONY: local-lint update-build-image-tag release pull certs certs-clean
 
@@ -143,7 +148,8 @@
 BUILD_DIR := /tmp/fortio_build
 BUILD_DIR_ABS := $(abspath $(BUILD_DIR))
 BUILD_DIR_BIN := $(BUILD_DIR_ABS)/bin
-OFFICIAL_BIN ?= $(BUILD_DIR)/result/fortio
+OFFICIAL_EXE ?= $(notdir $(OFFICIAL_TARGET))
+OFFICIAL_BIN ?= $(BUILD_DIR)/result/$(OFFICIAL_EXE)
 OFFICIAL_DIR ?= $(dir $(OFFICIAL_BIN))
 
 GOOS :=
@@ -177,14 +183,16 @@
 official-build: official-build-internal
 
 official-build-internal: $(BUILD_DIR) $(OFFICIAL_DIR)
+       @echo "Building OFFICIAL_EXE=$(OFFICIAL_EXE) BUILD_DIR=$(BUILD_DIR) 
BUILD_DIR_BIN=$(BUILD_DIR_BIN) MODE=$(MODE)"
+       @echo "OFFICIAL_BIN=$(OFFICIAL_BIN) OFFICIAL_DIR=$(OFFICIAL_DIR) 
OFFICIAL_TARGET=$(OFFICIAL_TARGET)"
        $(GO_BIN) version
 ifeq ($(MODE),install)
        GOPATH=$(BUILD_DIR_ABS) CGO_ENABLED=0 GOOS=$(GOOS) $(GO_BIN) install -a 
-ldflags -s $(OFFICIAL_TARGET)@v$(DIST_VERSION)
        # rename when building cross architecture (on windows it has .exe 
suffix thus the *)
        ls -lR $(BUILD_DIR_BIN)
-       -mv -f $(BUILD_DIR_BIN)/*_*/fortio* $(BUILD_DIR_BIN)
+       -mv -f $(BUILD_DIR_BIN)/*_*/$(OFFICIAL_EXE)* $(BUILD_DIR_BIN)
        -rmdir $(BUILD_DIR_BIN)/*_*
-       mv -f $(BUILD_DIR_BIN)/fortio* $(OFFICIAL_DIR)
+       mv -f $(BUILD_DIR_BIN)/$(OFFICIAL_EXE)* $(OFFICIAL_DIR)
 else
        CGO_ENABLED=0 GOOS=$(GOOS) $(GO_BIN) build -a -ldflags -s -o 
$(OFFICIAL_BIN) $(OFFICIAL_TARGET)
 endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/README.md new/fortio-1.38.4/README.md
--- old/fortio-1.38.2/README.md 2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/README.md 2022-11-13 00:06:32.000000000 +0100
@@ -11,7 +11,7 @@
 
 Fortio (Φορτίο) started as, and is, [Istio](https://istio.io/)'s load 
testing tool and later (2018) graduated to be its own project.
 
-Fortio is also used by, among others, 
[Meshery](https://docs.meshery.io/extensibility/load-generators)
+Fortio is also used by, among others, 
[Meshery](https://docs.meshery.io/extensibility/load-generators).
 
 Fortio runs at a specified query per second (qps) and records an histogram of 
execution time
 and calculates percentiles (e.g. p99 ie the response time such as 99% of the 
requests take less than that number (in seconds, SI unit)).
@@ -31,11 +31,11 @@
 As well as the newly integrated [Dynamic Flags](dflag/) support (greatly 
inspired/imported initially from https://github.com/mwitkow/go-flagz but 
recently reimplemented using Go generics).
 Even more recent is the new `jrpc` JSON Remote Procedure Calls library package 
([docs](https://pkg.go.dev/fortio.org/fortio/jrpc)).
 
-If you want to connect to fortio using https and fortio to provide real TLS 
certificates, or to multiplex grpc and regular http behind a single port, check 
out [Fortio Proxy](https://github.com/fortio/proxy#fortio-proxy)
+If you want to connect to fortio using https and fortio to provide real TLS 
certificates, or to multiplex grpc and regular http behind a single port, check 
out [Fortio Proxy](https://github.com/fortio/proxy#fortio-proxy).
 
 ## Installation
 
-We publish a multi architecture docker image (linux/amd64, linux/arm64, 
linux/ppc64le, linux/s390x) `fortio/fortio`
+We publish a multi architecture docker image (linux/amd64, linux/arm64, 
linux/ppc64le, linux/s390x) `fortio/fortio`.
 
 For instance:
 ```shell
@@ -49,16 +49,16 @@
 2. `go install fortio.org/fortio@latest`
 3. you can now run `fortio` (from your gopath bin/ directory, usually 
`~/go/bin`)
 
-The [releases](https://github.com/fortio/fortio/releases) page has binaries 
for many OS/architecture combinations (see assets).
+The [releases](https://github.com/fortio/fortio/releases) page has binaries 
for many OS/architecture combinations (see assets):
 
 ```shell
-curl -L 
https://github.com/fortio/fortio/releases/download/v1.38.2/fortio-linux_amd64-1.38.2.tgz
 \
+curl -L 
https://github.com/fortio/fortio/releases/download/v1.38.4/fortio-linux_amd64-1.38.4.tgz
 \
  | sudo tar -C / -xvzpf -
 # or the debian package
-wget 
https://github.com/fortio/fortio/releases/download/v1.38.2/fortio_1.38.2_amd64.deb
-dpkg -i fortio_1.38.2_amd64.deb
+wget 
https://github.com/fortio/fortio/releases/download/v1.38.4/fortio_1.38.4_amd64.deb
+dpkg -i fortio_1.38.4_amd64.deb
 # or the rpm
-rpm -i 
https://github.com/fortio/fortio/releases/download/v1.38.2/fortio-1.38.2-1.x86_64.rpm
+rpm -i 
https://github.com/fortio/fortio/releases/download/v1.38.4/fortio-1.38.4-1.x86_64.rpm
 # and more, see assets in release page
 ```
 
@@ -68,7 +68,7 @@
 brew install fortio
 ```
 
-On Windows, download 
https://github.com/fortio/fortio/releases/download/v1.38.2/fortio_win_1.38.2.zip
 and extract `fortio.exe` to any location, then using the Windows Command 
Prompt:
+On Windows, download 
https://github.com/fortio/fortio/releases/download/v1.38.4/fortio_win_1.38.4.zip
 and extract `fortio.exe` to any location, then using the Windows Command 
Prompt:
 ```
 fortio.exe server
 ```
@@ -76,7 +76,7 @@
 
 Once `fortio server` is running, you can visit its web UI at 
[http://localhost:8080/fortio/](http://localhost:8080/fortio/)
 
-You can get a preview of the reporting/graphing UI at 
[https://demo.fortio.org/](https://demo.fortio.org/).
+You can get a preview of the reporting/graphing UI at 
[https://demo.fortio.org](https://demo.fortio.org)
 <!--
 and on 
[istio.io/docs/performance-and-scalability/synthetic-benchmarks/](https://istio.io/docs/performance-and-scalability/synthetic-benchmarks/)
 -->
@@ -116,15 +116,17 @@
 <details>
 <!-- use release/updateFlags.sh to update this section -->
 <pre>
-Φορτίο 1.38.2 usage:
+Φορτίο 1.38.4 usage:
     fortio command [flags] target
 where command is one of: load (load testing), server (starts ui, rest api,
- http-echo, redirect, proxies, tcp-echo and grpc ping servers), tcp-echo (only
- the tcp-echo server), report (report only UI server), redirect (only the
- redirect server), proxies (only the -M and -P configured proxies), grpcping
- (grpc client), or curl (single URL debug), or nc (single tcp or udp://
- connection), or version (prints the full version and build details).
-where target is a url (http load tests) or host:port (grpc health test).
+ http-echo, redirect, proxies, tcp-echo, udp-echo and grpc ping servers),
+ tcp-echo (only the tcp-echo server), udp-echo (only udp-echo server),
+ report (report only UI server), redirect (only the redirect server),
+ proxies (only the -M and -P configured proxies), grpcping (grpc client),
+ or curl (single URL debug), or nc (single tcp or udp:// connection),
+ or version (prints the full version and build details).
+where target is a url (http load tests) or host:port (grpc health test),
+ or tcp://host:port (tcp load test), or udp://host:port (udp load test).
 flags are:
   -H header
         Additional header(s)
@@ -341,7 +343,7 @@
 </pre>
 </details>
 
-See also the FAQ entry about [fortio flags for best 
results](https://github.com/fortio/fortio/wiki/FAQ#i-want-to-get-the-best-results-what-flags-should-i-pass)
+See also the FAQ entry about [fortio flags for best 
results](https://github.com/fortio/fortio/wiki/FAQ#i-want-to-get-the-best-results-what-flags-should-i-pass).
 
 ## Server URLs and features
 
@@ -370,10 +372,10 @@
 * `/fortio/` A UI to
   * Run/Trigger tests and graph the results.
   * A UI to browse saved results and single graph or multi graph them 
(comparative graph of min,avg, median, p75, p99, p99.9 and max).
-  * Proxy/fetch other URLs
+  * Proxy/fetch other URLs.
   * `/fortio/data/index.tsv` an tab separated value file conforming to Google 
cloud storage [URL list data transfer 
format](https://cloud.google.com/storage/transfer/create-url-list) so you can 
export/backup local results to the cloud.
-  * Download/sync peer to peer JSON results files from other Fortio servers 
(using their `index.tsv` URLs)
-  * Download/sync from an Amazon S3 or Google Cloud compatible bucket listings 
[XML URLs](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html)
+  * Download/sync peer to peer JSON results files from other Fortio servers 
(using their `index.tsv` URLs).
+  * Download/sync from an Amazon S3 or Google Cloud compatible bucket listings 
[XML URLs](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html).
 
 * API to trigger and cancel runs from the running server (like the form ui but 
more directly and with `async=on` option)
   * `/fortio/rest/run` starts a run; the arguments are either from the command 
line or from POSTed JSON; `jsonPath` can be provided to look for in a subset of 
the json object, for instance `jsonPath=metadata` allows to use the flagger 
webhook meta data for fortio run parameters (see [Remote Triggered load test 
section below](#remote-triggered-load-test-server-mode-rest-api)).
@@ -406,7 +408,7 @@
 
 ### Sample of the graphing UI
 
-With the 2 histograms - total and errors overlayed
+With the 2 histograms - total and errors overlayed:
 
 ![Graphical 
result](https://user-images.githubusercontent.com/3664595/165001248-33e180d5-fd6b-4389-b73e-79a21e76d5b0.png)
 
@@ -457,7 +459,7 @@
 ```
 
 ### TCP
-Start the echo-server alone and run a load (use `tcp://` prefix for the load 
test to be for tcp echo server)
+Start the echo-server alone and run a load (use `tcp://` prefix for the load 
test to be for tcp echo server):
 ```Shell
 $ fortio tcp-echo &
 Fortio X.Y.Z tcp-echo TCP server listening on [::]:8078
@@ -486,7 +488,7 @@
 ```
 
 ### UDP
-Start the udp-echo server alone and run a load (use `tcp://` prefix for the 
load test to be for tcp echo server)
+Start the udp-echo server alone and run a load (use `udp://` prefix for the 
load test to be for udp echo server):
 ```
 $ fortio udp-echo &
 Fortio X.Y.Z udp-echo UDP server listening on [::]:8078
@@ -568,7 +570,7 @@
 * First, start Fortio server with the `-cert` and `-key` flags:
 
 `/path/to/fortio/server.crt` and `/path/to/fortio/server.key` are paths to the 
TLS certificate and key that
-you must provide.
+you must provide:
 
 ```Shell
 $ fortio server -cert /path/to/fortio/server.crt -key 
/path/to/fortio/server.key
@@ -655,10 +657,10 @@
 ### Remote triggered load test (server mode rest API)
 
 New since 1.18 the server has a `fortio/rest/run` endpoint similar to what the 
form UI submit in `fortio/` to start a run.
-  - plus `async` query arg or json value `"on"` will make the run asynchronous 
(returns just the runid of the run instead of waiting for the result)
-  - plus read all the run configuration from either query args or jsonPath 
POSTed info
-  - compatible with [flagger](https://github.com/fluxcd/flagger) and other 
webhooks
-  - New in 1.22: use `headers` json array to send headers (or multiple `&H=` 
query args)
+  - plus `async` query arg or json value `"on"` will make the run asynchronous 
(returns just the runid of the run instead of waiting for the result);
+  - plus read all the run configuration from either query args or jsonPath 
POSTed info;
+  - compatible with [flagger](https://github.com/fluxcd/flagger) and other 
webhooks;
+  - New in 1.22: use `headers` json array to send headers (or multiple `&H=` 
query args).
 
 Examples:
 
@@ -710,7 +712,7 @@
 foo
 ```
 
-and you get in result.json
+and you get in result.json:
 ```json
 {
   "RunType": "HTTP",
@@ -828,7 +830,7 @@
 }
 ```
 
-- There is also the `fortio/rest/stop` endpoint to stop a run by its id or all 
runs if not specified
+- There is also the `fortio/rest/stop` endpoint to stop a run by its id or all 
runs if not specified.
 
 
 ### GRPC load test
@@ -956,6 +958,8 @@
 
 ```
 
+Note: if you do not want the default fortio User-Agent to be sent pass `-H 
user-agent:`. If you want to send a present yet empty User-Agent: header, pass 
`-H "user-agent: "` (ie only whitespace sends empty one, empty value doesn't 
send any).
+
 ### Report only UI
 
 If you have json files saved from running the full UI or downloaded, using the 
`-sync` option, from an amazon or google cloud storage bucket or from a peer 
fortio server (to synchronize from a peer fortio, use 
`http://`_peer_`:8080/data/index.tsv` as the sync URL). You can then serve just 
the reports:
@@ -977,7 +981,7 @@
 Fortio X.Y.Z Multi on 5554 server listening on [::]:5554
 10:09:56 I http_forwarder.go:152> Multi-server on [::]:5554 running with 
&{Targets:[{Destination:http://localhost:8080 MirrorOrigin:true} 
{Destination:http://localhost:8080 MirrorOrigin:true}] Name:Multi on [::]:5554 
client:0xc0001ccc00}
 ```
-Call the debug endpoint on both
+Call the debug endpoint on both:
 ```Shell
 # in new window
 $ fortio curl -payload "a test" http://localhost:5554/debug
@@ -1022,7 +1026,7 @@
 There are 2 flags to further control the behaviour of the multi server proxies:
 
 - pass `-mirrorOriginFlag=false` to not mirror all headers and request type to 
targets.
-- pass `-multi-serial-mode` to stream request response serially instead of 
fetching in parallel and writing combined data after completion
+- pass `-multi-serial-mode` to stream request response serially instead of 
fetching in parallel and writing combined data after completion.
 
 Also remember you can pass multiple `-M`.
 
@@ -1045,14 +1049,14 @@
 
 ## Implementation details
 
-Fortio is written in the [Go](https://golang.org) language and includes a 
scalable semi log histogram in [stats.go](stats/stats.go) and a periodic runner 
engine in [periodic.go](periodic/periodic.go) with specializations for 
[http](http/httprunner.go) and [grpc](fortiogrpc/grpcrunner.go).
-The [http/](http/) package includes a very high performance specialized http 
1.1 client.
+Fortio is written in the [Go](https://golang.org) language and includes a 
scalable semi log histogram in [stats.go](stats/stats.go) and a periodic runner 
engine in [periodic.go](periodic/periodic.go) with specializations for 
[http](fhttp/httprunner.go) and [grpc](fgrpc/grpcrunner.go).
+The [fhttp/](fhttp/) package includes a very high performance specialized http 
1.1 client.
 You may find fortio's [logger](log/logger.go) useful as well.
 
 You can run the histogram code standalone as a command line in 
[histogram/](histogram/), a basic echo http server in [echosrv/](echosrv/), or 
both the http echo and GRPC ping server through `fortio server`, the fortio 
command line interface lives in this top level directory 
[fortio_main.go](fortio_main.go)
 
 There is also [fcurl/](fcurl/) which is the `fortio curl` part of the code (if 
you need a light http client without grpc or server side).
-A matching tiny (2Mb compressed) docker image is 
[fortio/fortio.fcurl](https://hub.docker.com/r/fortio/fortio.fcurl/tags/)
+A matching tiny (2Mb compressed) docker image is 
[fortio/fortio.fcurl](https://hub.docker.com/r/fortio/fortio.fcurl/tags/).
 
 ## More examples
 
@@ -1117,7 +1121,7 @@
 Response Body Sizes : count 300000 avg 0 +/- 0 min 0 max 0 sum 0
 </pre></details>
 
-Or you can get the data in [JSON 
format](https://github.com/fortio/fortio/wiki/Sample-JSON-output) (using `-json 
result.json`)
+Or you can get the data in [JSON 
format](https://github.com/fortio/fortio/wiki/Sample-JSON-output) (using `-json 
result.json`).
 
 ### Web/Graphical UI
 
@@ -1125,7 +1129,7 @@
 
 Simple form/UI:
 
-Sample requests with responses delayed by 250us and 0.5% of 503 and 1.5% of 
429 simulated http errors.
+Sample requests with responses delayed by 250us and 0.5% of 503 and 1.5% of 
429 simulated http errors:
 
 ![Web UI form 
screenshot](https://user-images.githubusercontent.com/3664595/41430618-53d911d4-6fc5-11e8-8e35-d4f5fea4426a.png)
 
@@ -1139,7 +1143,7 @@
 Code 503 : 15 (0.5 %)
 ```
 
-There are live examples on [demo.fortio.org](https://demo.fortio.org/)
+There are live examples on [https://demo.fortio.org](https://demo.fortio.org/)
 
 ## Contributing
 
@@ -1169,7 +1173,7 @@
 
 ## See also
 
-Our wiki and the [Fortio FAQ](https://github.com/fortio/fortio/wiki/FAQ) 
(including for instance differences between `fortio` and `wrk` or `httpbin`)
+Our wiki and the [Fortio FAQ](https://github.com/fortio/fortio/wiki/FAQ) 
(including for instance differences between `fortio` and `wrk` or `httpbin`).
 
 ## Disclaimer
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/Webtest.sh new/fortio-1.38.4/Webtest.sh
--- old/fortio-1.38.2/Webtest.sh        2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/Webtest.sh        2022-11-13 00:06:32.000000000 +0100
@@ -28,6 +28,22 @@
 DOCKERSECNAME=fortio_secure_server
 DOCKERSECVOLNAME=fortio_certs
 FORTIO_BIN_PATH=fortio # /usr/bin/fortio is the full path but isn't needed
+
+# Unresolvable should error out - #653
+docker run fortio/fortio:webtest curl http://doesnt.exist.google.com/
+if [[ $? == 0 ]]; then
+  echo "Error in curl should show up in status"
+  exit 1
+fi
+
+# Expect error with extra args: (timeout (brew install coreutils) returns 124
+# for timeout) - #652
+timeout 3 docker run fortio/fortio:webtest server -loglevel debug extra-arg
+if [[ $? == 124 || $? == 0 ]]; then
+  echo "Unrecognized extra args/typo in flags should error out immediatly"
+  exit 1
+fi
+
 DOCKERID=$(docker run -d --ulimit nofile=$FILE_LIMIT --net host --name 
$DOCKERNAME fortio/fortio:webtest server -ui-path $FORTIO_UI_PREFIX -loglevel 
$LOGLEVEL -maxpayloadsizekb $MAXPAYLOAD -timeout=$TIMEOUT)
 function cleanup {
   set +e # errors are ok during cleanup
@@ -125,7 +141,7 @@
 PPROF_URL="$BASE_URL/debug/pprof/heap?debug=1"
 $CURL "$PPROF_URL" | grep -i TotalAlloc # should find this in memory profile
 # creating dummy container to hold a volume for test certs due to remote 
docker bind mount limitation.
-DOCKERCURLID=$(docker run -d -v $TEST_CERT_VOL --net host --name 
$DOCKERSECVOLNAME docker.io/fortio/fortio.build:v47 sleep 120)
+DOCKERCURLID=$(docker run -d -v $TEST_CERT_VOL --net host --name 
$DOCKERSECVOLNAME 
docker.io/fortio/fortio.build:v50@sha256:fe69c193d8ad40eb0d791984881f3678aead02660b8e3468c757f717892ada4c
 sleep 120)
 # while we have something with actual curl binary do
 # Test for h2c upgrade (#562)
 docker exec $DOCKERSECVOLNAME /usr/bin/curl -v --http2 -m 10 -d foo42 
http://localhost:8080/debug | tee >(cat 1>&2) | grep foo42
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/bincommon/commonflags.go 
new/fortio-1.38.4/bincommon/commonflags.go
--- old/fortio-1.38.2/bincommon/commonflags.go  2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/bincommon/commonflags.go  2022-11-13 00:06:32.000000000 
+0100
@@ -152,7 +152,7 @@
        client, _ := fhttp.NewClient(o)
        // big gotcha that nil client isn't nil interface value (!)
        if client == nil || reflect.ValueOf(client).IsNil() {
-               return // error logged already
+               os.Exit(1) // error logged already
        }
        code, data, header := client.Fetch()
        log.LogVf("Fetch result code %d, data len %d, headerlen %d", code, 
len(data), header)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/dflag/Dockerfile.tests 
new/fortio-1.38.4/dflag/Dockerfile.tests
--- old/fortio-1.38.2/dflag/Dockerfile.tests    2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/dflag/Dockerfile.tests    1970-01-01 01:00:00.000000000 
+0100
@@ -1,8 +0,0 @@
-FROM golang as builder
-
-WORKDIR /build
-COPY go.mod .
-COPY go.sum .
-RUN go mod download
-ADD . /build/
-RUN go test -v ./dflag/...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/dflag/Makefile 
new/fortio-1.38.4/dflag/Makefile
--- old/fortio-1.38.2/dflag/Makefile    2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/dflag/Makefile    1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-tests:
-       docker build -f Dockerfile.tests ..
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/dflag/dynstring_test.go 
new/fortio-1.38.4/dflag/dynstring_test.go
--- old/fortio-1.38.2/dflag/dynstring_test.go   2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/dflag/dynstring_test.go   2022-11-13 00:06:32.000000000 
+0100
@@ -12,7 +12,7 @@
        "fortio.org/assert"
 )
 
-const notifierTimeout = 50 * time.Millisecond
+const notifierTimeout = 100 * time.Millisecond
 
 func TestDynString_SetAndGet(t *testing.T) {
        set := flag.NewFlagSet("foobar", flag.ContinueOnError)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/echosrv/echo.go 
new/fortio-1.38.4/echosrv/echo.go
--- old/fortio-1.38.2/echosrv/echo.go   2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/echosrv/echo.go   2022-11-13 00:06:32.000000000 +0100
@@ -37,7 +37,7 @@
 func main() {
        flag.Parse()
        if len(os.Args) >= 2 && strings.Contains(os.Args[1], "version") {
-               fmt.Println(version.Long())
+               fmt.Println(version.Full())
                os.Exit(0)
        }
        if _, addr := fhttp.Serve(*port, *debugPath); addr == nil {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/fhttp/http_client.go 
new/fortio-1.38.4/fhttp/http_client.go
--- old/fortio-1.38.2/fhttp/http_client.go      2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/fhttp/http_client.go      2022-11-13 00:06:32.000000000 
+0100
@@ -273,11 +273,22 @@
                return fmt.Errorf("invalid extra header '%s', expecting Key: 
Value", hdr)
        }
        key := strings.TrimSpace(s[0])
-       value := strings.TrimSpace(s[1])
-       if strings.EqualFold(key, "host") {
+       // No TrimSpace for the value, so we can set empty "" vs just 
whitespace " " which
+       // will get trimmed later but treated differently: not emitted vs 
emitted empty for User-Agent.
+       value := s[1]
+       switch strings.ToLower(key) {
+       case "host":
                log.LogVf("Will be setting special Host header to %s", value)
-               h.hostOverride = value
-       } else {
+               h.hostOverride = strings.TrimSpace(value) // This one needs to 
be trimmed
+       case "user-agent":
+               if value == "" {
+                       log.Infof("Deleting default User-Agent: header.")
+                       h.extraHeaders.Del(key)
+               } else {
+                       log.Infof("User-Agent being Set to %q", value)
+                       h.extraHeaders.Set(key, value)
+               }
+       default:
                log.LogVf("Setting regular extra header %s: %s", key, value)
                h.extraHeaders.Add(key, value)
                log.Debugf("headers now %+v", h.extraHeaders)
@@ -333,6 +344,10 @@
        if o.hostOverride != "" {
                req.Host = o.hostOverride
        }
+       // Another workaround for std client otherwise trying to set a default 
User-Agent
+       if _, ok := req.Header["User-Agent"]; !ok {
+               req.Header.Set("User-Agent", "")
+       }
        if !log.LogDebug() {
                return req, nil
        }
@@ -474,7 +489,6 @@
        if req == nil {
                return nil, err
        }
-
        client := Client{
                url:                  o.URL,
                path:                 req.URL.Path,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/fhttp/http_forwarder.go 
new/fortio-1.38.4/fhttp/http_forwarder.go
--- old/fortio-1.38.2/fhttp/http_forwarder.go   2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/fhttp/http_forwarder.go   2022-11-13 00:06:32.000000000 
+0100
@@ -72,7 +72,7 @@
        return req
 }
 
-// CopyHeaders copies all or trace headers.
+// CopyHeaders copies all or trace headers from `r` into `req`.
 func CopyHeaders(req, r *http.Request, all bool) {
        // Copy only trace headers unless all is true.
        for k, v := range r.Header {
@@ -85,6 +85,11 @@
                        log.Debugf("Skipping header %q", k)
                }
        }
+       if _, ok := r.Header["User-Agent"]; !ok {
+               // explicitly disable User-Agent so it's not set
+               // to default value (go client lib 'feature' workaround)
+               req.Header.Set("User-Agent", "")
+       }
 }
 
 // MakeSimpleRequest makes a new request for url but copies trace headers from 
input request r.
@@ -97,9 +102,7 @@
        }
        // Copy only trace headers or all of them:
        CopyHeaders(req, r, copyAllHeaders)
-       if copyAllHeaders {
-               req.Header.Add("X-Proxy-Agent", jrpc.UserAgent)
-       } else {
+       if !copyAllHeaders {
                req.Header.Set(jrpc.UserAgentHeader, jrpc.UserAgent)
        }
        return req
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/fhttp/http_forwarder_test.go 
new/fortio-1.38.4/fhttp/http_forwarder_test.go
--- old/fortio-1.38.2/fhttp/http_forwarder_test.go      2022-10-24 
22:53:20.000000000 +0200
+++ new/fortio-1.38.4/fhttp/http_forwarder_test.go      2022-11-13 
00:06:32.000000000 +0100
@@ -19,6 +19,8 @@
        "fmt"
        "net/http"
        "testing"
+
+       "fortio.org/fortio/jrpc"
 )
 
 func TestMultiProxy(t *testing.T) {
@@ -27,12 +29,18 @@
        for i := 0; i < 2; i++ {
                serial := (i == 0)
                mcfg := MultiServerConfig{Serial: serial}
-               mcfg.Targets = []TargetConf{{Destination: urlBase, 
MirrorOrigin: true}, {Destination: urlBase + "echo?status=555"}}
+               mcfg.Targets = []TargetConf{
+                       {Destination: urlBase, MirrorOrigin: true},
+                       {Destination: urlBase + "debug", MirrorOrigin: false},
+                       {Destination: urlBase + "echo?status=555"},
+               }
                _, multiAddr := MultiServer("0", &mcfg)
                url := fmt.Sprintf("http://%s/debug";, multiAddr)
                payload := "A test payload"
                opts := HTTPOptions{URL: url, Payload: []byte(payload)}
+               opts.AddAndValidateExtraHeader("User-agent:")
                opts.AddAndValidateExtraHeader("b3: traceid...")
+               opts.AddAndValidateExtraHeader("X-FA: bar") // so it comes just 
before X-Fortio-Multi-Id
                code, data := Fetch(&opts)
                if serial && code != http.StatusOK {
                        t.Errorf("Got %d %s instead of ok in serial mode (first 
response sets code) for %s", code, DebugSummary(data, 256), url)
@@ -41,18 +49,49 @@
                        t.Errorf("Got %d %s instead of 555 in parallel mode 
(non ok response sets code) for %s", code, DebugSummary(data, 256), url)
                }
                if !bytes.Contains(data, []byte(payload)) {
-                       t.Errorf("Result %s doesn't contain expected payload 
echo back %q", DebugSummary(data, 1024), payload)
+                       t.Errorf("Missing expected payload %q in %s", payload, 
DebugSummary(data, 1024))
+               }
+               searchFor := "B3: traceid..."
+               if !bytes.Contains(data, []byte(searchFor)) {
+                       t.Errorf("Missing expected trace header %q in %s", 
searchFor, DebugSummary(data, 1024))
+               }
+               searchFor = "\nX-Fa: bar\nX-Fortio-Multi-Id: 1\n"
+               if !bytes.Contains(data, []byte(searchFor)) {
+                       t.Errorf("Missing expected general header %q in 1st req 
%s", searchFor, DebugSummary(data, 1024))
+               }
+               searchFor = "\nX-Fa: bar\nX-Fortio-Multi-Id: 2\n"
+               if bytes.Contains(data, []byte(searchFor)) {
+                       t.Errorf("Unexpected non trace header %q in 2nd req 
%s", searchFor, DebugSummary(data, 1024))
                }
                // Issue #624
                if bytes.Contains(data, []byte("gzip")) {
-                       t.Errorf("Result %s contains unexpected gzip (accept 
encoding)", DebugSummary(data, 1024))
-               }
-               if !bytes.Contains(data, []byte("X-Fortio-Multi-Id: 1")) {
-                       t.Errorf("Result %s doesn't contain expected 
X-Fortio-Multi-Id: 1", DebugSummary(data, 1024))
+                       t.Errorf("Unexpected gzip (accept encoding)in %s", 
DebugSummary(data, 1024))
                }
-               // Second request errors 100% so shouldn't be found
-               if bytes.Contains(data, []byte("X-Fortio-Multi-Id: 2")) {
-                       t.Errorf("Result %s contains unexpected 
X-Fortio-Multi-Id: 2", DebugSummary(data, 1024))
+               searchFor = "X-Fortio-Multi-Id: 1"
+               if !bytes.Contains(data, []byte(searchFor)) {
+                       t.Errorf("Missing expected %q in %s", searchFor, 
DebugSummary(data, 1024))
+               }
+               // Second request should be found
+               searchFor = "X-Fortio-Multi-Id: 2"
+               if !bytes.Contains(data, []byte(searchFor)) {
+                       t.Errorf("Missing expected %q in %s", searchFor, 
DebugSummary(data, 1024))
+               }
+               // Third request errors 100% so shouldn't be found
+               searchFor = "X-Fortio-Multi-Id: 3"
+               if bytes.Contains(data, []byte(searchFor)) {
+                       t.Errorf("Unexpected %q in %s", searchFor, 
DebugSummary(data, 1024))
+               }
+               searchFor = "\nX-Proxy-Agent: " + jrpc.UserAgent + "\n"
+               if !bytes.Contains(data, []byte(searchFor)) {
+                       t.Errorf("Missing %q in %s", searchFor, 
DebugSummary(data, 2048))
+               }
+               searchFor = "\nUser-Agent: " + jrpc.UserAgent + 
"\nX-Fortio-Multi-Id: 2\n"
+               if !bytes.Contains(data, []byte(searchFor)) {
+                       t.Errorf("Missing %q in %s", searchFor, 
DebugSummary(data, 2048))
+               }
+               searchFor = "\nUser-Agent: " + jrpc.UserAgent + 
"\nX-Fortio-Multi-Id: 1\n"
+               if bytes.Contains(data, []byte(searchFor)) {
+                       t.Errorf("Unexpected %q in %s", searchFor, 
DebugSummary(data, 2048))
                }
        }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/fhttp/http_test.go 
new/fortio-1.38.4/fhttp/http_test.go
--- old/fortio-1.38.2/fhttp/http_test.go        2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/fhttp/http_test.go        2022-11-13 00:06:32.000000000 
+0100
@@ -67,13 +67,22 @@
        if err == nil {
                t.Errorf("Expected error for header without value, did not get 
one")
        }
-       o.ResetHeaders()
+       o.InitHeaders()
        h = o.AllHeaders()
        if h.Get("Host") != "" {
                t.Errorf("After reset Host header should be nil, got '%v'", 
h.Get("Host"))
        }
+       if len(h) != 1 {
+               t.Errorf("Header count mismatch after reset, got %d instead of 
1. %+v", len(h), h)
+       }
+       // test user-agent delete:
+       o.AddAndValidateExtraHeader("UsER-AgENT:")
+       h = o.AllHeaders()
        if len(h) != 0 {
-               t.Errorf("Header count mismatch after reset, got %d instead of 
1", len(h))
+               t.Errorf("Header count mismatch after delete, got %d instead of 
0. %+v", len(h), h)
+       }
+       if h.Get("User-Agent") != "" {
+               t.Errorf("User-Agent header should be empty after delete, got 
'%v'", h.Get("User-Agent"))
        }
 }
 
@@ -115,7 +124,7 @@
        if o.URL != expected {
                t.Errorf("Got initially '%s', expected '%s'", o.URL, expected)
        }
-       o.AddAndValidateExtraHeader("FoO: BaR")
+       o.AddAndValidateExtraHeader("FoO:BaR")
        // re init should not erase headers
        o.Init(o.URL)
        if o.AllHeaders().Get("Foo") != "BaR" {
@@ -1120,6 +1129,11 @@
        o.AddAndValidateExtraHeader("CCC: ccc")
        o.AddAndValidateExtraHeader("ZZZ: zzz")
        o.AddAndValidateExtraHeader("AAA: aaa")
+       // test that headers usually Add (list) but stay in order of being set
+       o.AddAndValidateExtraHeader("BBB: aa2")
+       // test that User-Agent is special, only last value is kept - and 
replaces the default jrpc.UserAgent
+       o.AddAndValidateExtraHeader("User-Agent: ua1")
+       o.AddAndValidateExtraHeader("User-Agent: ua2")
        client, _ := NewClient(&o)
        now := time.Now()
        code, data, header := client.Fetch() // used to panic/bug #127
@@ -1140,13 +1154,13 @@
                "Host: localhost:%d\n"+
                "Aaa: aaa\n"+
                "Accept-Encoding: gzip\n"+
-               "Bbb: bbb\n"+
+               "Bbb: bbb,aa2\n"+
                "Ccc: ccc\n"+
                "Content-Length: 4\n"+
                "Content-Type: application/octet-stream\n"+
-               "User-Agent: %s\n"+
+               "User-Agent: ua2\n"+
                "Zzz: zzz\n\n"+
-               "body:\n\nabcd\n", a.Port, jrpc.UserAgent)
+               "body:\n\nabcd\n", a.Port)
        if body != expected {
                t.Errorf("Get body: %s not as expected: %s", body, expected)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/fhttp/http_utils.go 
new/fortio-1.38.4/fhttp/http_utils.go
--- old/fortio-1.38.2/fhttp/http_utils.go       2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/fhttp/http_utils.go       2022-11-13 00:06:32.000000000 
+0100
@@ -33,6 +33,7 @@
 
        "fortio.org/fortio/dflag"
        "fortio.org/fortio/fnet"
+       "fortio.org/fortio/jrpc"
        "fortio.org/fortio/log"
        "fortio.org/fortio/stats"
 )
@@ -572,9 +573,14 @@
        _ = o.AddAndValidateExtraHeader("X-On-Behalf-Of: " + r.RemoteAddr)
 }
 
-// OnBehalfOfRequest same as OnBehalfOf but places the header directly on the 
dst request object.
+// OnBehalfOfRequest same as OnBehalfOf but places the header directly on the 
dst request object
+// but also adds a X-Proxy-Agent header if the user-agent isn't already the 
same as this running
+// server's version.
 func OnBehalfOfRequest(to *http.Request, from *http.Request) {
        to.Header.Add("X-On-Behalf-Of", from.RemoteAddr)
+       if to.Header.Get("User-Agent") != jrpc.UserAgent {
+               to.Header.Add("X-Proxy-Agent", jrpc.UserAgent)
+       }
 }
 
 // AddHTTPS replaces "http://"; in url with "https://"; or prepends "https://";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/fortio_main.go 
new/fortio-1.38.4/fortio_main.go
--- old/fortio-1.38.2/fortio_main.go    2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/fortio_main.go    2022-11-13 00:06:32.000000000 +0100
@@ -71,16 +71,18 @@
 
 // Usage to a writer.
 func usage(w io.Writer, msgs ...interface{}) {
-       _, _ = fmt.Fprintf(w, "Φορτίο %s usage:\n\t%s command [flags] 
target\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+       _, _ = fmt.Fprintf(w, "Φορτίο %s usage:\n\t%s command [flags] 
target\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
                version.Short(),
                os.Args[0],
                "where command is one of: load (load testing), server (starts 
ui, rest api,",
-               " http-echo, redirect, proxies, tcp-echo and grpc ping 
servers), tcp-echo (only",
-               " the tcp-echo server), report (report only UI server), 
redirect (only the",
-               " redirect server), proxies (only the -M and -P configured 
proxies), grpcping",
-               " (grpc client), or curl (single URL debug), or nc (single tcp 
or udp://",
-               " connection), or version (prints the full version and build 
details).",
-               "where target is a url (http load tests) or host:port (grpc 
health test).")
+               " http-echo, redirect, proxies, tcp-echo, udp-echo and grpc 
ping servers), ",
+               " tcp-echo (only the tcp-echo server), udp-echo (only udp-echo 
server),",
+               " report (report only UI server), redirect (only the redirect 
server),",
+               " proxies (only the -M and -P configured proxies), grpcping 
(grpc client),",
+               " or curl (single URL debug), or nc (single tcp or udp:// 
connection),",
+               " or version (prints the full version and build details).",
+               "where target is a url (http load tests) or host:port (grpc 
health test),",
+               " or tcp://host:port (tcp load test), or udp://host:port (udp 
load test).")
        bincommon.FlagsUsage(w, msgs...)
 }
 
@@ -183,7 +185,16 @@
        calcQPS = flag.Bool("calc-qps", false, "Calculate the qps based on 
number of requests (-n) and duration (-t)")
 )
 
-//nolint:funlen,gocyclo // well yes it's fairly big and lotsa ifs.
+// serverArgCheck always returns true after checking arguments length.
+// so it can be used with isServer = serverArgCheck() below.
+func serverArgCheck() bool {
+       if len(flag.Args()) != 0 {
+               usageErr("Error: too many arguments (typo in a flag?)")
+       }
+       return true
+}
+
+//nolint:funlen // well yes it's fairly long
 func main() {
        flag.Var(&proxiesFlags, "P",
                "Tcp proxies to run, e.g -P \"localport1 
dest_host1:dest_port1\" -P \"[::1]:0 www.google.com:443\" ...")
@@ -229,10 +240,10 @@
        case "load":
                fortioLoad(*curlFlag, percList)
        case "redirect":
-               isServer = true
+               isServer = serverArgCheck()
                fhttp.RedirectToHTTPS(*redirectFlag)
        case "report":
-               isServer = true
+               isServer = serverArgCheck()
                if *redirectFlag != disabled {
                        fhttp.RedirectToHTTPS(*redirectFlag)
                }
@@ -240,23 +251,20 @@
                        os.Exit(1) // error already logged
                }
        case "tcp-echo":
-               isServer = true
+               isServer = serverArgCheck()
                fnet.TCPEchoServer("tcp-echo", *tcpPortFlag)
                startProxies()
        case "udp-echo":
-               isServer = true
+               isServer = serverArgCheck()
                fnet.UDPEchoServer("udp-echo", *udpPortFlag, *udpAsyncFlag)
                startProxies()
        case "proxies":
-               if len(flag.Args()) != 0 {
-                       usageErr("Error: fortio proxies command only takes -P / 
-M flags")
-               }
-               isServer = true
+               isServer = serverArgCheck()
                if startProxies() == 0 {
                        usageErr("Error: fortio proxies command needs at least 
one -P / -M flag")
                }
        case "server":
-               isServer = true
+               isServer = serverArgCheck()
                if *tcpPortFlag != disabled {
                        fnet.TCPEchoServer("tcp-echo", *tcpPortFlag)
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/go.mod new/fortio-1.38.4/go.mod
--- old/fortio-1.38.2/go.mod    2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/go.mod    2022-11-13 00:06:32.000000000 +0100
@@ -3,18 +3,18 @@
 go 1.18
 
 require (
-       fortio.org/assert v1.1.0
+       fortio.org/assert v1.1.2
        github.com/fsnotify/fsnotify v1.6.0
        github.com/golang/protobuf v1.5.2
        github.com/google/uuid v1.3.0
-       golang.org/x/exp v0.0.0-20221012211006-4de253d81b95
-       golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458
+       golang.org/x/exp v0.0.0-20221111204811-129d8d6c17ab
+       golang.org/x/net v0.2.0
        google.golang.org/grpc v1.50.0
 )
 
 require (
-       golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect
-       golang.org/x/text v0.3.8 // indirect
+       golang.org/x/sys v0.2.0 // indirect
+       golang.org/x/text v0.4.0 // indirect
        google.golang.org/genproto v0.0.0-20220714211235-042d03aeabc9 // 
indirect
        google.golang.org/protobuf v1.28.0 // indirect
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/go.sum new/fortio-1.38.4/go.sum
--- old/fortio-1.38.2/go.sum    2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/go.sum    2022-11-13 00:06:32.000000000 +0100
@@ -1,5 +1,5 @@
-fortio.org/assert v1.1.0 h1:AEkX3WzLx4Qsvgg+HyZTp9wHKo0lr1ZcAylZ2YJgGYc=
-fortio.org/assert v1.1.0/go.mod h1:039mG+/iYDPO8Ibx8TrNuJCm2T2SuhwRI3uL9nHTTls=
+fortio.org/assert v1.1.2 h1:t6WGDqPD5VFrUvx30U0+3mgXXcoPonrdKqt0vfJHn8E=
+fortio.org/assert v1.1.2/go.mod h1:039mG+/iYDPO8Ibx8TrNuJCm2T2SuhwRI3uL9nHTTls=
 github.com/fsnotify/fsnotify v1.6.0 
h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
 github.com/fsnotify/fsnotify v1.6.0/go.mod 
h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/golang/protobuf v1.5.0/go.mod 
h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@@ -9,15 +9,15 @@
 github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 
h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg=
-golang.org/x/exp v0.0.0-20221012211006-4de253d81b95/go.mod 
h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
-golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458 
h1:MgJ6t2zo8v0tbmLCueaCbF1RM+TtB0rs3Lv8DGtOIpY=
-golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod 
h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/exp v0.0.0-20221111204811-129d8d6c17ab 
h1:1S7USr8/C0Sgk4egxq4zZ07zYt2Xh1IiFp8hUMXH/us=
+golang.org/x/exp v0.0.0-20221111204811-129d8d6c17ab/go.mod 
h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
+golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 
h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4=
-golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/genproto v0.0.0-20220714211235-042d03aeabc9 
h1:zfXhTgBfGlIh3jMXN06W8qbhFGsh6MJNJiYEuhTddOI=
 google.golang.org/genproto v0.0.0-20220714211235-042d03aeabc9/go.mod 
h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/rapi/restHandler.go 
new/fortio-1.38.4/rapi/restHandler.go
--- old/fortio-1.38.2/rapi/restHandler.go       2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/rapi/restHandler.go       2022-11-13 00:06:32.000000000 
+0100
@@ -266,7 +266,6 @@
        httpopts := &fhttp.HTTPOptions{}
        httpopts.HTTPReqTimeOut = timeout // to be normalized in init 0 
replaced by default value
        httpopts = httpopts.Init(url)
-       httpopts.ResetHeaders()
        httpopts.DisableFastClient = stdClient
        httpopts.SequentialWarmup = sequentialWarmup
        httpopts.Insecure = httpsInsecure
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/release/Dockerfile.in 
new/fortio-1.38.4/release/Dockerfile.in
--- old/fortio-1.38.2/release/Dockerfile.in     2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/release/Dockerfile.in     2022-11-13 00:06:32.000000000 
+0100
@@ -1,5 +1,5 @@
 # Concatenated after ../Dockerfile to create the tgz
-FROM docker.io/fortio/fortio.build:v47 as stage
+FROM 
docker.io/fortio/fortio.build:v50@sha256:fe69c193d8ad40eb0d791984881f3678aead02660b8e3468c757f717892ada4c
 as stage
 ARG archs="amd64 arm64 ppc64le s390x"
 ENV archs=${archs}
 # Build image defaults to build user, switch back to root for
@@ -11,7 +11,7 @@
 
 RUN mkdir -p /tgz usr/bin
 
-WORKDIR /go/src/fortio.org
+WORKDIR /build
 COPY . fortio
 # Check macos does not break
 RUN make -C fortio official-build BUILD_DIR=/build 
OFFICIAL_DIR=/tmp/fortio_mac GOOS=darwin GO_BIN=/usr/local/go/bin/go
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/release/README.md 
new/fortio-1.38.4/release/README.md
--- old/fortio-1.38.2/release/README.md 2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/release/README.md 2022-11-13 00:06:32.000000000 +0100
@@ -29,8 +29,8 @@
 
 Update [../Dockerfile.build](../Dockerfile.build)
 
-Edit the `BUILD_IMAGE_TAG := v5` line in the Makefile, set it to `v6`
-for instance (replace `v6` by whichever is the next one at the time)
+Edit the `BUILD_IMAGE_TAG := v50@sha...` line in the Makefile, set it to `v51`
+for instance (replace `v50` by whichever is the next one at the time and 
temporarily remove the SHA part)
 
 run
 
@@ -40,7 +40,9 @@
 
 Make sure it gets successfully pushed to the fortio registry (requires org 
access)
 
-run
+Then do `make build-image-sha` to get the new image sha to replace/put in 
`BUILD_IMAGE_TAG` line of the Makefile
+
+Then run
 
 ```Shell
 make update-build-image-tag SED=gsed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/ui/templates/main.html 
new/fortio-1.38.4/ui/templates/main.html
--- old/fortio-1.38.2/ui/templates/main.html    2022-10-24 22:53:20.000000000 
+0200
+++ new/fortio-1.38.4/ui/templates/main.html    2022-11-13 00:06:32.000000000 
+0100
@@ -53,12 +53,7 @@
     No Catch-Up (qps is a ceiling): <input type="checkbox" name="nocatchup" 
/><br />
     Percentiles: <input type="text" name="p" size="20" value="50, 75, 90, 99, 
99.9" /> <br />
     Histogram Resolution: <input type="text" name="r" size="8" value="0.0001" 
/> <br />
-    Headers: <br />
-  {{ range $name, $vals := .Headers }}{{range $val := $vals}}
-    <input type="text" name="H" size=40 value="{{$name}}: {{ $val }}" /> <br />
-  {{end}}{{end}} <!-- 3 extra header lines, TODO(#283): add a JS 'more 
headers' button -->
-    <input type="text" name="H" size=40 value="" /> <br />
-    <input type="text" name="H" size=40 value="" /> <br />
+    Extra Headers:<br />
     <input type="text" name="H" size=40 value="" /> <br />
     <button type="button" onclick="addCustomHeader()">+</button>
     <br />
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.38.2/ui/uihandler.go 
new/fortio-1.38.4/ui/uihandler.go
--- old/fortio-1.38.2/ui/uihandler.go   2022-10-24 22:53:20.000000000 +0200
+++ new/fortio-1.38.4/ui/uihandler.go   2022-11-13 00:06:32.000000000 +0100
@@ -185,8 +185,6 @@
        httpopts := &fhttp.HTTPOptions{}
        httpopts.HTTPReqTimeOut = timeout // to be normalized in init 0 
replaced by default value
        httpopts = httpopts.Init(url)
-       defaultHeaders := httpopts.AllHeaders()
-       httpopts.ResetHeaders()
        httpopts.DisableFastClient = stdClient
        httpopts.SequentialWarmup = sequentialWarmup
        httpopts.Insecure = httpsInsecure
@@ -221,7 +219,6 @@
                }
                err := mainTemplate.Execute(w, &struct {
                        R                           *http.Request
-                       Headers                     http.Header
                        Version                     string
                        LogoPath                    string
                        DebugPath                   string
@@ -237,7 +234,7 @@
                        DoStop                      bool
                        DoLoad                      bool
                }{
-                       r, defaultHeaders, version.Short(), logoPath, 
debugPath, echoPath, chartJSPath,
+                       r, version.Short(), logoPath, debugPath, echoPath, 
chartJSPath,
                        startTime.Format(time.ANSIC), url, labels, runid,
                        fhttp.RoundDuration(time.Since(startTime)), durSeconds, 
urlHostPort, mode == stop, mode == run,
                })

++++++ vendor.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/fortio.org/assert/README.md 
new/vendor/fortio.org/assert/README.md
--- old/vendor/fortio.org/assert/README.md      2022-10-25 20:45:32.000000000 
+0200
+++ new/vendor/fortio.org/assert/README.md      2022-11-15 10:21:46.000000000 
+0100
@@ -1,2 +1,3 @@
 # assert
-Minimalistic replacement for 
https://pkg.go.dev/github.com/stretchr/testify/assert 
+
+Minimalistic replacement for 
https://pkg.go.dev/github.com/stretchr/testify/assert
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/fortio.org/assert/assert.go 
new/vendor/fortio.org/assert/assert.go
--- old/vendor/fortio.org/assert/assert.go      2022-10-25 20:45:32.000000000 
+0200
+++ new/vendor/fortio.org/assert/assert.go      2022-11-15 10:21:46.000000000 
+0100
@@ -81,7 +81,10 @@
 
 // Fail fails the test.
 func Fail(t *testing.T, msg string) {
-       t.Fatal(msg)
+       _, file, line, _ := runtime.Caller(1)
+       file = file[strings.LastIndex(file, "/")+1:]
+       fmt.Printf("%s:%d %s\n", file, line, msg)
+       t.FailNow()
 }
 
 // CheckEquals checks if actual == expect and fails the test and logs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/golang.org/x/net/http2/h2c/h2c.go 
new/vendor/golang.org/x/net/http2/h2c/h2c.go
--- old/vendor/golang.org/x/net/http2/h2c/h2c.go        2022-10-25 
20:45:32.000000000 +0200
+++ new/vendor/golang.org/x/net/http2/h2c/h2c.go        2022-11-15 
10:21:46.000000000 +0100
@@ -109,6 +109,7 @@
                        if http2VerboseLogs {
                                log.Printf("h2c: error h2c upgrade: %v", err)
                        }
+                       w.WriteHeader(http.StatusInternalServerError)
                        return
                }
                defer conn.Close()
@@ -167,7 +168,10 @@
                return nil, nil, errors.New("h2c: connection does not support 
Hijack")
        }
 
-       body, _ := io.ReadAll(r.Body)
+       body, err := io.ReadAll(r.Body)
+       if err != nil {
+               return nil, nil, err
+       }
        r.Body = io.NopCloser(bytes.NewBuffer(body))
 
        conn, rw, err := hijacker.Hijack()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/golang.org/x/net/http2/headermap.go 
new/vendor/golang.org/x/net/http2/headermap.go
--- old/vendor/golang.org/x/net/http2/headermap.go      2022-10-25 
20:45:32.000000000 +0200
+++ new/vendor/golang.org/x/net/http2/headermap.go      2022-11-15 
10:21:46.000000000 +0100
@@ -27,7 +27,14 @@
                "accept-language",
                "accept-ranges",
                "age",
+               "access-control-allow-credentials",
+               "access-control-allow-headers",
+               "access-control-allow-methods",
                "access-control-allow-origin",
+               "access-control-expose-headers",
+               "access-control-max-age",
+               "access-control-request-headers",
+               "access-control-request-method",
                "allow",
                "authorization",
                "cache-control",
@@ -53,6 +60,7 @@
                "link",
                "location",
                "max-forwards",
+               "origin",
                "proxy-authenticate",
                "proxy-authorization",
                "range",
@@ -68,6 +76,8 @@
                "vary",
                "via",
                "www-authenticate",
+               "x-forwarded-for",
+               "x-forwarded-proto",
        }
        commonLowerHeader = make(map[string]string, len(common))
        commonCanonHeader = make(map[string]string, len(common))
@@ -85,3 +95,11 @@
        }
        return asciiToLower(v)
 }
+
+func canonicalHeader(v string) string {
+       buildCommonHeaderMapsOnce()
+       if s, ok := commonCanonHeader[v]; ok {
+               return s
+       }
+       return http.CanonicalHeaderKey(v)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/golang.org/x/net/http2/hpack/static_table.go 
new/vendor/golang.org/x/net/http2/hpack/static_table.go
--- old/vendor/golang.org/x/net/http2/hpack/static_table.go     1970-01-01 
01:00:00.000000000 +0100
+++ new/vendor/golang.org/x/net/http2/hpack/static_table.go     2022-11-15 
10:21:46.000000000 +0100
@@ -0,0 +1,188 @@
+// go generate gen.go
+// Code generated by the command above; DO NOT EDIT.
+
+package hpack
+
+var staticTable = &headerFieldTable{
+       evictCount: 0,
+       byName: map[string]uint64{
+               ":authority":                  1,
+               ":method":                     3,
+               ":path":                       5,
+               ":scheme":                     7,
+               ":status":                     14,
+               "accept-charset":              15,
+               "accept-encoding":             16,
+               "accept-language":             17,
+               "accept-ranges":               18,
+               "accept":                      19,
+               "access-control-allow-origin": 20,
+               "age":                         21,
+               "allow":                       22,
+               "authorization":               23,
+               "cache-control":               24,
+               "content-disposition":         25,
+               "content-encoding":            26,
+               "content-language":            27,
+               "content-length":              28,
+               "content-location":            29,
+               "content-range":               30,
+               "content-type":                31,
+               "cookie":                      32,
+               "date":                        33,
+               "etag":                        34,
+               "expect":                      35,
+               "expires":                     36,
+               "from":                        37,
+               "host":                        38,
+               "if-match":                    39,
+               "if-modified-since":           40,
+               "if-none-match":               41,
+               "if-range":                    42,
+               "if-unmodified-since":         43,
+               "last-modified":               44,
+               "link":                        45,
+               "location":                    46,
+               "max-forwards":                47,
+               "proxy-authenticate":          48,
+               "proxy-authorization":         49,
+               "range":                       50,
+               "referer":                     51,
+               "refresh":                     52,
+               "retry-after":                 53,
+               "server":                      54,
+               "set-cookie":                  55,
+               "strict-transport-security":   56,
+               "transfer-encoding":           57,
+               "user-agent":                  58,
+               "vary":                        59,
+               "via":                         60,
+               "www-authenticate":            61,
+       },
+       byNameValue: map[pairNameValue]uint64{
+               {name: ":authority", value: ""}:                   1,
+               {name: ":method", value: "GET"}:                   2,
+               {name: ":method", value: "POST"}:                  3,
+               {name: ":path", value: "/"}:                       4,
+               {name: ":path", value: "/index.html"}:             5,
+               {name: ":scheme", value: "http"}:                  6,
+               {name: ":scheme", value: "https"}:                 7,
+               {name: ":status", value: "200"}:                   8,
+               {name: ":status", value: "204"}:                   9,
+               {name: ":status", value: "206"}:                   10,
+               {name: ":status", value: "304"}:                   11,
+               {name: ":status", value: "400"}:                   12,
+               {name: ":status", value: "404"}:                   13,
+               {name: ":status", value: "500"}:                   14,
+               {name: "accept-charset", value: ""}:               15,
+               {name: "accept-encoding", value: "gzip, deflate"}: 16,
+               {name: "accept-language", value: ""}:              17,
+               {name: "accept-ranges", value: ""}:                18,
+               {name: "accept", value: ""}:                       19,
+               {name: "access-control-allow-origin", value: ""}:  20,
+               {name: "age", value: ""}:                          21,
+               {name: "allow", value: ""}:                        22,
+               {name: "authorization", value: ""}:                23,
+               {name: "cache-control", value: ""}:                24,
+               {name: "content-disposition", value: ""}:          25,
+               {name: "content-encoding", value: ""}:             26,
+               {name: "content-language", value: ""}:             27,
+               {name: "content-length", value: ""}:               28,
+               {name: "content-location", value: ""}:             29,
+               {name: "content-range", value: ""}:                30,
+               {name: "content-type", value: ""}:                 31,
+               {name: "cookie", value: ""}:                       32,
+               {name: "date", value: ""}:                         33,
+               {name: "etag", value: ""}:                         34,
+               {name: "expect", value: ""}:                       35,
+               {name: "expires", value: ""}:                      36,
+               {name: "from", value: ""}:                         37,
+               {name: "host", value: ""}:                         38,
+               {name: "if-match", value: ""}:                     39,
+               {name: "if-modified-since", value: ""}:            40,
+               {name: "if-none-match", value: ""}:                41,
+               {name: "if-range", value: ""}:                     42,
+               {name: "if-unmodified-since", value: ""}:          43,
+               {name: "last-modified", value: ""}:                44,
+               {name: "link", value: ""}:                         45,
+               {name: "location", value: ""}:                     46,
+               {name: "max-forwards", value: ""}:                 47,
+               {name: "proxy-authenticate", value: ""}:           48,
+               {name: "proxy-authorization", value: ""}:          49,
+               {name: "range", value: ""}:                        50,
+               {name: "referer", value: ""}:                      51,
+               {name: "refresh", value: ""}:                      52,
+               {name: "retry-after", value: ""}:                  53,
+               {name: "server", value: ""}:                       54,
+               {name: "set-cookie", value: ""}:                   55,
+               {name: "strict-transport-security", value: ""}:    56,
+               {name: "transfer-encoding", value: ""}:            57,
+               {name: "user-agent", value: ""}:                   58,
+               {name: "vary", value: ""}:                         59,
+               {name: "via", value: ""}:                          60,
+               {name: "www-authenticate", value: ""}:             61,
+       },
+       ents: []HeaderField{
+               {Name: ":authority", Value: "", Sensitive: false},
+               {Name: ":method", Value: "GET", Sensitive: false},
+               {Name: ":method", Value: "POST", Sensitive: false},
+               {Name: ":path", Value: "/", Sensitive: false},
+               {Name: ":path", Value: "/index.html", Sensitive: false},
+               {Name: ":scheme", Value: "http", Sensitive: false},
+               {Name: ":scheme", Value: "https", Sensitive: false},
+               {Name: ":status", Value: "200", Sensitive: false},
+               {Name: ":status", Value: "204", Sensitive: false},
+               {Name: ":status", Value: "206", Sensitive: false},
+               {Name: ":status", Value: "304", Sensitive: false},
+               {Name: ":status", Value: "400", Sensitive: false},
+               {Name: ":status", Value: "404", Sensitive: false},
+               {Name: ":status", Value: "500", Sensitive: false},
+               {Name: "accept-charset", Value: "", Sensitive: false},
+               {Name: "accept-encoding", Value: "gzip, deflate", Sensitive: 
false},
+               {Name: "accept-language", Value: "", Sensitive: false},
+               {Name: "accept-ranges", Value: "", Sensitive: false},
+               {Name: "accept", Value: "", Sensitive: false},
+               {Name: "access-control-allow-origin", Value: "", Sensitive: 
false},
+               {Name: "age", Value: "", Sensitive: false},
+               {Name: "allow", Value: "", Sensitive: false},
+               {Name: "authorization", Value: "", Sensitive: false},
+               {Name: "cache-control", Value: "", Sensitive: false},
+               {Name: "content-disposition", Value: "", Sensitive: false},
+               {Name: "content-encoding", Value: "", Sensitive: false},
+               {Name: "content-language", Value: "", Sensitive: false},
+               {Name: "content-length", Value: "", Sensitive: false},
+               {Name: "content-location", Value: "", Sensitive: false},
+               {Name: "content-range", Value: "", Sensitive: false},
+               {Name: "content-type", Value: "", Sensitive: false},
+               {Name: "cookie", Value: "", Sensitive: false},
+               {Name: "date", Value: "", Sensitive: false},
+               {Name: "etag", Value: "", Sensitive: false},
+               {Name: "expect", Value: "", Sensitive: false},
+               {Name: "expires", Value: "", Sensitive: false},
+               {Name: "from", Value: "", Sensitive: false},
+               {Name: "host", Value: "", Sensitive: false},
+               {Name: "if-match", Value: "", Sensitive: false},
+               {Name: "if-modified-since", Value: "", Sensitive: false},
+               {Name: "if-none-match", Value: "", Sensitive: false},
+               {Name: "if-range", Value: "", Sensitive: false},
+               {Name: "if-unmodified-since", Value: "", Sensitive: false},
+               {Name: "last-modified", Value: "", Sensitive: false},
+               {Name: "link", Value: "", Sensitive: false},
+               {Name: "location", Value: "", Sensitive: false},
+               {Name: "max-forwards", Value: "", Sensitive: false},
+               {Name: "proxy-authenticate", Value: "", Sensitive: false},
+               {Name: "proxy-authorization", Value: "", Sensitive: false},
+               {Name: "range", Value: "", Sensitive: false},
+               {Name: "referer", Value: "", Sensitive: false},
+               {Name: "refresh", Value: "", Sensitive: false},
+               {Name: "retry-after", Value: "", Sensitive: false},
+               {Name: "server", Value: "", Sensitive: false},
+               {Name: "set-cookie", Value: "", Sensitive: false},
+               {Name: "strict-transport-security", Value: "", Sensitive: 
false},
+               {Name: "transfer-encoding", Value: "", Sensitive: false},
+               {Name: "user-agent", Value: "", Sensitive: false},
+               {Name: "vary", Value: "", Sensitive: false},
+               {Name: "via", Value: "", Sensitive: false},
+               {Name: "www-authenticate", Value: "", Sensitive: false},
+       },
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/golang.org/x/net/http2/hpack/tables.go 
new/vendor/golang.org/x/net/http2/hpack/tables.go
--- old/vendor/golang.org/x/net/http2/hpack/tables.go   2022-10-25 
20:45:32.000000000 +0200
+++ new/vendor/golang.org/x/net/http2/hpack/tables.go   2022-11-15 
10:21:46.000000000 +0100
@@ -96,8 +96,7 @@
 // meaning t.ents is reversed for dynamic tables. Hence, when t is a dynamic
 // table, the return value i actually refers to the entry t.ents[t.len()-i].
 //
-// All tables are assumed to be a dynamic tables except for the global
-// staticTable pointer.
+// All tables are assumed to be a dynamic tables except for the global 
staticTable.
 //
 // See Section 2.3.3.
 func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch 
bool) {
@@ -125,81 +124,6 @@
        return k + 1
 }
 
-// 
http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
-var staticTable = newStaticTable()
-var staticTableEntries = [...]HeaderField{
-       {Name: ":authority"},
-       {Name: ":method", Value: "GET"},
-       {Name: ":method", Value: "POST"},
-       {Name: ":path", Value: "/"},
-       {Name: ":path", Value: "/index.html"},
-       {Name: ":scheme", Value: "http"},
-       {Name: ":scheme", Value: "https"},
-       {Name: ":status", Value: "200"},
-       {Name: ":status", Value: "204"},
-       {Name: ":status", Value: "206"},
-       {Name: ":status", Value: "304"},
-       {Name: ":status", Value: "400"},
-       {Name: ":status", Value: "404"},
-       {Name: ":status", Value: "500"},
-       {Name: "accept-charset"},
-       {Name: "accept-encoding", Value: "gzip, deflate"},
-       {Name: "accept-language"},
-       {Name: "accept-ranges"},
-       {Name: "accept"},
-       {Name: "access-control-allow-origin"},
-       {Name: "age"},
-       {Name: "allow"},
-       {Name: "authorization"},
-       {Name: "cache-control"},
-       {Name: "content-disposition"},
-       {Name: "content-encoding"},
-       {Name: "content-language"},
-       {Name: "content-length"},
-       {Name: "content-location"},
-       {Name: "content-range"},
-       {Name: "content-type"},
-       {Name: "cookie"},
-       {Name: "date"},
-       {Name: "etag"},
-       {Name: "expect"},
-       {Name: "expires"},
-       {Name: "from"},
-       {Name: "host"},
-       {Name: "if-match"},
-       {Name: "if-modified-since"},
-       {Name: "if-none-match"},
-       {Name: "if-range"},
-       {Name: "if-unmodified-since"},
-       {Name: "last-modified"},
-       {Name: "link"},
-       {Name: "location"},
-       {Name: "max-forwards"},
-       {Name: "proxy-authenticate"},
-       {Name: "proxy-authorization"},
-       {Name: "range"},
-       {Name: "referer"},
-       {Name: "refresh"},
-       {Name: "retry-after"},
-       {Name: "server"},
-       {Name: "set-cookie"},
-       {Name: "strict-transport-security"},
-       {Name: "transfer-encoding"},
-       {Name: "user-agent"},
-       {Name: "vary"},
-       {Name: "via"},
-       {Name: "www-authenticate"},
-}
-
-func newStaticTable() *headerFieldTable {
-       t := &headerFieldTable{}
-       t.init()
-       for _, e := range staticTableEntries[:] {
-               t.addEntry(e)
-       }
-       return t
-}
-
 var huffmanCodes = [256]uint32{
        0x1ff8,
        0x7fffd8,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/golang.org/x/net/http2/server.go 
new/vendor/golang.org/x/net/http2/server.go
--- old/vendor/golang.org/x/net/http2/server.go 2022-10-25 20:45:32.000000000 
+0200
+++ new/vendor/golang.org/x/net/http2/server.go 2022-11-15 10:21:46.000000000 
+0100
@@ -622,7 +622,9 @@
        resetQueued      bool        // RST_STREAM queued for write; set by 
sc.resetStream
        gotTrailerHeader bool        // HEADER frame for trailers was seen
        wroteHeaders     bool        // whether we wrote headers (not status 
100)
+       readDeadline     *time.Timer // nil if unused
        writeDeadline    *time.Timer // nil if unused
+       closeErr         error       // set before cw is closed
 
        trailer    http.Header // accumulated trailers
        reqTrailer http.Header // handler's Request.Trailer
@@ -869,7 +871,9 @@
 
        // Each connection starts with initialWindowSize inflow tokens.
        // If a higher value is configured, we add more tokens.
-       sc.sendWindowUpdate(nil)
+       if diff := sc.srv.initialConnRecvWindowSize() - initialWindowSize; diff 
> 0 {
+               sc.sendWindowUpdate(nil, int(diff))
+       }
 
        if err := sc.readPreface(); err != nil {
                sc.condlogf(err, "http2: server: error reading preface from 
client %v: %v", sc.conn.RemoteAddr(), err)
@@ -946,6 +950,8 @@
                                }
                        case *startPushRequest:
                                sc.startPush(v)
+                       case func(*serverConn):
+                               v(sc)
                        default:
                                panic(fmt.Sprintf("unexpected type %T", v))
                        }
@@ -1459,6 +1465,21 @@
                sc.sawFirstSettings = true
        }
 
+       // Discard frames for streams initiated after the identified last
+       // stream sent in a GOAWAY, or all frames after sending an error.
+       // We still need to return connection-level flow control for DATA 
frames.
+       // RFC 9113 Section 6.8.
+       if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || f.Header().StreamID > 
sc.maxClientStreamID) {
+
+               if f, ok := f.(*DataFrame); ok {
+                       if sc.inflow.available() < int32(f.Length) {
+                               return sc.countError("data_flow", 
streamError(f.Header().StreamID, ErrCodeFlowControl))
+                       }
+                       sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
+               }
+               return nil
+       }
+
        switch f := f.(type) {
        case *SettingsFrame:
                return sc.processSettings(f)
@@ -1501,9 +1522,6 @@
                // PROTOCOL_ERROR."
                return sc.countError("ping_on_stream", 
ConnectionError(ErrCodeProtocol))
        }
-       if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
-               return nil
-       }
        sc.writeFrame(FrameWriteRequest{write: writePingAck{f}})
        return nil
 }
@@ -1565,6 +1583,9 @@
                panic(fmt.Sprintf("invariant; can't close stream in state %v", 
st.state))
        }
        st.state = stateClosed
+       if st.readDeadline != nil {
+               st.readDeadline.Stop()
+       }
        if st.writeDeadline != nil {
                st.writeDeadline.Stop()
        }
@@ -1586,10 +1607,18 @@
        if p := st.body; p != nil {
                // Return any buffered unread bytes worth of conn-level flow 
control.
                // See golang.org/issue/16481
-               sc.sendWindowUpdate(nil)
+               sc.sendWindowUpdate(nil, p.Len())
 
                p.CloseWithError(err)
        }
+       if e, ok := err.(StreamError); ok {
+               if e.Cause != nil {
+                       err = e.Cause
+               } else {
+                       err = errStreamClosed
+               }
+       }
+       st.closeErr = err
        st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
        sc.writeSched.CloseStream(st.id)
 }
@@ -1686,16 +1715,6 @@
 func (sc *serverConn) processData(f *DataFrame) error {
        sc.serveG.check()
        id := f.Header().StreamID
-       if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || id > 
sc.maxClientStreamID) {
-               // Discard all DATA frames if the GOAWAY is due to an
-               // error, or:
-               //
-               // Section 6.8: After sending a GOAWAY frame, the sender
-               // can discard frames for streams initiated by the
-               // receiver with identifiers higher than the identified
-               // last stream.
-               return nil
-       }
 
        data := f.Data()
        state, st := sc.state(id)
@@ -1734,7 +1753,7 @@
                // sendWindowUpdate, which also schedules sending the
                // frames.
                sc.inflow.take(int32(f.Length))
-               sc.sendWindowUpdate(nil) // conn-level
+               sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
 
                if st != nil && st.resetQueued {
                        // Already have a stream error in flight. Don't send 
another.
@@ -1752,7 +1771,7 @@
                        return sc.countError("data_flow", streamError(id, 
ErrCodeFlowControl))
                }
                sc.inflow.take(int32(f.Length))
-               sc.sendWindowUpdate(nil) // conn-level
+               sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
 
                st.body.CloseWithError(fmt.Errorf("sender tried to send more 
than declared Content-Length of %d bytes", st.declBodyBytes))
                // RFC 7540, sec 8.1.2.6: A request or response is also 
malformed if the
@@ -1770,7 +1789,7 @@
                if len(data) > 0 {
                        wrote, err := st.body.Write(data)
                        if err != nil {
-                               sc.sendWindowUpdate32(nil, 
int32(f.Length)-int32(wrote))
+                               sc.sendWindowUpdate(nil, int(f.Length)-wrote)
                                return sc.countError("body_write_err", 
streamError(id, ErrCodeStreamClosed))
                        }
                        if wrote != len(data) {
@@ -1838,19 +1857,27 @@
        }
 }
 
+// onReadTimeout is run on its own goroutine (from time.AfterFunc)
+// when the stream's ReadTimeout has fired.
+func (st *stream) onReadTimeout() {
+       // Wrap the ErrDeadlineExceeded to avoid callers depending on us
+       // returning the bare error.
+       st.body.CloseWithError(fmt.Errorf("%w", os.ErrDeadlineExceeded))
+}
+
 // onWriteTimeout is run on its own goroutine (from time.AfterFunc)
 // when the stream's WriteTimeout has fired.
 func (st *stream) onWriteTimeout() {
-       st.sc.writeFrameFromHandler(FrameWriteRequest{write: streamError(st.id, 
ErrCodeInternal)})
+       st.sc.writeFrameFromHandler(FrameWriteRequest{write: StreamError{
+               StreamID: st.id,
+               Code:     ErrCodeInternal,
+               Cause:    os.ErrDeadlineExceeded,
+       }})
 }
 
 func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
        sc.serveG.check()
        id := f.StreamID
-       if sc.inGoAway {
-               // Ignore.
-               return nil
-       }
        // http://tools.ietf.org/html/rfc7540#section-5.1.1
        // Streams initiated by a client MUST use odd-numbered stream
        // identifiers. [...] An endpoint that receives an unexpected
@@ -1953,6 +1980,9 @@
        // (in Go 1.8), though. That's a more sane option anyway.
        if sc.hs.ReadTimeout != 0 {
                sc.conn.SetReadDeadline(time.Time{})
+               if st.body != nil {
+                       st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, 
st.onReadTimeout)
+               }
        }
 
        go sc.runHandler(rw, req, handler)
@@ -2021,9 +2051,6 @@
 }
 
 func (sc *serverConn) processPriority(f *PriorityFrame) error {
-       if sc.inGoAway {
-               return nil
-       }
        if err := sc.checkPriority(f.StreamID, f.PriorityParam); err != nil {
                return err
        }
@@ -2322,39 +2349,24 @@
 
 func (sc *serverConn) noteBodyRead(st *stream, n int) {
        sc.serveG.check()
-       sc.sendWindowUpdate(nil) // conn-level
+       sc.sendWindowUpdate(nil, n) // conn-level
        if st.state != stateHalfClosedRemote && st.state != stateClosed {
                // Don't send this WINDOW_UPDATE if the stream is closed
                // remotely.
-               sc.sendWindowUpdate(st)
+               sc.sendWindowUpdate(st, n)
        }
 }
 
 // st may be nil for conn-level
-func (sc *serverConn) sendWindowUpdate(st *stream) {
+func (sc *serverConn) sendWindowUpdate(st *stream, n int) {
        sc.serveG.check()
-
-       var n int32
-       if st == nil {
-               if avail, windowSize := sc.inflow.available(), 
sc.srv.initialConnRecvWindowSize(); avail > windowSize/2 {
-                       return
-               } else {
-                       n = windowSize - avail
-               }
-       } else {
-               if avail, windowSize := st.inflow.available(), 
sc.srv.initialStreamRecvWindowSize(); avail > windowSize/2 {
-                       return
-               } else {
-                       n = windowSize - avail
-               }
-       }
        // "The legal range for the increment to the flow control
        // window is 1 to 2^31-1 (2,147,483,647) octets."
        // A Go Read call on 64-bit machines could in theory read
        // a larger Read than this. Very unlikely, but we handle it here
        // rather than elsewhere for now.
        const maxUint31 = 1<<31 - 1
-       for n >= maxUint31 {
+       for n > maxUint31 {
                sc.sendWindowUpdate32(st, maxUint31)
                n -= maxUint31
        }
@@ -2474,7 +2486,15 @@
 
 type chunkWriter struct{ rws *responseWriterState }
 
-func (cw chunkWriter) Write(p []byte) (n int, err error) { return 
cw.rws.writeChunk(p) }
+func (cw chunkWriter) Write(p []byte) (n int, err error) {
+       n, err = cw.rws.writeChunk(p)
+       if err == errStreamClosed {
+               // If writing failed because the stream has been closed,
+               // return the reason it was closed.
+               err = cw.rws.stream.closeErr
+       }
+       return n, err
+}
 
 func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) 
> 0 }
 
@@ -2668,23 +2688,85 @@
        }
 }
 
+func (w *responseWriter) SetReadDeadline(deadline time.Time) error {
+       st := w.rws.stream
+       if !deadline.IsZero() && deadline.Before(time.Now()) {
+               // If we're setting a deadline in the past, reset the stream 
immediately
+               // so writes after SetWriteDeadline returns will fail.
+               st.onReadTimeout()
+               return nil
+       }
+       w.rws.conn.sendServeMsg(func(sc *serverConn) {
+               if st.readDeadline != nil {
+                       if !st.readDeadline.Stop() {
+                               // Deadline already exceeded, or stream has 
been closed.
+                               return
+                       }
+               }
+               if deadline.IsZero() {
+                       st.readDeadline = nil
+               } else if st.readDeadline == nil {
+                       st.readDeadline = 
time.AfterFunc(deadline.Sub(time.Now()), st.onReadTimeout)
+               } else {
+                       st.readDeadline.Reset(deadline.Sub(time.Now()))
+               }
+       })
+       return nil
+}
+
+func (w *responseWriter) SetWriteDeadline(deadline time.Time) error {
+       st := w.rws.stream
+       if !deadline.IsZero() && deadline.Before(time.Now()) {
+               // If we're setting a deadline in the past, reset the stream 
immediately
+               // so writes after SetWriteDeadline returns will fail.
+               st.onWriteTimeout()
+               return nil
+       }
+       w.rws.conn.sendServeMsg(func(sc *serverConn) {
+               if st.writeDeadline != nil {
+                       if !st.writeDeadline.Stop() {
+                               // Deadline already exceeded, or stream has 
been closed.
+                               return
+                       }
+               }
+               if deadline.IsZero() {
+                       st.writeDeadline = nil
+               } else if st.writeDeadline == nil {
+                       st.writeDeadline = 
time.AfterFunc(deadline.Sub(time.Now()), st.onWriteTimeout)
+               } else {
+                       st.writeDeadline.Reset(deadline.Sub(time.Now()))
+               }
+       })
+       return nil
+}
+
 func (w *responseWriter) Flush() {
+       w.FlushError()
+}
+
+func (w *responseWriter) FlushError() error {
        rws := w.rws
        if rws == nil {
                panic("Header called after Handler finished")
        }
+       var err error
        if rws.bw.Buffered() > 0 {
-               if err := rws.bw.Flush(); err != nil {
-                       // Ignore the error. The frame writer already knows.
-                       return
-               }
+               err = rws.bw.Flush()
        } else {
                // The bufio.Writer won't call chunkWriter.Write
                // (writeChunk with zero bytes, so we have to do it
                // ourselves to force the HTTP response header and/or
                // final DATA frame (with END_STREAM) to be sent.
-               rws.writeChunk(nil)
+               _, err = chunkWriter{rws}.Write(nil)
+               if err == nil {
+                       select {
+                       case <-rws.stream.cw:
+                               err = rws.stream.closeErr
+                       default:
+                       }
+               }
        }
+       return err
 }
 
 func (w *responseWriter) CloseNotify() <-chan bool {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/golang.org/x/net/http2/transport.go 
new/vendor/golang.org/x/net/http2/transport.go
--- old/vendor/golang.org/x/net/http2/transport.go      2022-10-25 
20:45:32.000000000 +0200
+++ new/vendor/golang.org/x/net/http2/transport.go      2022-11-15 
10:21:46.000000000 +0100
@@ -16,6 +16,7 @@
        "errors"
        "fmt"
        "io"
+       "io/fs"
        "log"
        "math"
        mathrand "math/rand"
@@ -501,6 +502,15 @@
        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.
 func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) 
(*http.Response, error) {
        if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && 
t.AllowHTTP)) {
@@ -526,11 +536,14 @@
                                }
                                backoff := float64(uint(1) << (uint(retry) - 1))
                                backoff += backoff * (0.1 * mathrand.Float64())
+                               d := time.Second * time.Duration(backoff)
+                               timer := backoffNewTimer(d)
                                select {
-                               case <-time.After(time.Second * 
time.Duration(backoff)):
+                               case <-timer.C:
                                        t.vlogf("RoundTrip retrying after 
failure: %v", err)
                                        continue
                                case <-req.Context().Done():
+                                       timer.Stop()
                                        err = req.Context().Err()
                                }
                        }
@@ -1075,7 +1088,7 @@
 func commaSeparatedTrailers(req *http.Request) (string, error) {
        keys := make([]string, 0, len(req.Trailer))
        for k := range req.Trailer {
-               k = http.CanonicalHeaderKey(k)
+               k = canonicalHeader(k)
                switch k {
                case "Transfer-Encoding", "Trailer", "Content-Length":
                        return "", fmt.Errorf("invalid Trailer key %q", k)
@@ -1612,7 +1625,7 @@
 
        var sawEOF bool
        for !sawEOF {
-               n, err := body.Read(buf[:len(buf)])
+               n, err := body.Read(buf)
                if hasContentLen {
                        remainLen -= int64(n)
                        if remainLen == 0 && err == nil {
@@ -1915,7 +1928,7 @@
 
        // Header list size is ok. Write the headers.
        enumerateHeaders(func(name, value string) {
-               name, ascii := asciiToLower(name)
+               name, ascii := lowerHeader(name)
                if !ascii {
                        // Skip writing invalid headers. Per RFC 7540, Section 
8.1.2, header
                        // field names have to be ASCII characters (just as in 
HTTP/1.x).
@@ -1968,7 +1981,7 @@
        }
 
        for k, vv := range trailer {
-               lowKey, ascii := asciiToLower(k)
+               lowKey, ascii := lowerHeader(k)
                if !ascii {
                        // Skip writing invalid headers. Per RFC 7540, Section 
8.1.2, header
                        // field names have to be ASCII characters (just as in 
HTTP/1.x).
@@ -2301,7 +2314,7 @@
                Status:     status + " " + http.StatusText(statusCode),
        }
        for _, hf := range regularFields {
-               key := http.CanonicalHeaderKey(hf.Name)
+               key := canonicalHeader(hf.Name)
                if key == "Trailer" {
                        t := res.Trailer
                        if t == nil {
@@ -2309,7 +2322,7 @@
                                res.Trailer = t
                        }
                        foreachHeaderElement(hf.Value, func(v string) {
-                               t[http.CanonicalHeaderKey(v)] = nil
+                               t[canonicalHeader(v)] = nil
                        })
                } else {
                        vv := header[key]
@@ -2414,7 +2427,7 @@
 
        trailer := make(http.Header)
        for _, hf := range f.RegularFields() {
-               key := http.CanonicalHeaderKey(hf.Name)
+               key := canonicalHeader(hf.Name)
                trailer[key] = append(trailer[key], hf.Value)
        }
        cs.trailer = trailer
@@ -2985,7 +2998,11 @@
 }
 
 func (gz *gzipReader) Close() error {
-       return gz.body.Close()
+       if err := gz.body.Close(); err != nil {
+               return err
+       }
+       gz.zerr = fs.ErrClosed
+       return nil
 }
 
 type errorReader struct{ err error }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/golang.org/x/sys/unix/sockcmsg_unix.go 
new/vendor/golang.org/x/sys/unix/sockcmsg_unix.go
--- old/vendor/golang.org/x/sys/unix/sockcmsg_unix.go   2022-10-25 
20:45:32.000000000 +0200
+++ new/vendor/golang.org/x/sys/unix/sockcmsg_unix.go   2022-11-15 
10:21:46.000000000 +0100
@@ -52,6 +52,20 @@
        return msgs, nil
 }
 
+// ParseOneSocketControlMessage parses a single socket control message from b, 
returning the message header,
+// message data (a slice of b), and the remainder of b after that single 
message.
+// When there are no remaining messages, len(remainder) == 0.
+func ParseOneSocketControlMessage(b []byte) (hdr Cmsghdr, data []byte, 
remainder []byte, err error) {
+       h, dbuf, err := socketControlMessageHeaderAndData(b)
+       if err != nil {
+               return Cmsghdr{}, nil, nil, err
+       }
+       if i := cmsgAlignOf(int(h.Len)); i < len(b) {
+               remainder = b[i:]
+       }
+       return *h, dbuf, remainder, nil
+}
+
 func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) {
        h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
        if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/golang.org/x/sys/unix/syscall_linux.go 
new/vendor/golang.org/x/sys/unix/syscall_linux.go
--- old/vendor/golang.org/x/sys/unix/syscall_linux.go   2022-10-25 
20:45:32.000000000 +0200
+++ new/vendor/golang.org/x/sys/unix/syscall_linux.go   2022-11-15 
10:21:46.000000000 +0100
@@ -1554,6 +1554,7 @@
                                var iova [1]Iovec
                                iova[0].Base = &dummy
                                iova[0].SetLen(1)
+                               iov = iova[:]
                        }
                }
                msg.Control = &oob[0]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt
--- old/vendor/modules.txt      2022-10-25 20:45:32.000000000 +0200
+++ new/vendor/modules.txt      2022-11-15 10:21:46.000000000 +0100
@@ -1,4 +1,4 @@
-# fortio.org/assert v1.1.0
+# fortio.org/assert v1.1.2
 ## explicit; go 1.18
 fortio.org/assert
 # github.com/fsnotify/fsnotify v1.6.0
@@ -15,10 +15,10 @@
 # github.com/google/uuid v1.3.0
 ## explicit
 github.com/google/uuid
-# golang.org/x/exp v0.0.0-20221012211006-4de253d81b95
+# golang.org/x/exp v0.0.0-20221111204811-129d8d6c17ab
 ## explicit; go 1.18
 golang.org/x/exp/constraints
-# golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458
+# golang.org/x/net v0.2.0
 ## explicit; go 1.17
 golang.org/x/net/context
 golang.org/x/net/http/httpguts
@@ -28,12 +28,12 @@
 golang.org/x/net/idna
 golang.org/x/net/internal/timeseries
 golang.org/x/net/trace
-# golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43
+# golang.org/x/sys v0.2.0
 ## explicit; go 1.17
 golang.org/x/sys/internal/unsafeheader
 golang.org/x/sys/unix
 golang.org/x/sys/windows
-# golang.org/x/text v0.3.8
+# golang.org/x/text v0.4.0
 ## explicit; go 1.17
 golang.org/x/text/secure/bidirule
 golang.org/x/text/transform

Reply via email to