Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
13d446f194 | |||
9fb0843d80 | |||
b599a971d4 | |||
6a59f55f97 | |||
8e6b7edec9 | |||
67dc92e476 | |||
f97c9cfc61 | |||
0c2083aed6 | |||
49aaa38f82 | |||
156cc5204d |
@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:1.19-alpine AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
ARG BUILDPLATFORM
|
||||
|
138
build-docker.sh
138
build-docker.sh
@ -1,134 +1,50 @@
|
||||
#! /bin/bash
|
||||
|
||||
IMAGE_NAME=robocar-steering
|
||||
BINARY_NAME=rc-steering
|
||||
TAG=$(git describe)
|
||||
FULL_IMAGE_NAME=docker.io/cyrilix/${IMAGE_NAME}:${TAG}
|
||||
OPENCV_VERSION=4.6.0
|
||||
SRC_CMD=./cmd/$BINARY_NAME
|
||||
GOLANG_VERSION=1.19
|
||||
BINARY=rc-steering
|
||||
|
||||
GOTAGS="-tags netgo"
|
||||
|
||||
image_build(){
|
||||
local containerName=builder
|
||||
|
||||
GOPATH=/go
|
||||
|
||||
buildah from --name ${containerName} docker.io/cyrilix/opencv-buildstage:${OPENCV_VERSION}
|
||||
buildah config --label maintainer="Cyrille Nofficial" "${containerName}"
|
||||
|
||||
buildah copy --from=docker.io/library/golang:${GOLANG_VERSION} "${containerName}" /usr/local/go /usr/local/go
|
||||
buildah config --env GOPATH=/go \
|
||||
--env PATH=/usr/local/go/bin:$GOPATH/bin:/usr/local/go/bin:/usr/bin:/bin \
|
||||
"${containerName}"
|
||||
|
||||
buildah run \
|
||||
--env GOPATH=${GOPATH} \
|
||||
"${containerName}" \
|
||||
mkdir -p /src "$GOPATH/src" "$GOPATH/bin"
|
||||
|
||||
buildah run \
|
||||
--env GOPATH=${GOPATH} \
|
||||
"${containerName}" \
|
||||
chmod -R 777 "$GOPATH"
|
||||
local platform=$1
|
||||
|
||||
|
||||
buildah config --env PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/lib64/pkgconfig "${containerName}"
|
||||
buildah config --workingdir /src/ "${containerName}"
|
||||
GOOS=$(echo $platform | cut -f1 -d/) && \
|
||||
GOARCH=$(echo $platform | cut -f2 -d/) && \
|
||||
GOARM=$(echo $platform | cut -f3 -d/ | sed "s/v//" )
|
||||
VARIANT="--variant $(echo $platform | cut -f3 -d/ )"
|
||||
if [[ -z "$GOARM" ]] ;
|
||||
then
|
||||
VARIANT=""
|
||||
fi
|
||||
|
||||
buildah add "${containerName}" . .
|
||||
local binary_suffix="$GOARCH$(echo $platform | cut -f3 -d/ )"
|
||||
|
||||
for platform in "linux/amd64" "linux/arm64" "linux/arm/v7"
|
||||
do
|
||||
local containerName="$IMAGE_NAME-$GOARCH$GOARM"
|
||||
|
||||
GOOS=$(echo "$platform" | cut -f1 -d/) && \
|
||||
GOARCH=$(echo "$platform" | cut -f2 -d/) && \
|
||||
GOARM=$(echo "$platform" | cut -f3 -d/ | sed "s/v//" )
|
||||
|
||||
case $GOARCH in
|
||||
"amd64")
|
||||
ARCH=amd64
|
||||
ARCH_LIB_DIR=/usr/lib/x86_64-linux-gnu
|
||||
EXTRA_LIBS=""
|
||||
CC=gcc
|
||||
CXX=g++
|
||||
;;
|
||||
"arm64")
|
||||
ARCH=arm64
|
||||
ARCH_LIB_DIR=/usr/lib/aarch64-linux-gnu
|
||||
EXTRA_LIBS="-ltbb"
|
||||
CC=aarch64-linux-gnu-gcc
|
||||
CXX=aarch64-linux-gnu-g++
|
||||
;;
|
||||
"arm")
|
||||
ARCH=armhf
|
||||
ARCH_LIB_DIR=/usr/lib/arm-linux-gnueabihf
|
||||
EXTRA_LIBS="-ltbb"
|
||||
CC=arm-linux-gnueabihf-gcc
|
||||
CXX=arm-linux-gnueabihf-g++
|
||||
;;
|
||||
esac
|
||||
printf "\n\nBuild go binary %s\n\n" "${BINARY}.${binary_suffix}"
|
||||
mkdir -p build
|
||||
CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} GOARM=${GOARM} go build -mod vendor -a ${GOTAGS} -o "build/${BINARY}.${binary_suffix}" ./cmd/${BINARY}/
|
||||
|
||||
printf "Build binary for %s\n\n" "${platform}"
|
||||
buildah --os "$GOOS" --arch "$GOARCH" $VARIANT --name "$containerName" from gcr.io/distroless/static
|
||||
buildah config --user 1234 "$containerName"
|
||||
buildah copy "$containerName" "build/${BINARY}.${binary_suffix}" /bin/$BINARY
|
||||
buildah config --entrypoint '["/bin/'$BINARY'"]' "${containerName}"
|
||||
|
||||
buildah run \
|
||||
--env CGO_ENABLED=1 \
|
||||
--env CC=${CC} \
|
||||
--env CXX=${CXX} \
|
||||
--env GOOS=${GOOS} \
|
||||
--env GOARCH=${GOARCH} \
|
||||
--env GOARM=${GOARM} \
|
||||
--env CGO_CPPFLAGS="-I/opt/opencv/${ARCH}/include/opencv4/" \
|
||||
--env CGO_LDFLAGS="-L/opt/opencv/${ARCH}/lib -L${ARCH_LIB_DIR} ${EXTRA_LIBS} -lopencv_core -lopencv_face -lopencv_videoio -lopencv_imgproc -lopencv_highgui -lopencv_imgcodecs -lopencv_objdetect -lopencv_features2d -lopencv_video -lopencv_dnn -lopencv_xfeatures2d -lopencv_calib3d -lopencv_photo -lopencv_flann" \
|
||||
--env CGO_CXXFLAGS="--std=c++1z" \
|
||||
"${containerName}" \
|
||||
go build -tags customenv -a -o ${BINARY_NAME}.${ARCH} ${SRC_CMD}
|
||||
|
||||
done
|
||||
buildah commit --rm ${containerName} ${IMAGE_NAME}-builder
|
||||
}
|
||||
|
||||
image_final(){
|
||||
local containerName=runtime
|
||||
|
||||
for platform in "linux/amd64" "linux/arm64" "linux/arm/v7"
|
||||
do
|
||||
|
||||
GOOS=$(echo $platform | cut -f1 -d/) && \
|
||||
GOARCH=$(echo $platform | cut -f2 -d/) && \
|
||||
GOARM=$(echo $platform | cut -f3 -d/ | sed "s/v//" )
|
||||
VARIANT="--variant $(echo $platform | cut -f3 -d/ )"
|
||||
|
||||
if [[ -z "$GOARM" ]] ;
|
||||
then
|
||||
VARIANT=""
|
||||
fi
|
||||
|
||||
if [[ "${GOARCH}" == "arm" ]]
|
||||
then
|
||||
BINARY="${BINARY_NAME}.armhf"
|
||||
else
|
||||
BINARY="${BINARY_NAME}.${GOARCH}"
|
||||
fi
|
||||
|
||||
buildah from --name "${containerName}" --os "${GOOS}" --arch "${GOARCH}" ${VARIANT} docker.io/cyrilix/opencv-runtime:${OPENCV_VERSION}
|
||||
|
||||
buildah copy --from ${IMAGE_NAME}-builder "$containerName" "/src/${BINARY}" /usr/local/bin/${BINARY_NAME}
|
||||
|
||||
buildah config --label maintainer="Cyrille Nofficial" "${containerName}"
|
||||
buildah config --user 1234 "$containerName"
|
||||
buildah config --cmd '' "$containerName"
|
||||
buildah config --entrypoint '[ "/usr/local/bin/'${BINARY_NAME}'" ]' "$containerName"
|
||||
|
||||
buildah commit --rm --manifest ${IMAGE_NAME} ${containerName}
|
||||
done
|
||||
buildah commit --rm --manifest $IMAGE_NAME "${containerName}" "${containerName}"
|
||||
}
|
||||
|
||||
buildah rmi localhost/$IMAGE_NAME
|
||||
buildah manifest rm localhost/${IMAGE_NAME}
|
||||
|
||||
image_build
|
||||
image_build linux/amd64
|
||||
image_build linux/arm64
|
||||
image_build linux/arm/v7
|
||||
|
||||
|
||||
# push image
|
||||
image_final
|
||||
printf "\n\nPush manifest to %s\n\n" ${FULL_IMAGE_NAME}
|
||||
buildah manifest push --rm -f v2s2 "localhost/$IMAGE_NAME" "docker://$FULL_IMAGE_NAME" --all
|
||||
buildah manifest push --rm -f v2s2 "localhost/$IMAGE_NAME" "docker://$FULL_IMAGE_NAME" --all
|
||||
|
@ -16,7 +16,6 @@ const (
|
||||
func main() {
|
||||
var mqttBroker, username, password, clientId string
|
||||
var steeringTopic, driveModeTopic, rcSteeringTopic, tfSteeringTopic, objectsTopic string
|
||||
var imgWidth, imgHeight int
|
||||
var enableObjectsCorrection, enableObjectsCorrectionOnUserMode bool
|
||||
var gridMapConfig, objectsMoveFactorsConfig string
|
||||
var deltaMiddle float64
|
||||
@ -31,8 +30,6 @@ func main() {
|
||||
flag.StringVar(&tfSteeringTopic, "mqtt-topic-tf-steering", os.Getenv("MQTT_TOPIC_TF_STEERING"), "Mqtt topic that contains tenorflow steering value, use MQTT_TOPIC_TF_STEERING if args not set")
|
||||
flag.StringVar(&driveModeTopic, "mqtt-topic-drive-mode", os.Getenv("MQTT_TOPIC_DRIVE_MODE"), "Mqtt topic that contains DriveMode value, use MQTT_TOPIC_DRIVE_MODE if args not set")
|
||||
flag.StringVar(&objectsTopic, "mqtt-topic-objects", os.Getenv("MQTT_TOPIC_OBJECTS"), "Mqtt topic that contains Objects from object detection value, use MQTT_TOPIC_OBJECTS if args not set")
|
||||
flag.IntVar(&imgWidth, "image-width", 160, "Video pixels width")
|
||||
flag.IntVar(&imgHeight, "image-height", 128, "Video pixels height")
|
||||
flag.BoolVar(&enableObjectsCorrection, "enable-objects-correction", false, "Adjust steering to avoid objects")
|
||||
flag.BoolVar(&enableObjectsCorrectionOnUserMode, "enable-objects-correction-user", false, "Adjust steering to avoid objects on user mode driving")
|
||||
flag.StringVar(&gridMapConfig, "grid-map-config", "", "Json file path to configure grid object correction")
|
||||
@ -69,7 +66,6 @@ func main() {
|
||||
zap.S().Infof("objects correction on user mode : %v", enableObjectsCorrectionOnUserMode)
|
||||
zap.S().Infof("grid map file config : %v", gridMapConfig)
|
||||
zap.S().Infof("objects move factors grid config: %v", objectsMoveFactorsConfig)
|
||||
zap.S().Infof("image width x height : %v x %v", imgWidth, imgHeight)
|
||||
|
||||
client, err := cli.Connect(mqttBroker, username, password, clientId)
|
||||
if err != nil {
|
||||
@ -85,7 +81,6 @@ func main() {
|
||||
steering.WidthDeltaMiddle(deltaMiddle),
|
||||
steering.WithGridMap(gridMapConfig),
|
||||
steering.WithObjectMoveFactors(objectsMoveFactorsConfig),
|
||||
steering.WithImageSize(imgWidth, imgHeight),
|
||||
),
|
||||
),
|
||||
steering.WithObjectsCorrectionEnabled(enableObjectsCorrection, enableObjectsCorrectionOnUserMode),
|
||||
|
23
go.mod
23
go.mod
@ -1,21 +1,18 @@
|
||||
module github.com/cyrilix/robocar-steering
|
||||
|
||||
go 1.19
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/cyrilix/robocar-base v0.1.7
|
||||
github.com/cyrilix/robocar-protobuf/go v1.1.0
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.1
|
||||
go.uber.org/zap v1.21.0
|
||||
gocv.io/x/gocv v0.31.0
|
||||
google.golang.org/protobuf v1.28.0
|
||||
github.com/cyrilix/robocar-base v0.1.8
|
||||
github.com/cyrilix/robocar-protobuf/go v1.5.2
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.3
|
||||
go.uber.org/zap v1.27.0
|
||||
google.golang.org/protobuf v1.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
)
|
||||
|
102
go.sum
102
go.sum
@ -1,84 +1,32 @@
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/cyrilix/robocar-base v0.1.7 h1:EVzZ0KjigSFpke5f3A/PybEH3WFUEIrYSc3z/dhOZ48=
|
||||
github.com/cyrilix/robocar-base v0.1.7/go.mod h1:4E11HQSNy2NT8e7MW188y6ST9C0RzarKyn7sK/3V/Lk=
|
||||
github.com/cyrilix/robocar-protobuf/go v1.1.0 h1:txIjGnnCF3UzedpsWu+sL7nMA+pNjSnX6HZlAmuReH4=
|
||||
github.com/cyrilix/robocar-protobuf/go v1.1.0/go.mod h1:Y3AE28K5V7EZxMXp/6A8RhkRz15VOfFy4CjST35FbtQ=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/cyrilix/robocar-base v0.1.8 h1:9hfH9rCcyGXR0dtESIhI9tCrK9juq+dSnJXCxCF2LVw=
|
||||
github.com/cyrilix/robocar-base v0.1.8/go.mod h1:oJnfYjoz2PX16BD8I8LJ14kRQt1zbFb7XaUHtUEZgjg=
|
||||
github.com/cyrilix/robocar-protobuf/go v1.5.2 h1:2JvVYCl+WD0OpJOuymPtANhBhyn4EA/MejPvMPW7v9Y=
|
||||
github.com/cyrilix/robocar-protobuf/go v1.5.2/go.mod h1:52tSzcJNtSdoU9jBNbdmOWN2Herc6hN+Yyebg4rc06w=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.1 h1:tUSpviiL5G3P9SZZJPC4ZULZJsxQKXxfENpMvdbAXAI=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.1/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hybridgroup/mjpeg v0.0.0-20140228234708-4680f319790e/go.mod h1:eagM805MRKrioHYuU7iKLUyFPVKqVV6um5DAvCkUtXs=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
gocv.io/x/gocv v0.31.0 h1:BHDtK8v+YPvoSPQTTiZB2fM/7BLg6511JqkruY2z6LQ=
|
||||
gocv.io/x/gocv v0.31.0/go.mod h1:oc6FvfYqfBp99p+yOEzs9tbYF9gOrAQSeL/dyIPefJU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -1,57 +0,0 @@
|
||||
package steering
|
||||
|
||||
import (
|
||||
"github.com/cyrilix/robocar-protobuf/go/events"
|
||||
"gocv.io/x/gocv"
|
||||
"image"
|
||||
)
|
||||
|
||||
func GroupBBoxes(bboxes []image.Rectangle) []image.Rectangle {
|
||||
if len(bboxes) == 0 {
|
||||
return []image.Rectangle{}
|
||||
}
|
||||
if len(bboxes) == 1 {
|
||||
return []image.Rectangle{bboxes[0]}
|
||||
}
|
||||
return gocv.GroupRectangles(bboxes, 1, 0.2)
|
||||
}
|
||||
func GroupObjects(objects []*events.Object, imgWidth, imgHeight int) []*events.Object {
|
||||
if len(objects) == 0 {
|
||||
return []*events.Object{}
|
||||
}
|
||||
if len(objects) == 1 {
|
||||
return []*events.Object{objects[0]}
|
||||
}
|
||||
|
||||
rectangles := make([]image.Rectangle, 0, len(objects))
|
||||
for _, o := range objects {
|
||||
rectangles = append(rectangles, *objectToRect(o, imgWidth, imgHeight))
|
||||
}
|
||||
grp := gocv.GroupRectangles(rectangles, 1, 0.2)
|
||||
result := make([]*events.Object, 0, len(grp))
|
||||
for _, r := range grp {
|
||||
result = append(result, rectToObject(&r, imgWidth, imgHeight))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func objectToRect(object *events.Object, imgWidth, imgHeight int) *image.Rectangle {
|
||||
r := image.Rect(
|
||||
int(object.Left*float32(imgWidth)),
|
||||
int(object.Top*float32(imgHeight)),
|
||||
int(object.Right*float32(imgWidth)),
|
||||
int(object.Bottom*float32(imgHeight)),
|
||||
)
|
||||
return &r
|
||||
}
|
||||
|
||||
func rectToObject(r *image.Rectangle, imgWidth, imgHeight int) *events.Object {
|
||||
return &events.Object{
|
||||
Type: events.TypeObject_ANY,
|
||||
Left: float32(r.Min.X) / float32(imgWidth),
|
||||
Top: float32(r.Min.Y) / float32(imgHeight),
|
||||
Right: float32(r.Max.X) / float32(imgWidth),
|
||||
Bottom: float32(r.Max.Y) / float32(imgHeight),
|
||||
Confidence: -1,
|
||||
}
|
||||
}
|
@ -1,304 +0,0 @@
|
||||
package steering
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cyrilix/robocar-protobuf/go/events"
|
||||
"go.uber.org/zap"
|
||||
"gocv.io/x/gocv"
|
||||
"image"
|
||||
"image/color"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type ObjectsList struct {
|
||||
BBoxes []BBox `json:"bboxes"`
|
||||
}
|
||||
type BBox struct {
|
||||
Left float32 `json:"left"`
|
||||
Top float32 `json:"top"`
|
||||
Bottom float32 `json:"bottom"`
|
||||
Right float32 `json:"right"`
|
||||
Confidence float32 `json:"confidence"`
|
||||
}
|
||||
|
||||
var (
|
||||
dataBBoxes map[string][]image.Rectangle
|
||||
dataObjects map[string][]*events.Object
|
||||
dataImages map[string]*gocv.Mat
|
||||
)
|
||||
|
||||
func init() {
|
||||
// TODO: empty img without bbox
|
||||
dataNames := []string{"01", "02", "03", "04"}
|
||||
dataBBoxes = make(map[string][]image.Rectangle, len(dataNames))
|
||||
dataObjects = make(map[string][]*events.Object, len(dataNames))
|
||||
dataImages = make(map[string]*gocv.Mat, len(dataNames))
|
||||
|
||||
for _, dataName := range dataNames {
|
||||
img, bb, err := loadData(dataName)
|
||||
if err != nil {
|
||||
zap.S().Panicf("unable to load data test: %v", err)
|
||||
}
|
||||
dataBBoxes[dataName] = bboxesToRectangles(bb, img.Cols(), img.Rows())
|
||||
dataObjects[dataName] = bboxesToObjects(bb)
|
||||
dataImages[dataName] = img
|
||||
}
|
||||
}
|
||||
|
||||
func bboxesToRectangles(bboxes []BBox, imgWidth, imgHeiht int) []image.Rectangle {
|
||||
rects := make([]image.Rectangle, 0, len(bboxes))
|
||||
for _, bb := range bboxes {
|
||||
rects = append(rects, bb.toRect(imgWidth, imgHeiht))
|
||||
}
|
||||
return rects
|
||||
}
|
||||
|
||||
func bboxesToObjects(bboxes []BBox) []*events.Object {
|
||||
objects := make([]*events.Object, 0, len(bboxes))
|
||||
for _, bb := range bboxes {
|
||||
objects = append(objects, &events.Object{
|
||||
Type: events.TypeObject_ANY,
|
||||
Left: bb.Left,
|
||||
Top: bb.Top,
|
||||
Right: bb.Right,
|
||||
Bottom: bb.Bottom,
|
||||
Confidence: bb.Confidence,
|
||||
})
|
||||
}
|
||||
return objects
|
||||
}
|
||||
func (bb *BBox) toRect(imgWidth, imgHeight int) image.Rectangle {
|
||||
return image.Rect(
|
||||
int(bb.Left*float32(imgWidth)),
|
||||
int(bb.Top*float32(imgHeight)),
|
||||
int(bb.Right*float32(imgWidth)),
|
||||
int(bb.Bottom*float32(imgHeight)),
|
||||
)
|
||||
}
|
||||
|
||||
func loadData(dataName string) (*gocv.Mat, []BBox, error) {
|
||||
contentBBoxes, err := os.ReadFile(fmt.Sprintf("test_data/bboxes-%s.json", dataName))
|
||||
if err != nil {
|
||||
return nil, []BBox{}, fmt.Errorf("unable to load json file for bbox of '%v': %w", dataName, err)
|
||||
}
|
||||
|
||||
var obj ObjectsList
|
||||
err = json.Unmarshal(contentBBoxes, &obj)
|
||||
if err != nil {
|
||||
return nil, []BBox{}, fmt.Errorf("unable to unmarsh json file for bbox of '%v': %w", dataName, err)
|
||||
}
|
||||
|
||||
imgContent, err := os.ReadFile(fmt.Sprintf("test_data/img-%s.jpg", dataName))
|
||||
if err != nil {
|
||||
return nil, []BBox{}, fmt.Errorf("unable to load jpg file of '%v': %w", dataName, err)
|
||||
}
|
||||
img, err := gocv.IMDecode(imgContent, gocv.IMReadUnchanged)
|
||||
if err != nil {
|
||||
return nil, []BBox{}, fmt.Errorf("unable to load jpg of '%v': %w", dataName, err)
|
||||
}
|
||||
return &img, obj.BBoxes, nil
|
||||
}
|
||||
|
||||
func drawImage(img *gocv.Mat, bboxes []BBox) {
|
||||
for _, bb := range bboxes {
|
||||
gocv.Rectangle(img, bb.toRect(img.Cols(), img.Rows()), color.RGBA{R: 0, G: 255, B: 0, A: 0}, 2)
|
||||
gocv.PutText(
|
||||
img,
|
||||
fmt.Sprintf("%.2f", bb.Confidence),
|
||||
image.Point{
|
||||
X: int(bb.Left*float32(img.Cols()) + 10.),
|
||||
Y: int(bb.Top*float32(img.Rows()) + 10.),
|
||||
},
|
||||
gocv.FontHersheyTriplex,
|
||||
0.4,
|
||||
color.RGBA{R: 0, G: 0, B: 0, A: 0},
|
||||
1)
|
||||
}
|
||||
}
|
||||
|
||||
func drawRectangles(img *gocv.Mat, rects []image.Rectangle, c color.RGBA) {
|
||||
for _, r := range rects {
|
||||
gocv.Rectangle(img, r, c, 2)
|
||||
}
|
||||
}
|
||||
|
||||
func saveImage(name string, img *gocv.Mat) error {
|
||||
err := os.MkdirAll("test_result", os.ModePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create directory for test result: %w", err)
|
||||
}
|
||||
jpg, err := gocv.IMEncode(gocv.JPEGFileExt, *img)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to encode jpg image: %w", err)
|
||||
}
|
||||
defer jpg.Close()
|
||||
|
||||
err = os.WriteFile(fmt.Sprintf("test_result/%s.jpg", name), jpg.GetBytes(), os.ModePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write jpeg file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DisplayImageAndBBoxes(dataName string) error {
|
||||
img, bboxes, err := loadData(dataName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load image and bboxes: %w", err)
|
||||
}
|
||||
drawImage(img, bboxes)
|
||||
err = saveImage(dataName, img)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to save image: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestDisplayBBox(t *testing.T) {
|
||||
|
||||
type args struct {
|
||||
dataName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
args: args{dataName: "01"},
|
||||
},
|
||||
{
|
||||
name: "02",
|
||||
args: args{dataName: "02"},
|
||||
},
|
||||
{
|
||||
name: "03",
|
||||
args: args{dataName: "03"},
|
||||
},
|
||||
{
|
||||
name: "04",
|
||||
args: args{dataName: "04"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := DisplayImageAndBBoxes(tt.args.dataName)
|
||||
if err != nil {
|
||||
t.Errorf("unable to draw image: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGroupBBoxes(t *testing.T) {
|
||||
type args struct {
|
||||
dataName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []image.Rectangle
|
||||
}{
|
||||
{
|
||||
name: "groupbbox-01",
|
||||
args: args{
|
||||
dataName: "01",
|
||||
},
|
||||
want: []image.Rectangle{{Min: image.Point{X: 42, Y: 20}, Max: image.Point{X: 84, Y: 57}}},
|
||||
},
|
||||
{
|
||||
name: "groupbbox-02",
|
||||
args: args{
|
||||
dataName: "02",
|
||||
},
|
||||
want: []image.Rectangle{{Min: image.Point{X: 25, Y: 13}, Max: image.Point{X: 110, Y: 80}}},
|
||||
},
|
||||
{
|
||||
name: "groupbbox-03",
|
||||
args: args{
|
||||
dataName: "03",
|
||||
},
|
||||
want: []image.Rectangle{{Min: image.Point{X: 0, Y: 17}, Max: image.Point{X: 35, Y: 77}}},
|
||||
},
|
||||
{
|
||||
name: "groupbbox-04",
|
||||
args: args{
|
||||
dataName: "04",
|
||||
},
|
||||
want: []image.Rectangle{{Min: image.Point{X: 129, Y: 10}, Max: image.Point{X: 159, Y: 64}}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := GroupBBoxes(dataBBoxes[tt.args.dataName])
|
||||
img := gocv.NewMat()
|
||||
defer img.Close()
|
||||
dataImages[tt.args.dataName].CopyTo(&img)
|
||||
drawRectangles(&img, got, color.RGBA{R: 0, G: 0, B: 255, A: 0})
|
||||
saveImage(tt.name, &img)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("GroupBBoxes() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestGroupObjects(t *testing.T) {
|
||||
type args struct {
|
||||
dataName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []*events.Object
|
||||
}{
|
||||
{
|
||||
name: "groupbbox-01",
|
||||
args: args{
|
||||
dataName: "01",
|
||||
},
|
||||
want: []*events.Object{
|
||||
{Left: 0.26660156, Top: 0.1706543, Right: 0.5258789, Bottom: 0.47583008, Confidence: 0.4482422},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "groupbbox-02",
|
||||
args: args{
|
||||
dataName: "02",
|
||||
},
|
||||
want: []*events.Object{
|
||||
{Left: 0.15625, Top: 0.108333334, Right: 0.6875, Bottom: 0.6666667, Confidence: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "groupbbox-03",
|
||||
args: args{
|
||||
dataName: "03",
|
||||
},
|
||||
want: []*events.Object{
|
||||
{Top: 0.14166667, Right: 0.21875, Bottom: 0.64166665, Confidence: -1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "groupbbox-04",
|
||||
args: args{
|
||||
dataName: "04",
|
||||
},
|
||||
want: []*events.Object{
|
||||
{Left: 0.80625, Top: 0.083333336, Right: 0.99375, Bottom: 0.53333336, Confidence: -1},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
img := dataImages[tt.args.dataName]
|
||||
got := GroupObjects(dataObjects[tt.args.dataName], img.Cols(), img.Rows())
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("GroupObjects() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -158,7 +158,7 @@ func (c *Controller) onRCSteering(_ mqtt.Client, message mqtt.Message) {
|
||||
func (c *Controller) onTFSteering(_ mqtt.Client, message mqtt.Message) {
|
||||
c.muDriveMode.RLock()
|
||||
defer c.muDriveMode.RUnlock()
|
||||
if c.driveMode != events.DriveMode_PILOT {
|
||||
if c.driveMode != events.DriveMode_PILOT && c.driveMode != events.DriveMode_COPILOT {
|
||||
// User mode, skip new message
|
||||
return
|
||||
}
|
||||
@ -202,7 +202,8 @@ func (c *Controller) Objects() []*events.Object {
|
||||
defer c.muObjects.RUnlock()
|
||||
res := make([]*events.Object, 0, len(c.objects))
|
||||
for _, o := range c.objects {
|
||||
res = append(res, o)
|
||||
oCpy := o
|
||||
res = append(res, oCpy)
|
||||
}
|
||||
zap.S().Debugf("copy object from %v to %v", c.objects, res)
|
||||
return res
|
||||
|
@ -65,6 +65,20 @@ func TestDefaultSteering(t *testing.T) {
|
||||
events.SteeringMessage{Steering: 0.7, Confidence: 1.0},
|
||||
events.ObjectsMessage{},
|
||||
},
|
||||
{
|
||||
events.DriveModeMessage{DriveMode: events.DriveMode_COPILOT},
|
||||
events.SteeringMessage{Steering: 0.5, Confidence: 1.0},
|
||||
events.SteeringMessage{Steering: 0.6, Confidence: 1.0},
|
||||
events.SteeringMessage{Steering: 0.6, Confidence: 1.0},
|
||||
events.ObjectsMessage{},
|
||||
},
|
||||
{
|
||||
events.DriveModeMessage{DriveMode: events.DriveMode_COPILOT},
|
||||
events.SteeringMessage{Steering: 0.4, Confidence: 1.0},
|
||||
events.SteeringMessage{Steering: 0.7, Confidence: 1.0},
|
||||
events.SteeringMessage{Steering: 0.7, Confidence: 1.0},
|
||||
events.ObjectsMessage{},
|
||||
},
|
||||
{
|
||||
events.DriveModeMessage{DriveMode: events.DriveMode_USER},
|
||||
events.SteeringMessage{Steering: 0.5, Confidence: 1.0},
|
||||
@ -214,6 +228,23 @@ func TestController_Start(t *testing.T) {
|
||||
// Get rc value without correction
|
||||
want: events.SteeringMessage{Steering: 0.4, Confidence: 1.0},
|
||||
},
|
||||
{
|
||||
name: "On copilot drive mode, none correction",
|
||||
fields: fields{
|
||||
driveMode: events.DriveMode_COPILOT,
|
||||
enableCorrection: false,
|
||||
enableCorrectionOnUser: false,
|
||||
},
|
||||
msgEvents: msgEvents{
|
||||
driveMode: events.DriveModeMessage{DriveMode: events.DriveMode_COPILOT},
|
||||
rcSteering: events.SteeringMessage{Steering: 0.3, Confidence: 1.0},
|
||||
tfSteering: events.SteeringMessage{Steering: 0.4, Confidence: 1.0},
|
||||
objects: events.ObjectsMessage{Objects: []*events.Object{&objectOnMiddleNear}},
|
||||
},
|
||||
correctionOnObject: 0.5,
|
||||
// Get rc value without correction
|
||||
want: events.SteeringMessage{Steering: 0.4, Confidence: 1.0},
|
||||
},
|
||||
{
|
||||
name: "On pilot drive mode, correction enabled",
|
||||
fields: fields{
|
||||
@ -248,6 +279,40 @@ func TestController_Start(t *testing.T) {
|
||||
// Get rc value without correction
|
||||
want: events.SteeringMessage{Steering: 0.5, Confidence: 1.0},
|
||||
},
|
||||
{
|
||||
name: "On copilot drive mode, correction enabled",
|
||||
fields: fields{
|
||||
driveMode: events.DriveMode_COPILOT,
|
||||
enableCorrection: true,
|
||||
enableCorrectionOnUser: false,
|
||||
},
|
||||
msgEvents: msgEvents{
|
||||
driveMode: events.DriveModeMessage{DriveMode: events.DriveMode_COPILOT},
|
||||
rcSteering: events.SteeringMessage{Steering: 0.3, Confidence: 1.0},
|
||||
tfSteering: events.SteeringMessage{Steering: 0.4, Confidence: 1.0},
|
||||
objects: events.ObjectsMessage{Objects: []*events.Object{&objectOnMiddleNear}},
|
||||
},
|
||||
correctionOnObject: 0.5,
|
||||
// Get rc value without correction
|
||||
want: events.SteeringMessage{Steering: 0.5, Confidence: 1.0},
|
||||
},
|
||||
{
|
||||
name: "On copilot drive mode, all corrections enabled",
|
||||
fields: fields{
|
||||
driveMode: events.DriveMode_COPILOT,
|
||||
enableCorrection: true,
|
||||
enableCorrectionOnUser: true,
|
||||
},
|
||||
msgEvents: msgEvents{
|
||||
driveMode: events.DriveModeMessage{DriveMode: events.DriveMode_COPILOT},
|
||||
rcSteering: events.SteeringMessage{Steering: 0.3, Confidence: 1.0},
|
||||
tfSteering: events.SteeringMessage{Steering: 0.4, Confidence: 1.0},
|
||||
objects: events.ObjectsMessage{Objects: []*events.Object{&objectOnMiddleNear}},
|
||||
},
|
||||
correctionOnObject: 0.5,
|
||||
// Get rc value without correction
|
||||
want: events.SteeringMessage{Steering: 0.5, Confidence: 1.0},
|
||||
},
|
||||
{
|
||||
name: "On user drive mode, only correction PILOT enabled",
|
||||
fields: fields{
|
||||
|
@ -60,26 +60,17 @@ func loadConfig(configPath string) (*GridMap, error) {
|
||||
return &gm, nil
|
||||
}
|
||||
|
||||
func WithImageSize(width, height int) OptionCorrector {
|
||||
return func(c *GridCorrector) {
|
||||
c.imgWidth = width
|
||||
c.imgHeight = height
|
||||
}
|
||||
}
|
||||
|
||||
func WidthDeltaMiddle(d float64) OptionCorrector {
|
||||
return func(c *GridCorrector) {
|
||||
c.deltaMiddle = d
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func NewGridCorrector(options ...OptionCorrector) *GridCorrector {
|
||||
c := &GridCorrector{
|
||||
gridMap: &defaultGridMap,
|
||||
objectMoveFactors: &defaultObjectFactors,
|
||||
deltaMiddle: 0.1,
|
||||
imgWidth: 160,
|
||||
imgHeight: 120,
|
||||
}
|
||||
for _, o := range options {
|
||||
o(c)
|
||||
@ -88,10 +79,9 @@ func NewGridCorrector(options ...OptionCorrector) *GridCorrector {
|
||||
}
|
||||
|
||||
type GridCorrector struct {
|
||||
gridMap *GridMap
|
||||
objectMoveFactors *GridMap
|
||||
deltaMiddle float64
|
||||
imgWidth, imgHeight int
|
||||
gridMap *GridMap
|
||||
objectMoveFactors *GridMap
|
||||
deltaMiddle float64
|
||||
}
|
||||
|
||||
/*
|
||||
@ -130,19 +120,16 @@ AdjustFromObjectPosition modify steering value according object positions
|
||||
40% |-----|-----|-----|-----|-----|-----|
|
||||
: | ... | ... | ... | ... | ... | ... |
|
||||
*/
|
||||
func (c *GridCorrector) AdjustFromObjectPosition(currentSteering float64, objects []*events.Object) float64 {
|
||||
func (c *GridCorrector) AdjustFromObjectPosition(currentSteering float64, objs []*events.Object) float64 {
|
||||
objects := objs
|
||||
|
||||
zap.S().Debugf("%v objects to avoid", len(objects))
|
||||
if len(objects) == 0 {
|
||||
return currentSteering
|
||||
}
|
||||
grpObjs := GroupObjects(objects, c.imgWidth, c.imgHeight)
|
||||
|
||||
// get nearest object
|
||||
nearest, err := c.nearObject(grpObjs)
|
||||
if err != nil {
|
||||
zap.S().Warnf("unexpected error on nearest search object, ignore objects: %v", err)
|
||||
return currentSteering
|
||||
}
|
||||
nearest := objs[0]
|
||||
|
||||
if currentSteering > -1*c.deltaMiddle && currentSteering < c.deltaMiddle {
|
||||
// Straight
|
||||
@ -195,24 +182,6 @@ func (c *GridCorrector) computeDeviation(nearest *events.Object) float64 {
|
||||
return delta
|
||||
}
|
||||
|
||||
func (c *GridCorrector) nearObject(objects []*events.Object) (*events.Object, error) {
|
||||
if len(objects) == 0 {
|
||||
return nil, fmt.Errorf("list objects must contain at least one object")
|
||||
}
|
||||
if len(objects) == 1 {
|
||||
return objects[0], nil
|
||||
}
|
||||
|
||||
var result *events.Object
|
||||
for _, obj := range objects {
|
||||
if result == nil || obj.Bottom > result.Bottom {
|
||||
result = obj
|
||||
continue
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func NewGridMapFromJson(fileName string) (*GridMap, error) {
|
||||
content, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
|
@ -167,56 +167,6 @@ func TestCorrector_AdjustFromObjectPosition(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorrector_nearObject(t *testing.T) {
|
||||
type args struct {
|
||||
objects []*events.Object
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *events.Object
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "List object is empty",
|
||||
args: args{
|
||||
objects: []*events.Object{},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "List with only one object",
|
||||
args: args{
|
||||
objects: []*events.Object{&objectOnMiddleNear},
|
||||
},
|
||||
want: &objectOnMiddleNear,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "List with many objects",
|
||||
args: args{
|
||||
objects: []*events.Object{&objectOnLeftDistant, &objectOnMiddleNear, &objectOnRightDistant, &objectOnMiddleDistant},
|
||||
},
|
||||
want: &objectOnMiddleNear,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &GridCorrector{}
|
||||
got, err := c.nearObject(tt.args.objects)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("nearObject() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("nearObject() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewGridMapFromJson(t *testing.T) {
|
||||
type args struct {
|
||||
fileName string
|
||||
|
658
vendor/github.com/cyrilix/robocar-protobuf/go/events/events.pb.go
generated
vendored
658
vendor/github.com/cyrilix/robocar-protobuf/go/events/events.pb.go
generated
vendored
File diff suppressed because it is too large
Load Diff
6
vendor/github.com/eclipse/paho.mqtt.golang/README.md
generated
vendored
6
vendor/github.com/eclipse/paho.mqtt.golang/README.md
generated
vendored
@ -113,7 +113,9 @@ identifier; this is as per the [spec](https://docs.oasis-open.org/mqtt/mqtt/v3.1
|
||||
not received, disconnecting` errors).
|
||||
* When QOS1+ subscriptions have been created previously and you connect with `CleanSession` set to false it is possible
|
||||
that the broker will deliver retained messages before `Subscribe` can be called. To process these messages either
|
||||
configure a handler with `AddRoute` or set a `DefaultPublishHandler`.
|
||||
configure a handler with `AddRoute` or set a `DefaultPublishHandler`. If there is no handler (or `DefaultPublishHandler`)
|
||||
then inbound messages will not be acknowledged. Adding a handler (even if it's `opts.SetDefaultPublishHandler(func(mqtt.Client, mqtt.Message) {})`)
|
||||
is highly recommended to avoid inadvertently hitting inflight message limits.
|
||||
* Loss of network connectivity may not be detected immediately. If this is an issue then consider setting
|
||||
`ClientOptions.KeepAlive` (sends regular messages to check the link is active).
|
||||
* Reusing a `Client` is not completely safe. After calling `Disconnect` please create a new Client (`NewClient()`) rather
|
||||
@ -193,4 +195,4 @@ Discussion of the Paho clients takes place on the [Eclipse paho-dev mailing list
|
||||
|
||||
General questions about the MQTT protocol are discussed in the [MQTT Google Group](https://groups.google.com/forum/?hl=en-US&fromgroups#!forum/mqtt).
|
||||
|
||||
There is much more information available via the [MQTT community site](http://mqtt.org).
|
||||
There is much more information available via the [MQTT community site](http://mqtt.org).
|
||||
|
104
vendor/github.com/eclipse/paho.mqtt.golang/backoff.go
generated
vendored
Normal file
104
vendor/github.com/eclipse/paho.mqtt.golang/backoff.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2021 IBM Corp and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Matt Brittan
|
||||
* Daichi Tomaru
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Controller for sleep with backoff when the client attempts reconnection
|
||||
// It has statuses for each situations cause reconnection.
|
||||
type backoffController struct {
|
||||
sync.RWMutex
|
||||
statusMap map[string]*backoffStatus
|
||||
}
|
||||
|
||||
type backoffStatus struct {
|
||||
lastSleepPeriod time.Duration
|
||||
lastErrorTime time.Time
|
||||
}
|
||||
|
||||
func newBackoffController() *backoffController {
|
||||
return &backoffController{
|
||||
statusMap: map[string]*backoffStatus{},
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate next sleep period from the specified parameters.
|
||||
// Returned values are next sleep period and whether the error situation is continual.
|
||||
// If connection errors continuouslly occurs, its sleep period is exponentially increased.
|
||||
// Also if there is a lot of time between last and this error, sleep period is initialized.
|
||||
func (b *backoffController) getBackoffSleepTime(
|
||||
situation string, initSleepPeriod time.Duration, maxSleepPeriod time.Duration, processTime time.Duration, skipFirst bool,
|
||||
) (time.Duration, bool) {
|
||||
// Decide first sleep time if the situation is not continual.
|
||||
var firstProcess = func(status *backoffStatus, init time.Duration, skip bool) (time.Duration, bool) {
|
||||
if skip {
|
||||
status.lastSleepPeriod = 0
|
||||
return 0, false
|
||||
}
|
||||
status.lastSleepPeriod = init
|
||||
return init, false
|
||||
}
|
||||
|
||||
// Prioritize maxSleep.
|
||||
if initSleepPeriod > maxSleepPeriod {
|
||||
initSleepPeriod = maxSleepPeriod
|
||||
}
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
status, exist := b.statusMap[situation]
|
||||
if !exist {
|
||||
b.statusMap[situation] = &backoffStatus{initSleepPeriod, time.Now()}
|
||||
return firstProcess(b.statusMap[situation], initSleepPeriod, skipFirst)
|
||||
}
|
||||
|
||||
oldTime := status.lastErrorTime
|
||||
status.lastErrorTime = time.Now()
|
||||
|
||||
// When there is a lot of time between last and this error, sleep period is initialized.
|
||||
if status.lastErrorTime.Sub(oldTime) > (processTime * 2 + status.lastSleepPeriod) {
|
||||
return firstProcess(status, initSleepPeriod, skipFirst)
|
||||
}
|
||||
|
||||
if status.lastSleepPeriod == 0 {
|
||||
status.lastSleepPeriod = initSleepPeriod
|
||||
return initSleepPeriod, true
|
||||
}
|
||||
|
||||
if nextSleepPeriod := status.lastSleepPeriod * 2; nextSleepPeriod <= maxSleepPeriod {
|
||||
status.lastSleepPeriod = nextSleepPeriod
|
||||
} else {
|
||||
status.lastSleepPeriod = maxSleepPeriod
|
||||
}
|
||||
|
||||
return status.lastSleepPeriod, true
|
||||
}
|
||||
|
||||
// Execute sleep the time returned from getBackoffSleepTime.
|
||||
func (b *backoffController) sleepWithBackoff(
|
||||
situation string, initSleepPeriod time.Duration, maxSleepPeriod time.Duration, processTime time.Duration, skipFirst bool,
|
||||
) (time.Duration, bool) {
|
||||
sleep, isFirst := b.getBackoffSleepTime(situation, initSleepPeriod, maxSleepPeriod, processTime, skipFirst)
|
||||
if sleep != 0 {
|
||||
time.Sleep(sleep)
|
||||
}
|
||||
return sleep, isFirst
|
||||
}
|
376
vendor/github.com/eclipse/paho.mqtt.golang/client.go
generated
vendored
376
vendor/github.com/eclipse/paho.mqtt.golang/client.go
generated
vendored
@ -38,13 +38,6 @@ import (
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
)
|
||||
|
||||
const (
|
||||
disconnected uint32 = iota
|
||||
connecting
|
||||
reconnecting
|
||||
connected
|
||||
)
|
||||
|
||||
// Client is the interface definition for a Client as used by this
|
||||
// library, the interface is primarily to allow mocking tests.
|
||||
//
|
||||
@ -52,9 +45,12 @@ const (
|
||||
// with an MQTT server using non-blocking methods that allow work
|
||||
// to be done in the background.
|
||||
// An application may connect to an MQTT server using:
|
||||
// A plain TCP socket
|
||||
// A secure SSL/TLS socket
|
||||
// A websocket
|
||||
//
|
||||
// A plain TCP socket (e.g. mqtt://test.mosquitto.org:1833)
|
||||
// A secure SSL/TLS socket (e.g. tls://test.mosquitto.org:8883)
|
||||
// A websocket (e.g ws://test.mosquitto.org:8080 or wss://test.mosquitto.org:8081)
|
||||
// Something else (using `options.CustomOpenConnectionFn`)
|
||||
//
|
||||
// To enable ensured message delivery at Quality of Service (QoS) levels
|
||||
// described in the MQTT spec, a message persistence mechanism must be
|
||||
// used. This is done by providing a type which implements the Store
|
||||
@ -128,8 +124,7 @@ type client struct {
|
||||
lastReceived atomic.Value // time.Time - the last time a packet was successfully received from network
|
||||
pingOutstanding int32 // set to 1 if a ping has been sent but response not ret received
|
||||
|
||||
status uint32 // see const definitions at top of file for possible values
|
||||
sync.RWMutex // Protects the above two variables (note: atomic writes are also used somewhat inconsistently)
|
||||
status connectionStatus // see constants in status.go for values
|
||||
|
||||
messageIds // effectively a map from message id to token completor
|
||||
|
||||
@ -146,6 +141,8 @@ type client struct {
|
||||
stop chan struct{} // Closed to request that workers stop
|
||||
workers sync.WaitGroup // used to wait for workers to complete (ping, keepalive, errwatch, resume)
|
||||
commsStopped chan struct{} // closed when the comms routines have stopped (kept running until after workers have closed to avoid deadlocks)
|
||||
|
||||
backoff *backoffController
|
||||
}
|
||||
|
||||
// NewClient will create an MQTT v3.1.1 client with all of the options specified
|
||||
@ -169,12 +166,12 @@ func NewClient(o *ClientOptions) Client {
|
||||
c.options.protocolVersionExplicit = false
|
||||
}
|
||||
c.persist = c.options.Store
|
||||
c.status = disconnected
|
||||
c.messageIds = messageIds{index: make(map[uint16]tokenCompletor)}
|
||||
c.msgRouter = newRouter()
|
||||
c.msgRouter.setDefaultHandler(c.options.DefaultPublishHandler)
|
||||
c.obound = make(chan *PacketAndToken)
|
||||
c.oboundP = make(chan *PacketAndToken)
|
||||
c.backoff = newBackoffController()
|
||||
return c
|
||||
}
|
||||
|
||||
@ -196,47 +193,27 @@ func (c *client) AddRoute(topic string, callback MessageHandler) {
|
||||
// the client is connected or not.
|
||||
// connected means that the connection is up now OR it will
|
||||
// be established/reestablished automatically when possible
|
||||
// Warning: The connection status may change at any time so use this with care!
|
||||
func (c *client) IsConnected() bool {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
status := atomic.LoadUint32(&c.status)
|
||||
// This will need to change if additional statuses are added
|
||||
s, r := c.status.ConnectionStatusRetry()
|
||||
switch {
|
||||
case status == connected:
|
||||
case s == connected:
|
||||
return true
|
||||
case c.options.AutoReconnect && status > connecting:
|
||||
return true
|
||||
case c.options.ConnectRetry && status == connecting:
|
||||
case c.options.ConnectRetry && s == connecting:
|
||||
return true
|
||||
case c.options.AutoReconnect:
|
||||
return s == reconnecting || (s == disconnecting && r) // r indicates we will reconnect
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsConnectionOpen return a bool signifying whether the client has an active
|
||||
// connection to mqtt broker, i.e not in disconnected or reconnect mode
|
||||
// connection to mqtt broker, i.e. not in disconnected or reconnect mode
|
||||
// Warning: The connection status may change at any time so use this with care!
|
||||
func (c *client) IsConnectionOpen() bool {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
status := atomic.LoadUint32(&c.status)
|
||||
switch {
|
||||
case status == connected:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) connectionStatus() uint32 {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
status := atomic.LoadUint32(&c.status)
|
||||
return status
|
||||
}
|
||||
|
||||
func (c *client) setConnected(status uint32) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
atomic.StoreUint32(&c.status, status)
|
||||
return c.status.ConnectionStatus() == connected
|
||||
}
|
||||
|
||||
// ErrNotConnected is the error returned from function calls that are
|
||||
@ -253,25 +230,31 @@ func (c *client) Connect() Token {
|
||||
t := newToken(packets.Connect).(*ConnectToken)
|
||||
DEBUG.Println(CLI, "Connect()")
|
||||
|
||||
if c.options.ConnectRetry && atomic.LoadUint32(&c.status) != disconnected {
|
||||
// if in any state other than disconnected and ConnectRetry is
|
||||
// enabled then the connection will come up automatically
|
||||
// client can assume connection is up
|
||||
WARN.Println(CLI, "Connect() called but not disconnected")
|
||||
t.returnCode = packets.Accepted
|
||||
t.flowComplete()
|
||||
connectionUp, err := c.status.Connecting()
|
||||
if err != nil {
|
||||
if err == errAlreadyConnectedOrReconnecting && c.options.AutoReconnect {
|
||||
// When reconnection is active we don't consider calls tro Connect to ba an error (mainly for compatability)
|
||||
WARN.Println(CLI, "Connect() called but not disconnected")
|
||||
t.returnCode = packets.Accepted
|
||||
t.flowComplete()
|
||||
return t
|
||||
}
|
||||
ERROR.Println(CLI, err) // CONNECT should never be called unless we are disconnected
|
||||
t.setError(err)
|
||||
return t
|
||||
}
|
||||
|
||||
c.persist.Open()
|
||||
if c.options.ConnectRetry {
|
||||
c.reserveStoredPublishIDs() // Reserve IDs to allow publish before connect complete
|
||||
c.reserveStoredPublishIDs() // Reserve IDs to allow publishing before connect complete
|
||||
}
|
||||
c.setConnected(connecting)
|
||||
|
||||
go func() {
|
||||
if len(c.options.Servers) == 0 {
|
||||
t.setError(fmt.Errorf("no servers defined to connect to"))
|
||||
if err := connectionUp(false); err != nil {
|
||||
ERROR.Println(CLI, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -285,26 +268,28 @@ func (c *client) Connect() Token {
|
||||
DEBUG.Println(CLI, "Connect failed, sleeping for", int(c.options.ConnectRetryInterval.Seconds()), "seconds and will then retry, error:", err.Error())
|
||||
time.Sleep(c.options.ConnectRetryInterval)
|
||||
|
||||
if atomic.LoadUint32(&c.status) == connecting {
|
||||
if c.status.ConnectionStatus() == connecting { // Possible connection aborted elsewhere
|
||||
goto RETRYCONN
|
||||
}
|
||||
}
|
||||
ERROR.Println(CLI, "Failed to connect to a broker")
|
||||
c.setConnected(disconnected)
|
||||
c.persist.Close()
|
||||
t.returnCode = rc
|
||||
t.setError(err)
|
||||
if err := connectionUp(false); err != nil {
|
||||
ERROR.Println(CLI, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
inboundFromStore := make(chan packets.ControlPacket) // there may be some inbound comms packets in the store that are awaiting processing
|
||||
if c.startCommsWorkers(conn, inboundFromStore) {
|
||||
inboundFromStore := make(chan packets.ControlPacket) // there may be some inbound comms packets in the store that are awaiting processing
|
||||
if c.startCommsWorkers(conn, connectionUp, inboundFromStore) { // note that this takes care of updating the status (to connected or disconnected)
|
||||
// Take care of any messages in the store
|
||||
if !c.options.CleanSession {
|
||||
c.resume(c.options.ResumeSubs, inboundFromStore)
|
||||
} else {
|
||||
c.persist.Reset()
|
||||
}
|
||||
} else {
|
||||
} else { // Note: With the new status subsystem this should only happen if Disconnect called simultaneously with the above
|
||||
WARN.Println(CLI, "Connect() called but connection established in another goroutine")
|
||||
}
|
||||
|
||||
@ -316,13 +301,20 @@ func (c *client) Connect() Token {
|
||||
}
|
||||
|
||||
// internal function used to reconnect the client when it loses its connection
|
||||
func (c *client) reconnect() {
|
||||
// The connection status MUST be reconnecting prior to calling this function (via call to status.connectionLost)
|
||||
func (c *client) reconnect(connectionUp connCompletedFn) {
|
||||
DEBUG.Println(CLI, "enter reconnect")
|
||||
var (
|
||||
sleep = 1 * time.Second
|
||||
initSleep = 1 * time.Second
|
||||
conn net.Conn
|
||||
)
|
||||
|
||||
// If the reason of connection lost is same as the before one, sleep timer is set before attempting connection is started.
|
||||
// Sleep time is exponentially increased as the same situation continues
|
||||
if slp, isContinual := c.backoff.sleepWithBackoff("connectionLost", initSleep, c.options.MaxReconnectInterval, 3 * time.Second, true); isContinual {
|
||||
DEBUG.Println(CLI, "Detect continual connection lost after reconnect, slept for", int(slp.Seconds()), "seconds")
|
||||
}
|
||||
|
||||
for {
|
||||
if nil != c.options.OnReconnecting {
|
||||
c.options.OnReconnecting(c, &c.options)
|
||||
@ -332,32 +324,20 @@ func (c *client) reconnect() {
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
DEBUG.Println(CLI, "Reconnect failed, sleeping for", int(sleep.Seconds()), "seconds:", err)
|
||||
time.Sleep(sleep)
|
||||
if sleep < c.options.MaxReconnectInterval {
|
||||
sleep *= 2
|
||||
}
|
||||
sleep, _ := c.backoff.sleepWithBackoff("attemptReconnection", initSleep, c.options.MaxReconnectInterval, c.options.ConnectTimeout, false)
|
||||
DEBUG.Println(CLI, "Reconnect failed, slept for", int(sleep.Seconds()), "seconds:", err)
|
||||
|
||||
if sleep > c.options.MaxReconnectInterval {
|
||||
sleep = c.options.MaxReconnectInterval
|
||||
}
|
||||
// Disconnect may have been called
|
||||
if atomic.LoadUint32(&c.status) == disconnected {
|
||||
break
|
||||
if c.status.ConnectionStatus() != reconnecting { // Disconnect may have been called
|
||||
if err := connectionUp(false); err != nil { // Should always return an error
|
||||
ERROR.Println(CLI, err.Error())
|
||||
}
|
||||
DEBUG.Println(CLI, "Client moved to disconnected state while reconnecting, abandoning reconnect")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect() must have been called while we were trying to reconnect.
|
||||
if c.connectionStatus() == disconnected {
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
DEBUG.Println(CLI, "Client moved to disconnected state while reconnecting, abandoning reconnect")
|
||||
return
|
||||
}
|
||||
|
||||
inboundFromStore := make(chan packets.ControlPacket) // there may be some inbound comms packets in the store that are awaiting processing
|
||||
if c.startCommsWorkers(conn, inboundFromStore) {
|
||||
inboundFromStore := make(chan packets.ControlPacket) // there may be some inbound comms packets in the store that are awaiting processing
|
||||
if c.startCommsWorkers(conn, connectionUp, inboundFromStore) { // note that this takes care of updating the status (to connected or disconnected)
|
||||
c.resume(c.options.ResumeSubs, inboundFromStore)
|
||||
}
|
||||
close(inboundFromStore)
|
||||
@ -392,6 +372,7 @@ func (c *client) attemptConnection() (net.Conn, byte, bool, error) {
|
||||
DEBUG.Println(CLI, "using custom onConnectAttempt handler...")
|
||||
tlsCfg = c.options.OnConnectAttempt(broker, c.options.TLSConfig)
|
||||
}
|
||||
connDeadline := time.Now().Add(c.options.ConnectTimeout) // Time by which connection must be established
|
||||
dialer := c.options.Dialer
|
||||
if dialer == nil { //
|
||||
WARN.Println(CLI, "dialer was nil, using default")
|
||||
@ -411,16 +392,23 @@ func (c *client) attemptConnection() (net.Conn, byte, bool, error) {
|
||||
}
|
||||
DEBUG.Println(CLI, "socket connected to broker")
|
||||
|
||||
// Now we send the perform the MQTT connection handshake
|
||||
// Now we perform the MQTT connection handshake ensuring that it does not exceed the timeout
|
||||
if err := conn.SetDeadline(connDeadline); err != nil {
|
||||
ERROR.Println(CLI, "set deadline for handshake ", err)
|
||||
}
|
||||
|
||||
// Now we perform the MQTT connection handshake
|
||||
rc, sessionPresent, err = connectMQTT(conn, cm, protocolVersion)
|
||||
if rc == packets.Accepted {
|
||||
if err := conn.SetDeadline(time.Time{}); err != nil {
|
||||
ERROR.Println(CLI, "reset deadline following handshake ", err)
|
||||
}
|
||||
break // successfully connected
|
||||
}
|
||||
|
||||
// We may be have to attempt the connection with MQTT 3.1
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
// We may have to attempt the connection with MQTT 3.1
|
||||
_ = conn.Close()
|
||||
|
||||
if !c.options.protocolVersionExplicit && protocolVersion == 4 { // try falling back to 3.1?
|
||||
DEBUG.Println(CLI, "Trying reconnect using MQTT 3.1 protocol")
|
||||
protocolVersion = 3
|
||||
@ -452,43 +440,59 @@ func (c *client) attemptConnection() (net.Conn, byte, bool, error) {
|
||||
// reusing the `client` may lead to panics. If you want to reconnect when the connection drops then use
|
||||
// `SetAutoReconnect` and/or `SetConnectRetry`options instead of implementing this yourself.
|
||||
func (c *client) Disconnect(quiesce uint) {
|
||||
defer c.disconnect()
|
||||
done := make(chan struct{}) // Simplest way to ensure quiesce is always honoured
|
||||
go func() {
|
||||
defer close(done)
|
||||
disDone, err := c.status.Disconnecting()
|
||||
if err != nil {
|
||||
// Status has been set to disconnecting, but we had to wait for something else to complete
|
||||
WARN.Println(CLI, err.Error())
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
c.disconnect() // Force disconnection
|
||||
disDone() // Update status
|
||||
}()
|
||||
DEBUG.Println(CLI, "disconnecting")
|
||||
dm := packets.NewControlPacket(packets.Disconnect).(*packets.DisconnectPacket)
|
||||
dt := newToken(packets.Disconnect)
|
||||
select {
|
||||
case c.oboundP <- &PacketAndToken{p: dm, t: dt}:
|
||||
// wait for work to finish, or quiesce time consumed
|
||||
DEBUG.Println(CLI, "calling WaitTimeout")
|
||||
dt.WaitTimeout(time.Duration(quiesce) * time.Millisecond)
|
||||
DEBUG.Println(CLI, "WaitTimeout done")
|
||||
// Below code causes a potential data race. Following status refactor it should no longer be required
|
||||
// but leaving in as need to check code further.
|
||||
// case <-c.commsStopped:
|
||||
// WARN.Println("Disconnect packet could not be sent because comms stopped")
|
||||
case <-time.After(time.Duration(quiesce) * time.Millisecond):
|
||||
WARN.Println("Disconnect packet not sent due to timeout")
|
||||
}
|
||||
}()
|
||||
|
||||
status := atomic.LoadUint32(&c.status)
|
||||
c.setConnected(disconnected)
|
||||
|
||||
if status != connected {
|
||||
WARN.Println(CLI, "Disconnect() called but not connected (disconnected/reconnecting)")
|
||||
return
|
||||
}
|
||||
|
||||
DEBUG.Println(CLI, "disconnecting")
|
||||
dm := packets.NewControlPacket(packets.Disconnect).(*packets.DisconnectPacket)
|
||||
dt := newToken(packets.Disconnect)
|
||||
// Return when done or after timeout expires (would like to change but this maintains compatibility)
|
||||
delay := time.NewTimer(time.Duration(quiesce) * time.Millisecond)
|
||||
select {
|
||||
case c.oboundP <- &PacketAndToken{p: dm, t: dt}:
|
||||
// wait for work to finish, or quiesce time consumed
|
||||
DEBUG.Println(CLI, "calling WaitTimeout")
|
||||
dt.WaitTimeout(time.Duration(quiesce) * time.Millisecond)
|
||||
DEBUG.Println(CLI, "WaitTimeout done")
|
||||
// Let's comment this chunk of code until we are able to safely read this variable
|
||||
// without data races.
|
||||
// case <-c.commsStopped:
|
||||
// WARN.Println("Disconnect packet could not be sent because comms stopped")
|
||||
case <-time.After(time.Duration(quiesce) * time.Millisecond):
|
||||
WARN.Println("Disconnect packet not sent due to timeout")
|
||||
case <-done:
|
||||
if !delay.Stop() {
|
||||
<-delay.C
|
||||
}
|
||||
case <-delay.C:
|
||||
}
|
||||
}
|
||||
|
||||
// forceDisconnect will end the connection with the mqtt broker immediately (used for tests only)
|
||||
func (c *client) forceDisconnect() {
|
||||
if !c.IsConnected() {
|
||||
WARN.Println(CLI, "already disconnected")
|
||||
disDone, err := c.status.Disconnecting()
|
||||
if err != nil {
|
||||
// Possible that we are not actually connected
|
||||
WARN.Println(CLI, err.Error())
|
||||
return
|
||||
}
|
||||
c.setConnected(disconnected)
|
||||
DEBUG.Println(CLI, "forcefully disconnecting")
|
||||
c.disconnect()
|
||||
disDone()
|
||||
}
|
||||
|
||||
// disconnect cleans up after a final disconnection (user requested so no auto reconnection)
|
||||
@ -505,49 +509,79 @@ func (c *client) disconnect() {
|
||||
|
||||
// internalConnLost cleanup when connection is lost or an error occurs
|
||||
// Note: This function will not block
|
||||
func (c *client) internalConnLost(err error) {
|
||||
func (c *client) internalConnLost(whyConnLost error) {
|
||||
// It is possible that internalConnLost will be called multiple times simultaneously
|
||||
// (including after sending a DisconnectPacket) as such we only do cleanup etc if the
|
||||
// routines were actually running and are not being disconnected at users request
|
||||
DEBUG.Println(CLI, "internalConnLost called")
|
||||
stopDone := c.stopCommsWorkers()
|
||||
if stopDone != nil { // stopDone will be nil if workers already in the process of stopping or stopped
|
||||
go func() {
|
||||
DEBUG.Println(CLI, "internalConnLost waiting on workers")
|
||||
<-stopDone
|
||||
DEBUG.Println(CLI, "internalConnLost workers stopped")
|
||||
// It is possible that Disconnect was called which led to this error so reconnection depends upon status
|
||||
reconnect := c.options.AutoReconnect && c.connectionStatus() > connecting
|
||||
|
||||
if c.options.CleanSession && !reconnect {
|
||||
c.messageIds.cleanUp() // completes PUB/SUB/UNSUB tokens
|
||||
} else if !c.options.ResumeSubs {
|
||||
c.messageIds.cleanUpSubscribe() // completes SUB/UNSUB tokens
|
||||
}
|
||||
if reconnect {
|
||||
c.setConnected(reconnecting)
|
||||
go c.reconnect()
|
||||
} else {
|
||||
c.setConnected(disconnected)
|
||||
}
|
||||
if c.options.OnConnectionLost != nil {
|
||||
go c.options.OnConnectionLost(c, err)
|
||||
}
|
||||
DEBUG.Println(CLI, "internalConnLost complete")
|
||||
}()
|
||||
disDone, err := c.status.ConnectionLost(c.options.AutoReconnect && c.status.ConnectionStatus() > connecting)
|
||||
if err != nil {
|
||||
if err == errConnLossWhileDisconnecting || err == errAlreadyHandlingConnectionLoss {
|
||||
return // Loss of connection is expected or already being handled
|
||||
}
|
||||
ERROR.Println(CLI, fmt.Sprintf("internalConnLost unexpected status: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// c.stopCommsWorker returns a channel that is closed when the operation completes. This was required prior
|
||||
// to the implementation of proper status management but has been left in place, for now, to minimise change
|
||||
stopDone := c.stopCommsWorkers()
|
||||
// stopDone was required in previous versions because there was no connectionLost status (and there were
|
||||
// issues with status handling). This code has been left in place for the time being just in case the new
|
||||
// status handling contains bugs (refactoring required at some point).
|
||||
if stopDone == nil { // stopDone will be nil if workers already in the process of stopping or stopped
|
||||
ERROR.Println(CLI, "internalConnLost stopDone unexpectedly nil - BUG BUG")
|
||||
// Cannot really do anything other than leave things disconnected
|
||||
if _, err = disDone(false); err != nil { // Safest option - cannot leave status as connectionLost
|
||||
ERROR.Println(CLI, fmt.Sprintf("internalConnLost failed to set status to disconnected (stopDone): %s", err.Error()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// It may take a while for the disconnection to complete whatever called us needs to exit cleanly so finnish in goRoutine
|
||||
go func() {
|
||||
DEBUG.Println(CLI, "internalConnLost waiting on workers")
|
||||
<-stopDone
|
||||
DEBUG.Println(CLI, "internalConnLost workers stopped")
|
||||
|
||||
reConnDone, err := disDone(true)
|
||||
if err != nil {
|
||||
ERROR.Println(CLI, "failure whilst reporting completion of disconnect", err)
|
||||
} else if reConnDone == nil { // Should never happen
|
||||
ERROR.Println(CLI, "BUG BUG BUG reconnection function is nil", err)
|
||||
}
|
||||
|
||||
reconnect := err == nil && reConnDone != nil
|
||||
|
||||
if c.options.CleanSession && !reconnect {
|
||||
c.messageIds.cleanUp() // completes PUB/SUB/UNSUB tokens
|
||||
} else if !c.options.ResumeSubs {
|
||||
c.messageIds.cleanUpSubscribe() // completes SUB/UNSUB tokens
|
||||
}
|
||||
if reconnect {
|
||||
go c.reconnect(reConnDone) // Will set connection status to reconnecting
|
||||
}
|
||||
if c.options.OnConnectionLost != nil {
|
||||
go c.options.OnConnectionLost(c, whyConnLost)
|
||||
}
|
||||
DEBUG.Println(CLI, "internalConnLost complete")
|
||||
}()
|
||||
}
|
||||
|
||||
// startCommsWorkers is called when the connection is up.
|
||||
// It starts off all of the routines needed to process incoming and outgoing messages.
|
||||
// Returns true if the comms workers were started (i.e. they were not already running)
|
||||
func (c *client) startCommsWorkers(conn net.Conn, inboundFromStore <-chan packets.ControlPacket) bool {
|
||||
// It starts off the routines needed to process incoming and outgoing messages.
|
||||
// Returns true if the comms workers were started (i.e. successful connection)
|
||||
// connectionUp(true) will be called once everything is up; connectionUp(false) will be called on failure
|
||||
func (c *client) startCommsWorkers(conn net.Conn, connectionUp connCompletedFn, inboundFromStore <-chan packets.ControlPacket) bool {
|
||||
DEBUG.Println(CLI, "startCommsWorkers called")
|
||||
c.connMu.Lock()
|
||||
defer c.connMu.Unlock()
|
||||
if c.conn != nil {
|
||||
WARN.Println(CLI, "startCommsWorkers called when commsworkers already running")
|
||||
conn.Close() // No use for the new network connection
|
||||
if c.conn != nil { // Should never happen due to new status handling; leaving in for safety for the time being
|
||||
WARN.Println(CLI, "startCommsWorkers called when commsworkers already running BUG BUG")
|
||||
_ = conn.Close() // No use for the new network connection
|
||||
if err := connectionUp(false); err != nil {
|
||||
ERROR.Println(CLI, err.Error())
|
||||
}
|
||||
return false
|
||||
}
|
||||
c.conn = conn // Store the connection
|
||||
@ -567,7 +601,17 @@ func (c *client) startCommsWorkers(conn net.Conn, inboundFromStore <-chan packet
|
||||
c.workers.Add(1) // Done will be called when ackOut is closed
|
||||
ackOut := c.msgRouter.matchAndDispatch(incomingPubChan, c.options.Order, c)
|
||||
|
||||
c.setConnected(connected)
|
||||
// The connection is now ready for use (we spin up a few go routines below). It is possible that
|
||||
// Disconnect has been called in the interim...
|
||||
if err := connectionUp(true); err != nil {
|
||||
DEBUG.Println(CLI, err)
|
||||
close(c.stop) // Tidy up anything we have already started
|
||||
close(incomingPubChan)
|
||||
c.workers.Wait()
|
||||
c.conn.Close()
|
||||
c.conn = nil
|
||||
return false
|
||||
}
|
||||
DEBUG.Println(CLI, "client is connected/reconnected")
|
||||
if c.options.OnConnect != nil {
|
||||
go c.options.OnConnect(c)
|
||||
@ -660,8 +704,9 @@ func (c *client) startCommsWorkers(conn net.Conn, inboundFromStore <-chan packet
|
||||
}
|
||||
|
||||
// stopWorkersAndComms - Cleanly shuts down worker go routines (including the comms routines) and waits until everything has stopped
|
||||
// Returns nil it workers did not need to be stopped; otherwise returns a channel which will be closed when the stop is complete
|
||||
// Returns nil if workers did not need to be stopped; otherwise returns a channel which will be closed when the stop is complete
|
||||
// Note: This may block so run as a go routine if calling from any of the comms routines
|
||||
// Note2: It should be possible to simplify this now that the new status management code is in place.
|
||||
func (c *client) stopCommsWorkers() chan struct{} {
|
||||
DEBUG.Println(CLI, "stopCommsWorkers called")
|
||||
// It is possible that this function will be called multiple times simultaneously due to the way things get shutdown
|
||||
@ -710,7 +755,8 @@ func (c *client) Publish(topic string, qos byte, retained bool, payload interfac
|
||||
case !c.IsConnected():
|
||||
token.setError(ErrNotConnected)
|
||||
return token
|
||||
case c.connectionStatus() == reconnecting && qos == 0:
|
||||
case c.status.ConnectionStatus() == reconnecting && qos == 0:
|
||||
// message written to store and will be sent when connection comes up
|
||||
token.flowComplete()
|
||||
return token
|
||||
}
|
||||
@ -740,11 +786,13 @@ func (c *client) Publish(topic string, qos byte, retained bool, payload interfac
|
||||
token.messageID = mID
|
||||
}
|
||||
persistOutbound(c.persist, pub)
|
||||
switch c.connectionStatus() {
|
||||
switch c.status.ConnectionStatus() {
|
||||
case connecting:
|
||||
DEBUG.Println(CLI, "storing publish message (connecting), topic:", topic)
|
||||
case reconnecting:
|
||||
DEBUG.Println(CLI, "storing publish message (reconnecting), topic:", topic)
|
||||
case disconnecting:
|
||||
DEBUG.Println(CLI, "storing publish message (disconnecting), topic:", topic)
|
||||
default:
|
||||
DEBUG.Println(CLI, "sending publish message, topic:", topic)
|
||||
publishWaitTimeout := c.options.WriteTimeout
|
||||
@ -777,11 +825,11 @@ func (c *client) Subscribe(topic string, qos byte, callback MessageHandler) Toke
|
||||
if !c.IsConnectionOpen() {
|
||||
switch {
|
||||
case !c.options.ResumeSubs:
|
||||
// if not connected and resumesubs not set this sub will be thrown away
|
||||
// if not connected and resumeSubs not set this sub will be thrown away
|
||||
token.setError(fmt.Errorf("not currently connected and ResumeSubs not set"))
|
||||
return token
|
||||
case c.options.CleanSession && c.connectionStatus() == reconnecting:
|
||||
// if reconnecting and cleansession is true this sub will be thrown away
|
||||
case c.options.CleanSession && c.status.ConnectionStatus() == reconnecting:
|
||||
// if reconnecting and cleanSession is true this sub will be thrown away
|
||||
token.setError(fmt.Errorf("reconnecting state and cleansession is true"))
|
||||
return token
|
||||
}
|
||||
@ -822,11 +870,13 @@ func (c *client) Subscribe(topic string, qos byte, callback MessageHandler) Toke
|
||||
if c.options.ResumeSubs { // Only persist if we need this to resume subs after a disconnection
|
||||
persistOutbound(c.persist, sub)
|
||||
}
|
||||
switch c.connectionStatus() {
|
||||
switch c.status.ConnectionStatus() {
|
||||
case connecting:
|
||||
DEBUG.Println(CLI, "storing subscribe message (connecting), topic:", topic)
|
||||
case reconnecting:
|
||||
DEBUG.Println(CLI, "storing subscribe message (reconnecting), topic:", topic)
|
||||
case disconnecting:
|
||||
DEBUG.Println(CLI, "storing subscribe message (disconnecting), topic:", topic)
|
||||
default:
|
||||
DEBUG.Println(CLI, "sending subscribe message, topic:", topic)
|
||||
subscribeWaitTimeout := c.options.WriteTimeout
|
||||
@ -864,8 +914,8 @@ func (c *client) SubscribeMultiple(filters map[string]byte, callback MessageHand
|
||||
// if not connected and resumesubs not set this sub will be thrown away
|
||||
token.setError(fmt.Errorf("not currently connected and ResumeSubs not set"))
|
||||
return token
|
||||
case c.options.CleanSession && c.connectionStatus() == reconnecting:
|
||||
// if reconnecting and cleansession is true this sub will be thrown away
|
||||
case c.options.CleanSession && c.status.ConnectionStatus() == reconnecting:
|
||||
// if reconnecting and cleanSession is true this sub will be thrown away
|
||||
token.setError(fmt.Errorf("reconnecting state and cleansession is true"))
|
||||
return token
|
||||
}
|
||||
@ -896,11 +946,13 @@ func (c *client) SubscribeMultiple(filters map[string]byte, callback MessageHand
|
||||
if c.options.ResumeSubs { // Only persist if we need this to resume subs after a disconnection
|
||||
persistOutbound(c.persist, sub)
|
||||
}
|
||||
switch c.connectionStatus() {
|
||||
switch c.status.ConnectionStatus() {
|
||||
case connecting:
|
||||
DEBUG.Println(CLI, "storing subscribe message (connecting), topics:", sub.Topics)
|
||||
case reconnecting:
|
||||
DEBUG.Println(CLI, "storing subscribe message (reconnecting), topics:", sub.Topics)
|
||||
case disconnecting:
|
||||
DEBUG.Println(CLI, "storing subscribe message (disconnecting), topics:", sub.Topics)
|
||||
default:
|
||||
DEBUG.Println(CLI, "sending subscribe message, topics:", sub.Topics)
|
||||
subscribeWaitTimeout := c.options.WriteTimeout
|
||||
@ -1050,7 +1102,7 @@ func (c *client) resume(subscription bool, ibound chan packets.ControlPacket) {
|
||||
}
|
||||
releaseSemaphore(token) // If limiting simultaneous messages then we need to know when message is acknowledged
|
||||
default:
|
||||
ERROR.Println(STR, "invalid message type in store (discarded)")
|
||||
ERROR.Println(STR, fmt.Sprintf("invalid message type (inbound - %T) in store (discarded)", packet))
|
||||
c.persist.Del(key)
|
||||
}
|
||||
} else {
|
||||
@ -1064,7 +1116,7 @@ func (c *client) resume(subscription bool, ibound chan packets.ControlPacket) {
|
||||
return
|
||||
}
|
||||
default:
|
||||
ERROR.Println(STR, "invalid message type in store (discarded)")
|
||||
ERROR.Println(STR, fmt.Sprintf("invalid message type (%T) in store (discarded)", packet))
|
||||
c.persist.Del(key)
|
||||
}
|
||||
}
|
||||
@ -1085,11 +1137,11 @@ func (c *client) Unsubscribe(topics ...string) Token {
|
||||
if !c.IsConnectionOpen() {
|
||||
switch {
|
||||
case !c.options.ResumeSubs:
|
||||
// if not connected and resumesubs not set this unsub will be thrown away
|
||||
// if not connected and resumeSubs not set this unsub will be thrown away
|
||||
token.setError(fmt.Errorf("not currently connected and ResumeSubs not set"))
|
||||
return token
|
||||
case c.options.CleanSession && c.connectionStatus() == reconnecting:
|
||||
// if reconnecting and cleansession is true this unsub will be thrown away
|
||||
case c.options.CleanSession && c.status.ConnectionStatus() == reconnecting:
|
||||
// if reconnecting and cleanSession is true this unsub will be thrown away
|
||||
token.setError(fmt.Errorf("reconnecting state and cleansession is true"))
|
||||
return token
|
||||
}
|
||||
@ -1112,11 +1164,13 @@ func (c *client) Unsubscribe(topics ...string) Token {
|
||||
persistOutbound(c.persist, unsub)
|
||||
}
|
||||
|
||||
switch c.connectionStatus() {
|
||||
switch c.status.ConnectionStatus() {
|
||||
case connecting:
|
||||
DEBUG.Println(CLI, "storing unsubscribe message (connecting), topics:", topics)
|
||||
case reconnecting:
|
||||
DEBUG.Println(CLI, "storing unsubscribe message (reconnecting), topics:", topics)
|
||||
case disconnecting:
|
||||
DEBUG.Println(CLI, "storing unsubscribe message (reconnecting), topics:", topics)
|
||||
default:
|
||||
DEBUG.Println(CLI, "sending unsubscribe message, topics:", topics)
|
||||
subscribeWaitTimeout := c.options.WriteTimeout
|
||||
|
26
vendor/github.com/eclipse/paho.mqtt.golang/messageids.go
generated
vendored
26
vendor/github.com/eclipse/paho.mqtt.golang/messageids.go
generated
vendored
@ -31,7 +31,7 @@ import (
|
||||
type MId uint16
|
||||
|
||||
type messageIds struct {
|
||||
sync.RWMutex
|
||||
mu sync.RWMutex // Named to prevent Mu from being accessible directly via client
|
||||
index map[uint16]tokenCompletor
|
||||
|
||||
lastIssuedID uint16 // The most recently issued ID. Used so we cycle through ids rather than immediately reusing them (can make debugging easier)
|
||||
@ -44,7 +44,7 @@ const (
|
||||
|
||||
// cleanup clears the message ID map; completes all token types and sets error on PUB, SUB and UNSUB tokens.
|
||||
func (mids *messageIds) cleanUp() {
|
||||
mids.Lock()
|
||||
mids.mu.Lock()
|
||||
for _, token := range mids.index {
|
||||
switch token.(type) {
|
||||
case *PublishToken:
|
||||
@ -59,14 +59,14 @@ func (mids *messageIds) cleanUp() {
|
||||
token.flowComplete()
|
||||
}
|
||||
mids.index = make(map[uint16]tokenCompletor)
|
||||
mids.Unlock()
|
||||
mids.mu.Unlock()
|
||||
DEBUG.Println(MID, "cleaned up")
|
||||
}
|
||||
|
||||
// cleanUpSubscribe removes all SUBSCRIBE and UNSUBSCRIBE tokens (setting error)
|
||||
// This may be called when the connection is lost, and we will not be resending SUB/UNSUB packets
|
||||
func (mids *messageIds) cleanUpSubscribe() {
|
||||
mids.Lock()
|
||||
mids.mu.Lock()
|
||||
for mid, token := range mids.index {
|
||||
switch token.(type) {
|
||||
case *SubscribeToken:
|
||||
@ -77,19 +77,19 @@ func (mids *messageIds) cleanUpSubscribe() {
|
||||
delete(mids.index, mid)
|
||||
}
|
||||
}
|
||||
mids.Unlock()
|
||||
mids.mu.Unlock()
|
||||
DEBUG.Println(MID, "cleaned up subs")
|
||||
}
|
||||
|
||||
func (mids *messageIds) freeID(id uint16) {
|
||||
mids.Lock()
|
||||
mids.mu.Lock()
|
||||
delete(mids.index, id)
|
||||
mids.Unlock()
|
||||
mids.mu.Unlock()
|
||||
}
|
||||
|
||||
func (mids *messageIds) claimID(token tokenCompletor, id uint16) {
|
||||
mids.Lock()
|
||||
defer mids.Unlock()
|
||||
mids.mu.Lock()
|
||||
defer mids.mu.Unlock()
|
||||
if _, ok := mids.index[id]; !ok {
|
||||
mids.index[id] = token
|
||||
} else {
|
||||
@ -105,8 +105,8 @@ func (mids *messageIds) claimID(token tokenCompletor, id uint16) {
|
||||
// getID will return an available id or 0 if none available
|
||||
// The id will generally be the previous id + 1 (because this makes tracing messages a bit simpler)
|
||||
func (mids *messageIds) getID(t tokenCompletor) uint16 {
|
||||
mids.Lock()
|
||||
defer mids.Unlock()
|
||||
mids.mu.Lock()
|
||||
defer mids.mu.Unlock()
|
||||
i := mids.lastIssuedID // note: the only situation where lastIssuedID is 0 the map will be empty
|
||||
looped := false // uint16 will loop from 65535->0
|
||||
for {
|
||||
@ -127,8 +127,8 @@ func (mids *messageIds) getID(t tokenCompletor) uint16 {
|
||||
}
|
||||
|
||||
func (mids *messageIds) getToken(id uint16) tokenCompletor {
|
||||
mids.RLock()
|
||||
defer mids.RUnlock()
|
||||
mids.mu.RLock()
|
||||
defer mids.mu.RUnlock()
|
||||
if token, ok := mids.index[id]; ok {
|
||||
return token
|
||||
}
|
||||
|
11
vendor/github.com/eclipse/paho.mqtt.golang/net.go
generated
vendored
11
vendor/github.com/eclipse/paho.mqtt.golang/net.go
generated
vendored
@ -150,7 +150,7 @@ type incomingComms struct {
|
||||
|
||||
// startIncomingComms initiates incoming communications; this includes starting a goroutine to process incoming
|
||||
// messages.
|
||||
// Accepts a channel of inbound messages from the store (persisted messages); note this must be closed as soon as the
|
||||
// Accepts a channel of inbound messages from the store (persisted messages); note this must be closed as soon as
|
||||
// everything in the store has been sent.
|
||||
// Returns a channel that will be passed any received packets; this will be closed on a network error (and inboundFromStore closed)
|
||||
func startIncomingComms(conn io.Reader,
|
||||
@ -332,7 +332,7 @@ func startOutgoingComms(conn net.Conn,
|
||||
DEBUG.Println(NET, "outbound wrote disconnect, closing connection")
|
||||
// As per the MQTT spec "After sending a DISCONNECT Packet the Client MUST close the Network Connection"
|
||||
// Closing the connection will cause the goroutines to end in sequence (starting with incoming comms)
|
||||
conn.Close()
|
||||
_ = conn.Close()
|
||||
}
|
||||
case msg, ok := <-oboundFromIncoming: // message triggered by an inbound message (PubrecPacket or PubrelPacket)
|
||||
if !ok {
|
||||
@ -370,9 +370,10 @@ type commsFns interface {
|
||||
// startComms initiates goroutines that handles communications over the network connection
|
||||
// Messages will be stored (via commsFns) and deleted from the store as necessary
|
||||
// It returns two channels:
|
||||
// packets.PublishPacket - Will receive publish packets received over the network.
|
||||
// Closed when incoming comms routines exit (on shutdown or if network link closed)
|
||||
// error - Any errors will be sent on this channel. The channel is closed when all comms routines have shut down
|
||||
//
|
||||
// packets.PublishPacket - Will receive publish packets received over the network.
|
||||
// Closed when incoming comms routines exit (on shutdown or if network link closed)
|
||||
// error - Any errors will be sent on this channel. The channel is closed when all comms routines have shut down
|
||||
//
|
||||
// Note: The comms routines monitoring oboundp and obound will not shutdown until those channels are both closed. Any messages received between the
|
||||
// connection being closed and those channels being closed will generate errors (and nothing will be sent). That way the chance of a deadlock is
|
||||
|
8
vendor/github.com/eclipse/paho.mqtt.golang/netconn.go
generated
vendored
8
vendor/github.com/eclipse/paho.mqtt.golang/netconn.go
generated
vendored
@ -40,10 +40,14 @@ import (
|
||||
func openConnection(uri *url.URL, tlsc *tls.Config, timeout time.Duration, headers http.Header, websocketOptions *WebsocketOptions, dialer *net.Dialer) (net.Conn, error) {
|
||||
switch uri.Scheme {
|
||||
case "ws":
|
||||
conn, err := NewWebsocket(uri.String(), nil, timeout, headers, websocketOptions)
|
||||
dialURI := *uri // #623 - Gorilla Websockets does not accept URL's where uri.User != nil
|
||||
dialURI.User = nil
|
||||
conn, err := NewWebsocket(dialURI.String(), nil, timeout, headers, websocketOptions)
|
||||
return conn, err
|
||||
case "wss":
|
||||
conn, err := NewWebsocket(uri.String(), tlsc, timeout, headers, websocketOptions)
|
||||
dialURI := *uri // #623 - Gorilla Websockets does not accept URL's where uri.User != nil
|
||||
dialURI.User = nil
|
||||
conn, err := NewWebsocket(dialURI.String(), tlsc, timeout, headers, websocketOptions)
|
||||
return conn, err
|
||||
case "mqtt", "tcp":
|
||||
allProxy := os.Getenv("all_proxy")
|
||||
|
9
vendor/github.com/eclipse/paho.mqtt.golang/options.go
generated
vendored
9
vendor/github.com/eclipse/paho.mqtt.golang/options.go
generated
vendored
@ -104,6 +104,7 @@ type ClientOptions struct {
|
||||
MaxResumePubInFlight int // // 0 = no limit; otherwise this is the maximum simultaneous messages sent while resuming
|
||||
Dialer *net.Dialer
|
||||
CustomOpenConnectionFn OpenConnectionFunc
|
||||
AutoAckDisabled bool
|
||||
}
|
||||
|
||||
// NewClientOptions will create a new ClientClientOptions type with some
|
||||
@ -147,6 +148,7 @@ func NewClientOptions() *ClientOptions {
|
||||
WebsocketOptions: &WebsocketOptions{},
|
||||
Dialer: &net.Dialer{Timeout: 30 * time.Second},
|
||||
CustomOpenConnectionFn: nil,
|
||||
AutoAckDisabled: false,
|
||||
}
|
||||
return o
|
||||
}
|
||||
@ -446,3 +448,10 @@ func (o *ClientOptions) SetCustomOpenConnectionFn(customOpenConnectionFn OpenCon
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// SetAutoAckDisabled enables or disables the Automated Acking of Messages received by the handler.
|
||||
// By default it is set to false. Setting it to true will disable the auto-ack globally.
|
||||
func (o *ClientOptions) SetAutoAckDisabled(autoAckDisabled bool) *ClientOptions {
|
||||
o.AutoAckDisabled = autoAckDisabled
|
||||
return o
|
||||
}
|
||||
|
12
vendor/github.com/eclipse/paho.mqtt.golang/ping.go
generated
vendored
12
vendor/github.com/eclipse/paho.mqtt.golang/ping.go
generated
vendored
@ -32,16 +32,16 @@ import (
|
||||
func keepalive(c *client, conn io.Writer) {
|
||||
defer c.workers.Done()
|
||||
DEBUG.Println(PNG, "keepalive starting")
|
||||
var checkInterval int64
|
||||
var checkInterval time.Duration
|
||||
var pingSent time.Time
|
||||
|
||||
if c.options.KeepAlive > 10 {
|
||||
checkInterval = 5
|
||||
checkInterval = 5 * time.Second
|
||||
} else {
|
||||
checkInterval = c.options.KeepAlive / 2
|
||||
checkInterval = time.Duration(c.options.KeepAlive) * time.Second / 2
|
||||
}
|
||||
|
||||
intervalTicker := time.NewTicker(time.Duration(checkInterval * int64(time.Second)))
|
||||
intervalTicker := time.NewTicker(checkInterval)
|
||||
defer intervalTicker.Stop()
|
||||
|
||||
for {
|
||||
@ -58,8 +58,8 @@ func keepalive(c *client, conn io.Writer) {
|
||||
if atomic.LoadInt32(&c.pingOutstanding) == 0 {
|
||||
DEBUG.Println(PNG, "keepalive sending ping")
|
||||
ping := packets.NewControlPacket(packets.Pingreq).(*packets.PingreqPacket)
|
||||
// We don't want to wait behind large messages being sent, the Write call
|
||||
// will block until it it able to send the packet.
|
||||
// We don't want to wait behind large messages being sent, the `Write` call
|
||||
// will block until it is able to send the packet.
|
||||
atomic.StoreInt32(&c.pingOutstanding, 1)
|
||||
if err := ping.Write(conn); err != nil {
|
||||
ERROR.Println(PNG, err)
|
||||
|
12
vendor/github.com/eclipse/paho.mqtt.golang/router.go
generated
vendored
12
vendor/github.com/eclipse/paho.mqtt.golang/router.go
generated
vendored
@ -186,7 +186,9 @@ func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
hd(client, m)
|
||||
m.Ack()
|
||||
if !client.options.AutoAckDisabled {
|
||||
m.Ack()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
@ -201,7 +203,9 @@ func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
r.defaultHandler(client, m)
|
||||
m.Ack()
|
||||
if !client.options.AutoAckDisabled {
|
||||
m.Ack()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
@ -212,7 +216,9 @@ func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order
|
||||
r.RUnlock()
|
||||
for _, handler := range handlers {
|
||||
handler(client, m)
|
||||
m.Ack()
|
||||
if !client.options.AutoAckDisabled {
|
||||
m.Ack()
|
||||
}
|
||||
}
|
||||
// DEBUG.Println(ROU, "matchAndDispatch handled message")
|
||||
}
|
||||
|
296
vendor/github.com/eclipse/paho.mqtt.golang/status.go
generated
vendored
Normal file
296
vendor/github.com/eclipse/paho.mqtt.golang/status.go
generated
vendored
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* Copyright (c) 2021 IBM Corp and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
* Matt Brittan
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Status - Manage the connection status
|
||||
|
||||
// Multiple go routines will want to access/set this. Previously status was implemented as a `uint32` and updated
|
||||
// with a mixture of atomic functions and a mutex (leading to some deadlock type issues that were very hard to debug).
|
||||
|
||||
// In this new implementation `connectionStatus` takes over managing the state and provides functions that allow the
|
||||
// client to request a move to a particular state (it may reject these requests!). In some cases the 'state' is
|
||||
// transitory, for example `connecting`, in those cases a function will be returned that allows the client to move
|
||||
// to a more static state (`disconnected` or `connected`).
|
||||
|
||||
// This "belts-and-braces" may be a little over the top but issues with the status have caused a number of difficult
|
||||
// to trace bugs in the past and the likelihood that introducing a new system would introduce bugs seemed high!
|
||||
// I have written this in a way that should make it very difficult to misuse it (but it does make things a little
|
||||
// complex with functions returning functions that return functions!).
|
||||
|
||||
type status uint32
|
||||
|
||||
const (
|
||||
disconnected status = iota // default (nil) status is disconnected
|
||||
disconnecting // Transitioning from one of the below states back to disconnected
|
||||
connecting
|
||||
reconnecting
|
||||
connected
|
||||
)
|
||||
|
||||
// String simplify output of statuses
|
||||
func (s status) String() string {
|
||||
switch s {
|
||||
case disconnected:
|
||||
return "disconnected"
|
||||
case disconnecting:
|
||||
return "disconnecting"
|
||||
case connecting:
|
||||
return "connecting"
|
||||
case reconnecting:
|
||||
return "reconnecting"
|
||||
case connected:
|
||||
return "connected"
|
||||
default:
|
||||
return "invalid"
|
||||
}
|
||||
}
|
||||
|
||||
type connCompletedFn func(success bool) error
|
||||
type disconnectCompletedFn func()
|
||||
type connectionLostHandledFn func(bool) (connCompletedFn, error)
|
||||
|
||||
/* State transitions
|
||||
|
||||
static states are `disconnected` and `connected`. For all other states a process will hold a function that will move
|
||||
the state to one of those. That function effectively owns the state and any other changes must not proceed until it
|
||||
completes. One exception to that is that the state can always be moved to `disconnecting` which provides a signal that
|
||||
transitions to `connected` will be rejected (this is required because a Disconnect can be requested while in the
|
||||
Connecting state).
|
||||
|
||||
# Basic Operations
|
||||
|
||||
The standard workflows are:
|
||||
|
||||
disconnected -> `Connecting()` -> connecting -> `connCompletedFn(true)` -> connected
|
||||
connected -> `Disconnecting()` -> disconnecting -> `disconnectCompletedFn()` -> disconnected
|
||||
connected -> `ConnectionLost(false)` -> disconnecting -> `connectionLostHandledFn(true/false)` -> disconnected
|
||||
connected -> `ConnectionLost(true)` -> disconnecting -> `connectionLostHandledFn(true)` -> connected
|
||||
|
||||
Unfortunately the above workflows are complicated by the fact that `Disconnecting()` or `ConnectionLost()` may,
|
||||
potentially, be called at any time (i.e. whilst in the middle of transitioning between states). If this happens:
|
||||
|
||||
* The state will be set to disconnecting (which will prevent any request to move the status to connected)
|
||||
* The call to `Disconnecting()`/`ConnectionLost()` will block until the previously active call completes and then
|
||||
handle the disconnection.
|
||||
|
||||
Reading the tests (unit_status_test.go) might help understand these rules.
|
||||
*/
|
||||
|
||||
var (
|
||||
errAbortConnection = errors.New("disconnect called whist connection attempt in progress")
|
||||
errAlreadyConnectedOrReconnecting = errors.New("status is already connected or reconnecting")
|
||||
errStatusMustBeDisconnected = errors.New("status can only transition to connecting from disconnected")
|
||||
errAlreadyDisconnected = errors.New("status is already disconnected")
|
||||
errDisconnectionRequested = errors.New("disconnection was requested whilst the action was in progress")
|
||||
errDisconnectionInProgress = errors.New("disconnection already in progress")
|
||||
errAlreadyHandlingConnectionLoss = errors.New("status is already Connection Lost")
|
||||
errConnLossWhileDisconnecting = errors.New("connection status is disconnecting so loss of connection is expected")
|
||||
)
|
||||
|
||||
// connectionStatus encapsulates, and protects, the connection status.
|
||||
type connectionStatus struct {
|
||||
sync.RWMutex // Protects the variables below
|
||||
status status
|
||||
willReconnect bool // only used when status == disconnecting. Indicates that an attempt will be made to reconnect (allows us to abort that)
|
||||
|
||||
// Some statuses are transitional (e.g. connecting, connectionLost, reconnecting, disconnecting), that is, whatever
|
||||
// process moves us into that status will move us out of it when an action is complete. Sometimes other users
|
||||
// will need to know when the action is complete (e.g. the user calls `Disconnect()` whilst the status is
|
||||
// `connecting`). `actionCompleted` will be set whenever we move into one of the above statues and the channel
|
||||
// returned to anything else requesting a status change. The channel will be closed when the operation is complete.
|
||||
actionCompleted chan struct{} // Only valid whilst status is Connecting or Reconnecting; will be closed when connection completed (success or failure)
|
||||
}
|
||||
|
||||
// ConnectionStatus returns the connection status.
|
||||
// WARNING: the status may change at any time so users should not assume they are the only goroutine touching this
|
||||
func (c *connectionStatus) ConnectionStatus() status {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.status
|
||||
}
|
||||
|
||||
// ConnectionStatusRetry returns the connection status and retry flag (indicates that we expect to reconnect).
|
||||
// WARNING: the status may change at any time so users should not assume they are the only goroutine touching this
|
||||
func (c *connectionStatus) ConnectionStatusRetry() (status, bool) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.status, c.willReconnect
|
||||
}
|
||||
|
||||
// Connecting - Changes the status to connecting if that is a permitted operation
|
||||
// Will do nothing unless the current status is disconnected
|
||||
// Returns a function that MUST be called when the operation is complete (pass in true if successful)
|
||||
func (c *connectionStatus) Connecting() (connCompletedFn, error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
// Calling Connect when already connecting (or if reconnecting) may not always be considered an error
|
||||
if c.status == connected || c.status == reconnecting {
|
||||
return nil, errAlreadyConnectedOrReconnecting
|
||||
}
|
||||
if c.status != disconnected {
|
||||
return nil, errStatusMustBeDisconnected
|
||||
}
|
||||
c.status = connecting
|
||||
c.actionCompleted = make(chan struct{})
|
||||
return c.connected, nil
|
||||
}
|
||||
|
||||
// connected is an internal function (it is returned by functions that set the status to connecting or reconnecting,
|
||||
// calling it completes the operation). `success` is used to indicate whether the operation was successfully completed.
|
||||
func (c *connectionStatus) connected(success bool) error {
|
||||
c.Lock()
|
||||
defer func() {
|
||||
close(c.actionCompleted) // Alert anything waiting on the connection process to complete
|
||||
c.actionCompleted = nil // Be tidy
|
||||
c.Unlock()
|
||||
}()
|
||||
|
||||
// Status may have moved to disconnecting in the interim (i.e. at users request)
|
||||
if c.status == disconnecting {
|
||||
return errAbortConnection
|
||||
}
|
||||
if success {
|
||||
c.status = connected
|
||||
} else {
|
||||
c.status = disconnected
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Disconnecting - should be called when beginning the disconnection process (cleanup etc.).
|
||||
// Can be called from ANY status and the end result will always be a status of disconnected
|
||||
// Note that if a connection/reconnection attempt is in progress this function will set the status to `disconnecting`
|
||||
// then block until the connection process completes (or aborts).
|
||||
// Returns a function that MUST be called when the operation is complete (assumed to always be successful!)
|
||||
func (c *connectionStatus) Disconnecting() (disconnectCompletedFn, error) {
|
||||
c.Lock()
|
||||
if c.status == disconnected {
|
||||
c.Unlock()
|
||||
return nil, errAlreadyDisconnected // May not always be treated as an error
|
||||
}
|
||||
if c.status == disconnecting { // Need to wait for existing process to complete
|
||||
c.willReconnect = false // Ensure that the existing disconnect process will not reconnect
|
||||
disConnectDone := c.actionCompleted
|
||||
c.Unlock()
|
||||
<-disConnectDone // Wait for existing operation to complete
|
||||
return nil, errAlreadyDisconnected // Well we are now!
|
||||
}
|
||||
|
||||
prevStatus := c.status
|
||||
c.status = disconnecting
|
||||
|
||||
// We may need to wait for connection/reconnection process to complete (they should regularly check the status)
|
||||
if prevStatus == connecting || prevStatus == reconnecting {
|
||||
connectDone := c.actionCompleted
|
||||
c.Unlock() // Safe because the only way to leave the disconnecting status is via this function
|
||||
<-connectDone
|
||||
|
||||
if prevStatus == reconnecting && !c.willReconnect {
|
||||
return nil, errAlreadyDisconnected // Following connectionLost process we will be disconnected
|
||||
}
|
||||
c.Lock()
|
||||
}
|
||||
c.actionCompleted = make(chan struct{})
|
||||
c.Unlock()
|
||||
return c.disconnectionCompleted, nil
|
||||
}
|
||||
|
||||
// disconnectionCompleted is an internal function (it is returned by functions that set the status to disconnecting)
|
||||
func (c *connectionStatus) disconnectionCompleted() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.status = disconnected
|
||||
close(c.actionCompleted) // Alert anything waiting on the connection process to complete
|
||||
c.actionCompleted = nil
|
||||
}
|
||||
|
||||
// ConnectionLost - should be called when the connection is lost.
|
||||
// This really only differs from Disconnecting in that we may transition into a reconnection (but that could be
|
||||
// cancelled something else calls Disconnecting in the meantime).
|
||||
// The returned function should be called when cleanup is completed. It will return a function to be called when
|
||||
// reconnect completes (or nil if no reconnect requested/disconnect called in the interim).
|
||||
// Note: This function may block if a connection is in progress (the move to connected will be rejected)
|
||||
func (c *connectionStatus) ConnectionLost(willReconnect bool) (connectionLostHandledFn, error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.status == disconnected {
|
||||
return nil, errAlreadyDisconnected
|
||||
}
|
||||
if c.status == disconnecting { // its expected that connection lost will be called during the disconnection process
|
||||
return nil, errDisconnectionInProgress
|
||||
}
|
||||
|
||||
c.willReconnect = willReconnect
|
||||
prevStatus := c.status
|
||||
c.status = disconnecting
|
||||
|
||||
// There is a slight possibility that a connection attempt is in progress (connection up and goroutines started but
|
||||
// status not yet changed). By changing the status we ensure that process will exit cleanly
|
||||
if prevStatus == connecting || prevStatus == reconnecting {
|
||||
connectDone := c.actionCompleted
|
||||
c.Unlock() // Safe because the only way to leave the disconnecting status is via this function
|
||||
<-connectDone
|
||||
c.Lock()
|
||||
if !willReconnect {
|
||||
// In this case the connection will always be aborted so there is nothing more for us to do
|
||||
return nil, errAlreadyDisconnected
|
||||
}
|
||||
}
|
||||
c.actionCompleted = make(chan struct{})
|
||||
|
||||
return c.getConnectionLostHandler(willReconnect), nil
|
||||
}
|
||||
|
||||
// getConnectionLostHandler is an internal function. It returns the function to be returned by ConnectionLost
|
||||
func (c *connectionStatus) getConnectionLostHandler(reconnectRequested bool) connectionLostHandledFn {
|
||||
return func(proceed bool) (connCompletedFn, error) {
|
||||
// Note that connCompletedFn will only be provided if both reconnectRequested and proceed are true
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
// `Disconnecting()` may have been called while the disconnection was being processed (this makes it permanent!)
|
||||
if !c.willReconnect || !proceed {
|
||||
c.status = disconnected
|
||||
close(c.actionCompleted) // Alert anything waiting on the connection process to complete
|
||||
c.actionCompleted = nil
|
||||
if !reconnectRequested || !proceed {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errDisconnectionRequested
|
||||
}
|
||||
|
||||
c.status = reconnecting
|
||||
return c.connected, nil // Note that c.actionCompleted is still live and will be closed in connected
|
||||
}
|
||||
}
|
||||
|
||||
// forceConnectionStatus - forces the connection status to the specified value.
|
||||
// This should only be used when there is no alternative (i.e. only in tests and to recover from situations that
|
||||
// are unexpected)
|
||||
func (c *connectionStatus) forceConnectionStatus(s status) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.status = s
|
||||
}
|
3
vendor/github.com/golang/protobuf/AUTHORS
generated
vendored
3
vendor/github.com/golang/protobuf/AUTHORS
generated
vendored
@ -1,3 +0,0 @@
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
3
vendor/github.com/golang/protobuf/CONTRIBUTORS
generated
vendored
3
vendor/github.com/golang/protobuf/CONTRIBUTORS
generated
vendored
@ -1,3 +0,0 @@
|
||||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
28
vendor/github.com/golang/protobuf/LICENSE
generated
vendored
28
vendor/github.com/golang/protobuf/LICENSE
generated
vendored
@ -1,28 +0,0 @@
|
||||
Copyright 2010 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
64
vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
generated
vendored
64
vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
generated
vendored
@ -1,64 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/golang/protobuf/ptypes/timestamp/timestamp.proto
|
||||
|
||||
package timestamp
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// Symbols defined in public import of google/protobuf/timestamp.proto.
|
||||
|
||||
type Timestamp = timestamppb.Timestamp
|
||||
|
||||
var File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = []byte{
|
||||
0x0a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x37,
|
||||
0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3b, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = []interface{}{}
|
||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() }
|
||||
func file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() {
|
||||
if File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes,
|
||||
DependencyIndexes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs,
|
||||
}.Build()
|
||||
File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto = out.File
|
||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = nil
|
||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = nil
|
||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = nil
|
||||
}
|
20
vendor/github.com/gorilla/websocket/.editorconfig
generated
vendored
Normal file
20
vendor/github.com/gorilla/websocket/.editorconfig
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
; https://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
eclint_indent_style = unset
|
26
vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
26
vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
@ -1,25 +1 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
coverage.coverprofile
|
||||
|
3
vendor/github.com/gorilla/websocket/.golangci.yml
generated
vendored
Normal file
3
vendor/github.com/gorilla/websocket/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
run:
|
||||
skip-dirs:
|
||||
- examples/*.go
|
9
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
9
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
@ -1,9 +0,0 @@
|
||||
# This is the official list of Gorilla WebSocket authors for copyright
|
||||
# purposes.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Gary Burd <gary@beagledreams.com>
|
||||
Google LLC (https://opensource.google.com/)
|
||||
Joachim Bauch <mail@joachim-bauch.de>
|
||||
|
39
vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
39
vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
@ -1,22 +1,27 @@
|
||||
Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
Copyright (c) 2023 The Gorilla Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
34
vendor/github.com/gorilla/websocket/Makefile
generated
vendored
Normal file
34
vendor/github.com/gorilla/websocket/Makefile
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '')
|
||||
GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
|
||||
GO_SEC=$(shell which gosec 2> /dev/null || echo '')
|
||||
GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest
|
||||
|
||||
GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '')
|
||||
GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest
|
||||
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint:
|
||||
$(if $(GO_LINT), ,go install $(GO_LINT_URI))
|
||||
@echo "##### Running golangci-lint"
|
||||
golangci-lint run -v
|
||||
|
||||
.PHONY: gosec
|
||||
gosec:
|
||||
$(if $(GO_SEC), ,go install $(GO_SEC_URI))
|
||||
@echo "##### Running gosec"
|
||||
gosec -exclude-dir examples ./...
|
||||
|
||||
.PHONY: govulncheck
|
||||
govulncheck:
|
||||
$(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI))
|
||||
@echo "##### Running govulncheck"
|
||||
govulncheck ./...
|
||||
|
||||
.PHONY: verify
|
||||
verify: golangci-lint gosec govulncheck
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@echo "##### Running tests"
|
||||
go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./...
|
48
vendor/github.com/gorilla/websocket/README.md
generated
vendored
48
vendor/github.com/gorilla/websocket/README.md
generated
vendored
@ -1,10 +1,14 @@
|
||||
# Gorilla WebSocket
|
||||
# gorilla/websocket
|
||||
|
||||
[](https://godoc.org/github.com/gorilla/websocket)
|
||||
[](https://circleci.com/gh/gorilla/websocket)
|
||||

|
||||
[](https://codecov.io/github/gorilla/websocket)
|
||||
[](https://godoc.org/github.com/gorilla/websocket)
|
||||
[](https://sourcegraph.com/github.com/gorilla/websocket?badge)
|
||||
|
||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||
|
||||

|
||||
|
||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||
|
||||
### Documentation
|
||||
|
||||
@ -13,6 +17,7 @@ Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
||||
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
|
||||
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
|
||||
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
|
||||
* [Write buffer pool example](https://github.com/gorilla/websocket/tree/master/examples/bufferpool)
|
||||
|
||||
### Status
|
||||
|
||||
@ -29,36 +34,3 @@ package API is stable.
|
||||
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
|
||||
Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn
|
||||
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
|
||||
|
||||
### Gorilla WebSocket compared with other packages
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><a href="http://godoc.org/github.com/gorilla/websocket">github.com/gorilla</a></th>
|
||||
<th><a href="http://godoc.org/golang.org/x/net/websocket">golang.org/x/net</a></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr><td colspan="3"><a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> Features</td></tr>
|
||||
<tr><td>Passes <a href="https://github.com/crossbario/autobahn-testsuite">Autobahn Test Suite</a></td><td><a href="https://github.com/gorilla/websocket/tree/master/examples/autobahn">Yes</a></td><td>No</td></tr>
|
||||
<tr><td>Receive <a href="https://tools.ietf.org/html/rfc6455#section-5.4">fragmented</a> message<td>Yes</td><td><a href="https://code.google.com/p/go/issues/detail?id=7632">No</a>, see note 1</td></tr>
|
||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close</a> message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=4588">No</a></td></tr>
|
||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr>
|
||||
<tr><td>Get the <a href="https://tools.ietf.org/html/rfc6455#section-5.6">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>
|
||||
<tr><td colspan="3">Other Features</tr></td>
|
||||
<tr><td><a href="https://tools.ietf.org/html/rfc7692">Compression Extensions</a></td><td>Experimental</td><td>No</td></tr>
|
||||
<tr><td>Read message using io.Reader</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextReader">Yes</a></td><td>No, see note 3</td></tr>
|
||||
<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
|
||||
</table>
|
||||
|
||||
Notes:
|
||||
|
||||
1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html).
|
||||
2. The application can get the type of a received data message by implementing
|
||||
a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal)
|
||||
function.
|
||||
3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries.
|
||||
Read returns when the input buffer is full or a frame boundary is
|
||||
encountered. Each call to Write sends a single frame message. The Gorilla
|
||||
io.Reader and io.WriteCloser operate on a single WebSocket message.
|
||||
|
||||
|
119
vendor/github.com/gorilla/websocket/client.go
generated
vendored
119
vendor/github.com/gorilla/websocket/client.go
generated
vendored
@ -9,14 +9,18 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
// ErrBadHandshake is returned when the server response to opening handshake is
|
||||
@ -48,15 +52,23 @@ func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufS
|
||||
}
|
||||
|
||||
// A Dialer contains options for connecting to WebSocket server.
|
||||
//
|
||||
// It is safe to call Dialer's methods concurrently.
|
||||
type Dialer struct {
|
||||
// NetDial specifies the dial function for creating TCP connections. If
|
||||
// NetDial is nil, net.Dial is used.
|
||||
NetDial func(network, addr string) (net.Conn, error)
|
||||
|
||||
// NetDialContext specifies the dial function for creating TCP connections. If
|
||||
// NetDialContext is nil, net.DialContext is used.
|
||||
// NetDialContext is nil, NetDial is used.
|
||||
NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
// NetDialTLSContext specifies the dial function for creating TLS/TCP connections. If
|
||||
// NetDialTLSContext is nil, NetDialContext is used.
|
||||
// If NetDialTLSContext is set, Dial assumes the TLS handshake is done there and
|
||||
// TLSClientConfig is ignored.
|
||||
NetDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
// Proxy specifies a function to return a proxy for a given
|
||||
// Request. If the function returns a non-nil error, the
|
||||
// request is aborted with the provided error.
|
||||
@ -65,6 +77,8 @@ type Dialer struct {
|
||||
|
||||
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
|
||||
// If nil, the default configuration is used.
|
||||
// If either NetDialTLS or NetDialTLSContext are set, Dial assumes the TLS handshake
|
||||
// is done there and TLSClientConfig is ignored.
|
||||
TLSClientConfig *tls.Config
|
||||
|
||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||
@ -176,7 +190,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
}
|
||||
|
||||
req := &http.Request{
|
||||
Method: "GET",
|
||||
Method: http.MethodGet,
|
||||
URL: u,
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
@ -214,6 +228,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
k == "Connection" ||
|
||||
k == "Sec-Websocket-Key" ||
|
||||
k == "Sec-Websocket-Version" ||
|
||||
//#nosec G101 (CWE-798): Potential HTTP request smuggling via parameter pollution
|
||||
k == "Sec-Websocket-Extensions" ||
|
||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||
@ -237,13 +252,32 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
// Get network dial function.
|
||||
var netDial func(network, add string) (net.Conn, error)
|
||||
|
||||
if d.NetDialContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialContext(ctx, network, addr)
|
||||
switch u.Scheme {
|
||||
case "http":
|
||||
if d.NetDialContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDial != nil {
|
||||
netDial = d.NetDial
|
||||
}
|
||||
} else if d.NetDial != nil {
|
||||
netDial = d.NetDial
|
||||
} else {
|
||||
case "https":
|
||||
if d.NetDialTLSContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialTLSContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDialContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDial != nil {
|
||||
netDial = d.NetDial
|
||||
}
|
||||
default:
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
if netDial == nil {
|
||||
netDialer := &net.Dialer{}
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return netDialer.DialContext(ctx, network, addr)
|
||||
@ -260,7 +294,9 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
}
|
||||
err = c.SetDeadline(deadline)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
if err := c.Close(); err != nil {
|
||||
log.Printf("websocket: failed to close network connection: %v", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
@ -274,7 +310,7 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
return nil, nil, err
|
||||
}
|
||||
if proxyURL != nil {
|
||||
dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))
|
||||
dialer, err := proxy.FromURL(proxyURL, netDialerFunc(netDial))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -289,22 +325,26 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
}
|
||||
|
||||
netConn, err := netDial("tcp", hostPort)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if trace != nil && trace.GotConn != nil {
|
||||
trace.GotConn(httptrace.GotConnInfo{
|
||||
Conn: netConn,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if netConn != nil {
|
||||
netConn.Close()
|
||||
if err := netConn.Close(); err != nil {
|
||||
log.Printf("websocket: failed to close network connection: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if u.Scheme == "https" {
|
||||
if u.Scheme == "https" && d.NetDialTLSContext == nil {
|
||||
// If NetDialTLSContext is set, assume that the TLS handshake has already been done
|
||||
|
||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||
if cfg.ServerName == "" {
|
||||
cfg.ServerName = hostNoPort
|
||||
@ -312,11 +352,12 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
tlsConn := tls.Client(netConn, cfg)
|
||||
netConn = tlsConn
|
||||
|
||||
var err error
|
||||
if trace != nil {
|
||||
err = doHandshakeWithTrace(trace, tlsConn, cfg)
|
||||
} else {
|
||||
err = doHandshake(tlsConn, cfg)
|
||||
if trace != nil && trace.TLSHandshakeStart != nil {
|
||||
trace.TLSHandshakeStart()
|
||||
}
|
||||
err := doHandshake(ctx, tlsConn, cfg)
|
||||
if trace != nil && trace.TLSHandshakeDone != nil {
|
||||
trace.TLSHandshakeDone(tlsConn.ConnectionState(), err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -338,6 +379,17 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
|
||||
resp, err := http.ReadResponse(conn.br, req)
|
||||
if err != nil {
|
||||
if d.TLSClientConfig != nil {
|
||||
for _, proto := range d.TLSClientConfig.NextProtos {
|
||||
if proto != "http/1.1" {
|
||||
return nil, nil, fmt.Errorf(
|
||||
"websocket: protocol %q was given but is not supported;"+
|
||||
"sharing tls.Config with net/http Transport can cause this error: %w",
|
||||
proto, err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@ -348,15 +400,15 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
}
|
||||
|
||||
if resp.StatusCode != 101 ||
|
||||
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
|
||||
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
|
||||
!tokenListContainsValue(resp.Header, "Upgrade", "websocket") ||
|
||||
!tokenListContainsValue(resp.Header, "Connection", "upgrade") ||
|
||||
resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
|
||||
// Before closing the network connection on return from this
|
||||
// function, slurp up some of the response to aid application
|
||||
// debugging.
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := io.ReadFull(resp.Body, buf)
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
|
||||
resp.Body = io.NopCloser(bytes.NewReader(buf[:n]))
|
||||
return nil, resp, ErrBadHandshake
|
||||
}
|
||||
|
||||
@ -374,22 +426,19 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
break
|
||||
}
|
||||
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||
resp.Body = io.NopCloser(bytes.NewReader([]byte{}))
|
||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||
|
||||
netConn.SetDeadline(time.Time{})
|
||||
if err := netConn.SetDeadline(time.Time{}); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
netConn = nil // to avoid close in defer.
|
||||
return conn, resp, nil
|
||||
}
|
||||
|
||||
func doHandshake(tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return err
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{MinVersion: tls.VersionTLS12}
|
||||
}
|
||||
if !cfg.InsecureSkipVerify {
|
||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return cfg.Clone()
|
||||
}
|
||||
|
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
@ -1,16 +0,0 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return cfg.Clone()
|
||||
}
|
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
@ -1,38 +0,0 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
// cloneTLSConfig clones all public fields except the fields
|
||||
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
|
||||
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
|
||||
// config in active use.
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return &tls.Config{
|
||||
Rand: cfg.Rand,
|
||||
Time: cfg.Time,
|
||||
Certificates: cfg.Certificates,
|
||||
NameToCertificate: cfg.NameToCertificate,
|
||||
GetCertificate: cfg.GetCertificate,
|
||||
RootCAs: cfg.RootCAs,
|
||||
NextProtos: cfg.NextProtos,
|
||||
ServerName: cfg.ServerName,
|
||||
ClientAuth: cfg.ClientAuth,
|
||||
ClientCAs: cfg.ClientCAs,
|
||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||
CipherSuites: cfg.CipherSuites,
|
||||
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
||||
ClientSessionCache: cfg.ClientSessionCache,
|
||||
MinVersion: cfg.MinVersion,
|
||||
MaxVersion: cfg.MaxVersion,
|
||||
CurvePreferences: cfg.CurvePreferences,
|
||||
}
|
||||
}
|
9
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
9
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
@ -8,6 +8,7 @@ import (
|
||||
"compress/flate"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
@ -33,7 +34,9 @@ func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
|
||||
"\x01\x00\x00\xff\xff"
|
||||
|
||||
fr, _ := flateReaderPool.Get().(io.ReadCloser)
|
||||
fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
|
||||
if err := fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &flateReadWrapper{fr}
|
||||
}
|
||||
|
||||
@ -132,7 +135,9 @@ func (r *flateReadWrapper) Read(p []byte) (int, error) {
|
||||
// Preemptively place the reader back in the pool. This helps with
|
||||
// scenarios where the application does not call NextReader() soon after
|
||||
// this final read.
|
||||
r.Close()
|
||||
if err := r.Close(); err != nil {
|
||||
log.Printf("websocket: flateReadWrapper.Close() returned error: %v", err)
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
148
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
148
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
@ -6,13 +6,14 @@ package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
@ -180,13 +181,20 @@ var (
|
||||
errInvalidControlFrame = errors.New("websocket: invalid control frame")
|
||||
)
|
||||
|
||||
// maskRand is an io.Reader for generating mask bytes. The reader is initialized
|
||||
// to crypto/rand Reader. Tests swap the reader to a math/rand reader for
|
||||
// reproducible results.
|
||||
var maskRand = rand.Reader
|
||||
|
||||
// newMaskKey returns a new 32 bit value for masking client frames.
|
||||
func newMaskKey() [4]byte {
|
||||
n := rand.Uint32()
|
||||
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
||||
var k [4]byte
|
||||
_, _ = io.ReadFull(maskRand, k[:])
|
||||
return k
|
||||
}
|
||||
|
||||
func hideTempErr(err error) error {
|
||||
if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||
if e, ok := err.(net.Error); ok {
|
||||
err = &netError{msg: e.Error(), timeout: e.Timeout()}
|
||||
}
|
||||
return err
|
||||
@ -371,7 +379,9 @@ func (c *Conn) read(n int) ([]byte, error) {
|
||||
if err == io.EOF {
|
||||
err = errUnexpectedEOF
|
||||
}
|
||||
c.br.Discard(len(p))
|
||||
if _, err := c.br.Discard(len(p)); err != nil {
|
||||
return p, err
|
||||
}
|
||||
return p, err
|
||||
}
|
||||
|
||||
@ -386,7 +396,9 @@ func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error
|
||||
return err
|
||||
}
|
||||
|
||||
c.conn.SetWriteDeadline(deadline)
|
||||
if err := c.conn.SetWriteDeadline(deadline); err != nil {
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
if len(buf1) == 0 {
|
||||
_, err = c.conn.Write(buf0)
|
||||
} else {
|
||||
@ -396,11 +408,17 @@ func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
if frameType == CloseMessage {
|
||||
c.writeFatal(ErrCloseSent)
|
||||
_ = c.writeFatal(ErrCloseSent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) writeBufs(bufs ...[]byte) error {
|
||||
b := net.Buffers(bufs)
|
||||
_, err := b.WriteTo(c.conn)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteControl writes a control message with the given deadline. The allowed
|
||||
// message types are CloseMessage, PingMessage and PongMessage.
|
||||
func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error {
|
||||
@ -431,7 +449,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
||||
|
||||
d := 1000 * time.Hour
|
||||
if !deadline.IsZero() {
|
||||
d = deadline.Sub(time.Now())
|
||||
d = time.Until(deadline)
|
||||
if d < 0 {
|
||||
return errWriteTimeout
|
||||
}
|
||||
@ -453,13 +471,15 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
|
||||
return err
|
||||
}
|
||||
|
||||
c.conn.SetWriteDeadline(deadline)
|
||||
if err := c.conn.SetWriteDeadline(deadline); err != nil {
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
_, err = c.conn.Write(buf)
|
||||
if err != nil {
|
||||
return c.writeFatal(err)
|
||||
}
|
||||
if messageType == CloseMessage {
|
||||
c.writeFatal(ErrCloseSent)
|
||||
_ = c.writeFatal(ErrCloseSent)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -470,7 +490,9 @@ func (c *Conn) beginMessage(mw *messageWriter, messageType int) error {
|
||||
// probably better to return an error in this situation, but we cannot
|
||||
// change this without breaking existing applications.
|
||||
if c.writer != nil {
|
||||
c.writer.Close()
|
||||
if err := c.writer.Close(); err != nil {
|
||||
log.Printf("websocket: discarding writer close error: %v", err)
|
||||
}
|
||||
c.writer = nil
|
||||
}
|
||||
|
||||
@ -623,7 +645,7 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error {
|
||||
}
|
||||
|
||||
if final {
|
||||
w.endMessage(errWriteClosed)
|
||||
_ = w.endMessage(errWriteClosed)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -788,53 +810,77 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
// 1. Skip remainder of previous frame.
|
||||
|
||||
if c.readRemaining > 0 {
|
||||
if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil {
|
||||
if _, err := io.CopyN(io.Discard, c.br, c.readRemaining); err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Read and parse first two bytes of frame header.
|
||||
// To aid debugging, collect and report all errors in the first two bytes
|
||||
// of the header.
|
||||
|
||||
var errors []string
|
||||
|
||||
p, err := c.read(2)
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
|
||||
final := p[0]&finalBit != 0
|
||||
frameType := int(p[0] & 0xf)
|
||||
final := p[0]&finalBit != 0
|
||||
rsv1 := p[0]&rsv1Bit != 0
|
||||
rsv2 := p[0]&rsv2Bit != 0
|
||||
rsv3 := p[0]&rsv3Bit != 0
|
||||
mask := p[1]&maskBit != 0
|
||||
c.setReadRemaining(int64(p[1] & 0x7f))
|
||||
|
||||
c.readDecompress = false
|
||||
if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 {
|
||||
c.readDecompress = true
|
||||
p[0] &^= rsv1Bit
|
||||
if err := c.setReadRemaining(int64(p[1] & 0x7f)); err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
|
||||
if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 {
|
||||
return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16))
|
||||
c.readDecompress = false
|
||||
if rsv1 {
|
||||
if c.newDecompressionReader != nil {
|
||||
c.readDecompress = true
|
||||
} else {
|
||||
errors = append(errors, "RSV1 set")
|
||||
}
|
||||
}
|
||||
|
||||
if rsv2 {
|
||||
errors = append(errors, "RSV2 set")
|
||||
}
|
||||
|
||||
if rsv3 {
|
||||
errors = append(errors, "RSV3 set")
|
||||
}
|
||||
|
||||
switch frameType {
|
||||
case CloseMessage, PingMessage, PongMessage:
|
||||
if c.readRemaining > maxControlFramePayloadSize {
|
||||
return noFrame, c.handleProtocolError("control frame length > 125")
|
||||
errors = append(errors, "len > 125 for control")
|
||||
}
|
||||
if !final {
|
||||
return noFrame, c.handleProtocolError("control frame not final")
|
||||
errors = append(errors, "FIN not set on control")
|
||||
}
|
||||
case TextMessage, BinaryMessage:
|
||||
if !c.readFinal {
|
||||
return noFrame, c.handleProtocolError("message start before final message frame")
|
||||
errors = append(errors, "data before FIN")
|
||||
}
|
||||
c.readFinal = final
|
||||
case continuationFrame:
|
||||
if c.readFinal {
|
||||
return noFrame, c.handleProtocolError("continuation after final message frame")
|
||||
errors = append(errors, "continuation after FIN")
|
||||
}
|
||||
c.readFinal = final
|
||||
default:
|
||||
return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType))
|
||||
errors = append(errors, "bad opcode "+strconv.Itoa(frameType))
|
||||
}
|
||||
|
||||
if mask != c.isServer {
|
||||
errors = append(errors, "bad MASK")
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return noFrame, c.handleProtocolError(strings.Join(errors, ", "))
|
||||
}
|
||||
|
||||
// 3. Read and parse frame length as per
|
||||
@ -872,10 +918,6 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
|
||||
// 4. Handle frame masking.
|
||||
|
||||
if mask != c.isServer {
|
||||
return noFrame, c.handleProtocolError("incorrect mask flag")
|
||||
}
|
||||
|
||||
if mask {
|
||||
c.readMaskPos = 0
|
||||
p, err := c.read(len(c.readMaskKey))
|
||||
@ -897,7 +939,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
}
|
||||
|
||||
if c.readLimit > 0 && c.readLength > c.readLimit {
|
||||
c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait))
|
||||
if err := c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)); err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
return noFrame, ErrReadLimit
|
||||
}
|
||||
|
||||
@ -909,7 +953,9 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
var payload []byte
|
||||
if c.readRemaining > 0 {
|
||||
payload, err = c.read(int(c.readRemaining))
|
||||
c.setReadRemaining(0)
|
||||
if err := c.setReadRemaining(0); err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
if err != nil {
|
||||
return noFrame, err
|
||||
}
|
||||
@ -935,7 +981,7 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
if len(payload) >= 2 {
|
||||
closeCode = int(binary.BigEndian.Uint16(payload))
|
||||
if !isValidReceivedCloseCode(closeCode) {
|
||||
return noFrame, c.handleProtocolError("invalid close code")
|
||||
return noFrame, c.handleProtocolError("bad close code " + strconv.Itoa(closeCode))
|
||||
}
|
||||
closeText = string(payload[2:])
|
||||
if !utf8.ValidString(closeText) {
|
||||
@ -952,7 +998,13 @@ func (c *Conn) advanceFrame() (int, error) {
|
||||
}
|
||||
|
||||
func (c *Conn) handleProtocolError(message string) error {
|
||||
c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait))
|
||||
data := FormatCloseMessage(CloseProtocolError, message)
|
||||
if len(data) > maxControlFramePayloadSize {
|
||||
data = data[:maxControlFramePayloadSize]
|
||||
}
|
||||
if err := c.WriteControl(CloseMessage, data, time.Now().Add(writeWait)); err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New("websocket: " + message)
|
||||
}
|
||||
|
||||
@ -969,7 +1021,9 @@ func (c *Conn) handleProtocolError(message string) error {
|
||||
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
||||
// Close previous reader, only relevant for decompression.
|
||||
if c.reader != nil {
|
||||
c.reader.Close()
|
||||
if err := c.reader.Close(); err != nil {
|
||||
log.Printf("websocket: discarding reader close error: %v", err)
|
||||
}
|
||||
c.reader = nil
|
||||
}
|
||||
|
||||
@ -1025,7 +1079,9 @@ func (r *messageReader) Read(b []byte) (int, error) {
|
||||
}
|
||||
rem := c.readRemaining
|
||||
rem -= int64(n)
|
||||
c.setReadRemaining(rem)
|
||||
if err := c.setReadRemaining(rem); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if c.readRemaining > 0 && c.readErr == io.EOF {
|
||||
c.readErr = errUnexpectedEOF
|
||||
}
|
||||
@ -1065,7 +1121,7 @@ func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
|
||||
if err != nil {
|
||||
return messageType, nil, err
|
||||
}
|
||||
p, err = ioutil.ReadAll(r)
|
||||
p, err = io.ReadAll(r)
|
||||
return messageType, p, err
|
||||
}
|
||||
|
||||
@ -1107,7 +1163,9 @@ func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
|
||||
if h == nil {
|
||||
h = func(code int, text string) error {
|
||||
message := FormatCloseMessage(code, "")
|
||||
c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
|
||||
if err := c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1132,7 +1190,7 @@ func (c *Conn) SetPingHandler(h func(appData string) error) {
|
||||
err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))
|
||||
if err == ErrCloseSent {
|
||||
return nil
|
||||
} else if e, ok := err.(net.Error); ok && e.Temporary() {
|
||||
} else if _, ok := err.(net.Error); ok {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
@ -1160,8 +1218,16 @@ func (c *Conn) SetPongHandler(h func(appData string) error) {
|
||||
c.handlePong = h
|
||||
}
|
||||
|
||||
// NetConn returns the underlying connection that is wrapped by c.
|
||||
// Note that writing to or reading from this connection directly will corrupt the
|
||||
// WebSocket connection.
|
||||
func (c *Conn) NetConn() net.Conn {
|
||||
return c.conn
|
||||
}
|
||||
|
||||
// UnderlyingConn returns the internal net.Conn. This can be used to further
|
||||
// modifications to connection specific flags.
|
||||
// Deprecated: Use the NetConn method.
|
||||
func (c *Conn) UnderlyingConn() net.Conn {
|
||||
return c.conn
|
||||
}
|
||||
|
15
vendor/github.com/gorilla/websocket/conn_write.go
generated
vendored
15
vendor/github.com/gorilla/websocket/conn_write.go
generated
vendored
@ -1,15 +0,0 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import "net"
|
||||
|
||||
func (c *Conn) writeBufs(bufs ...[]byte) error {
|
||||
b := net.Buffers(bufs)
|
||||
_, err := b.WriteTo(c.conn)
|
||||
return err
|
||||
}
|
18
vendor/github.com/gorilla/websocket/conn_write_legacy.go
generated
vendored
18
vendor/github.com/gorilla/websocket/conn_write_legacy.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
func (c *Conn) writeBufs(bufs ...[]byte) error {
|
||||
for _, buf := range bufs {
|
||||
if len(buf) > 0 {
|
||||
if _, err := c.conn.Write(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
5
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
5
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
@ -2,12 +2,14 @@
|
||||
// this source code is governed by a BSD-style license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
//go:build !appengine
|
||||
// +build !appengine
|
||||
|
||||
package websocket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// #nosec G103 -- (CWE-242) Has been audited
|
||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
@ -21,6 +23,7 @@ func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
}
|
||||
|
||||
// Mask one byte at a time to word boundary.
|
||||
//#nosec G103 -- (CWE-242) Has been audited
|
||||
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
||||
n = wordSize - n
|
||||
for i := range b[:n] {
|
||||
@ -35,11 +38,13 @@ func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
for i := range k {
|
||||
k[i] = key[(pos+i)&3]
|
||||
}
|
||||
//#nosec G103 -- (CWE-242) Has been audited
|
||||
kw := *(*uintptr)(unsafe.Pointer(&k))
|
||||
|
||||
// Mask one word at a time.
|
||||
n := (len(b) / wordSize) * wordSize
|
||||
for i := 0; i < n; i += wordSize {
|
||||
//#nosec G103 -- (CWE-242) Has been audited
|
||||
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
||||
}
|
||||
|
||||
|
1
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
1
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
@ -2,6 +2,7 @@
|
||||
// this source code is governed by a BSD-style license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
package websocket
|
||||
|
19
vendor/github.com/gorilla/websocket/proxy.go
generated
vendored
19
vendor/github.com/gorilla/websocket/proxy.go
generated
vendored
@ -8,10 +8,13 @@ import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
type netDialerFunc func(network, addr string) (net.Conn, error)
|
||||
@ -21,7 +24,7 @@ func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {
|
||||
proxy.RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil
|
||||
})
|
||||
}
|
||||
@ -48,14 +51,16 @@ func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
connectReq := &http.Request{
|
||||
Method: "CONNECT",
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Opaque: addr},
|
||||
Host: addr,
|
||||
Header: connectHeader,
|
||||
}
|
||||
|
||||
if err := connectReq.Write(conn); err != nil {
|
||||
conn.Close()
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Printf("httpProxyDialer: failed to close connection: %v", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -64,12 +69,16 @@ func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error)
|
||||
br := bufio.NewReader(conn)
|
||||
resp, err := http.ReadResponse(br, connectReq)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Printf("httpProxyDialer: failed to close connection: %v", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
conn.Close()
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Printf("httpProxyDialer: failed to close connection: %v", err)
|
||||
}
|
||||
f := strings.SplitN(resp.Status, " ", 2)
|
||||
return nil, errors.New(f[1])
|
||||
}
|
||||
|
50
vendor/github.com/gorilla/websocket/server.go
generated
vendored
50
vendor/github.com/gorilla/websocket/server.go
generated
vendored
@ -8,6 +8,7 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@ -23,6 +24,8 @@ func (e HandshakeError) Error() string { return e.message }
|
||||
|
||||
// Upgrader specifies parameters for upgrading an HTTP connection to a
|
||||
// WebSocket connection.
|
||||
//
|
||||
// It is safe to call Upgrader's methods concurrently.
|
||||
type Upgrader struct {
|
||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||
HandshakeTimeout time.Duration
|
||||
@ -115,8 +118,8 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header
|
||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||
//
|
||||
// The responseHeader is included in the response to the client's upgrade
|
||||
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
|
||||
// application negotiated subprotocol (Sec-WebSocket-Protocol).
|
||||
// request. Use the responseHeader to specify cookies (Set-Cookie). To specify
|
||||
// subprotocols supported by the server, set Upgrader.Subprotocols directly.
|
||||
//
|
||||
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
|
||||
// response.
|
||||
@ -131,7 +134,7 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
|
||||
}
|
||||
|
||||
if r.Method != "GET" {
|
||||
if r.Method != http.MethodGet {
|
||||
return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET")
|
||||
}
|
||||
|
||||
@ -152,8 +155,8 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
}
|
||||
|
||||
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
||||
if challengeKey == "" {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank")
|
||||
if !isValidChallengeKey(challengeKey) {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header must be Base64 encoded value of 16-byte in length")
|
||||
}
|
||||
|
||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||
@ -181,7 +184,9 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
}
|
||||
|
||||
if brw.Reader.Buffered() > 0 {
|
||||
netConn.Close()
|
||||
if err := netConn.Close(); err != nil {
|
||||
log.Printf("websocket: failed to close network connection: %v", err)
|
||||
}
|
||||
return nil, errors.New("websocket: client sent data before handshake is complete")
|
||||
}
|
||||
|
||||
@ -246,17 +251,34 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
p = append(p, "\r\n"...)
|
||||
|
||||
// Clear deadlines set by HTTP server.
|
||||
netConn.SetDeadline(time.Time{})
|
||||
if err := netConn.SetDeadline(time.Time{}); err != nil {
|
||||
if err := netConn.Close(); err != nil {
|
||||
log.Printf("websocket: failed to close network connection: %v", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.HandshakeTimeout > 0 {
|
||||
netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
|
||||
if err := netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)); err != nil {
|
||||
if err := netConn.Close(); err != nil {
|
||||
log.Printf("websocket: failed to close network connection: %v", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, err = netConn.Write(p); err != nil {
|
||||
netConn.Close()
|
||||
if err := netConn.Close(); err != nil {
|
||||
log.Printf("websocket: failed to close network connection: %v", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if u.HandshakeTimeout > 0 {
|
||||
netConn.SetWriteDeadline(time.Time{})
|
||||
if err := netConn.SetWriteDeadline(time.Time{}); err != nil {
|
||||
if err := netConn.Close(); err != nil {
|
||||
log.Printf("websocket: failed to close network connection: %v", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
@ -354,8 +376,12 @@ func bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte {
|
||||
// bufio.Writer's underlying writer.
|
||||
var wh writeHook
|
||||
bw.Reset(&wh)
|
||||
bw.WriteByte(0)
|
||||
bw.Flush()
|
||||
if err := bw.WriteByte(0); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := bw.Flush(); err != nil {
|
||||
log.Printf("websocket: bufioWriterBuffer: Flush: %v", err)
|
||||
}
|
||||
|
||||
bw.Reset(originalWriter)
|
||||
|
||||
|
18
vendor/github.com/gorilla/websocket/tls_handshake.go
generated
vendored
Normal file
18
vendor/github.com/gorilla/websocket/tls_handshake.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if !cfg.InsecureSkipVerify {
|
||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
19
vendor/github.com/gorilla/websocket/trace.go
generated
vendored
19
vendor/github.com/gorilla/websocket/trace.go
generated
vendored
@ -1,19 +0,0 @@
|
||||
// +build go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http/httptrace"
|
||||
)
|
||||
|
||||
func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
if trace.TLSHandshakeStart != nil {
|
||||
trace.TLSHandshakeStart()
|
||||
}
|
||||
err := doHandshake(tlsConn, cfg)
|
||||
if trace.TLSHandshakeDone != nil {
|
||||
trace.TLSHandshakeDone(tlsConn.ConnectionState(), err)
|
||||
}
|
||||
return err
|
||||
}
|
12
vendor/github.com/gorilla/websocket/trace_17.go
generated
vendored
12
vendor/github.com/gorilla/websocket/trace_17.go
generated
vendored
@ -1,12 +0,0 @@
|
||||
// +build !go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http/httptrace"
|
||||
)
|
||||
|
||||
func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
return doHandshake(tlsConn, cfg)
|
||||
}
|
19
vendor/github.com/gorilla/websocket/util.go
generated
vendored
19
vendor/github.com/gorilla/websocket/util.go
generated
vendored
@ -6,7 +6,7 @@ package websocket
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/sha1" //#nosec G505 -- (CWE-327) https://datatracker.ietf.org/doc/html/rfc6455#page-54
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -17,7 +17,7 @@ import (
|
||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||
|
||||
func computeAcceptKey(challengeKey string) string {
|
||||
h := sha1.New()
|
||||
h := sha1.New() //#nosec G401 -- (CWE-326) https://datatracker.ietf.org/doc/html/rfc6455#page-54
|
||||
h.Write([]byte(challengeKey))
|
||||
h.Write(keyGUID)
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
@ -281,3 +281,18 @@ headers:
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// isValidChallengeKey checks if the argument meets RFC6455 specification.
|
||||
func isValidChallengeKey(s string) bool {
|
||||
// From RFC6455:
|
||||
//
|
||||
// A |Sec-WebSocket-Key| header field with a base64-encoded (see
|
||||
// Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in
|
||||
// length.
|
||||
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
decoded, err := base64.StdEncoding.DecodeString(s)
|
||||
return err == nil && len(decoded) == 16
|
||||
}
|
||||
|
473
vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
473
vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
@ -1,473 +0,0 @@
|
||||
// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
|
||||
//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy
|
||||
|
||||
// Package proxy provides support for a variety of protocols to proxy network
|
||||
// data.
|
||||
//
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type proxy_direct struct{}
|
||||
|
||||
// Direct is a direct proxy: one that makes network connections directly.
|
||||
var proxy_Direct = proxy_direct{}
|
||||
|
||||
func (proxy_direct) Dial(network, addr string) (net.Conn, error) {
|
||||
return net.Dial(network, addr)
|
||||
}
|
||||
|
||||
// A PerHost directs connections to a default Dialer unless the host name
|
||||
// requested matches one of a number of exceptions.
|
||||
type proxy_PerHost struct {
|
||||
def, bypass proxy_Dialer
|
||||
|
||||
bypassNetworks []*net.IPNet
|
||||
bypassIPs []net.IP
|
||||
bypassZones []string
|
||||
bypassHosts []string
|
||||
}
|
||||
|
||||
// NewPerHost returns a PerHost Dialer that directs connections to either
|
||||
// defaultDialer or bypass, depending on whether the connection matches one of
|
||||
// the configured rules.
|
||||
func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {
|
||||
return &proxy_PerHost{
|
||||
def: defaultDialer,
|
||||
bypass: bypass,
|
||||
}
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network through either
|
||||
// defaultDialer or bypass.
|
||||
func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.dialerForRequest(host).Dial(network, addr)
|
||||
}
|
||||
|
||||
func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
for _, net := range p.bypassNetworks {
|
||||
if net.Contains(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassIP := range p.bypassIPs {
|
||||
if bypassIP.Equal(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
for _, zone := range p.bypassZones {
|
||||
if strings.HasSuffix(host, zone) {
|
||||
return p.bypass
|
||||
}
|
||||
if host == zone[1:] {
|
||||
// For a zone ".example.com", we match "example.com"
|
||||
// too.
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassHost := range p.bypassHosts {
|
||||
if bypassHost == host {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
// AddFromString parses a string that contains comma-separated values
|
||||
// specifying hosts that should use the bypass proxy. Each value is either an
|
||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
||||
// (localhost). A best effort is made to parse the string and errors are
|
||||
// ignored.
|
||||
func (p *proxy_PerHost) AddFromString(s string) {
|
||||
hosts := strings.Split(s, ",")
|
||||
for _, host := range hosts {
|
||||
host = strings.TrimSpace(host)
|
||||
if len(host) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(host, "/") {
|
||||
// We assume that it's a CIDR address like 127.0.0.0/8
|
||||
if _, net, err := net.ParseCIDR(host); err == nil {
|
||||
p.AddNetwork(net)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
p.AddIP(ip)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(host, "*.") {
|
||||
p.AddZone(host[1:])
|
||||
continue
|
||||
}
|
||||
p.AddHost(host)
|
||||
}
|
||||
}
|
||||
|
||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match an IP.
|
||||
func (p *proxy_PerHost) AddIP(ip net.IP) {
|
||||
p.bypassIPs = append(p.bypassIPs, ip)
|
||||
}
|
||||
|
||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match.
|
||||
func (p *proxy_PerHost) AddNetwork(net *net.IPNet) {
|
||||
p.bypassNetworks = append(p.bypassNetworks, net)
|
||||
}
|
||||
|
||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
||||
// "example.com" matches "example.com" and all of its subdomains.
|
||||
func (p *proxy_PerHost) AddZone(zone string) {
|
||||
if strings.HasSuffix(zone, ".") {
|
||||
zone = zone[:len(zone)-1]
|
||||
}
|
||||
if !strings.HasPrefix(zone, ".") {
|
||||
zone = "." + zone
|
||||
}
|
||||
p.bypassZones = append(p.bypassZones, zone)
|
||||
}
|
||||
|
||||
// AddHost specifies a host name that will use the bypass proxy.
|
||||
func (p *proxy_PerHost) AddHost(host string) {
|
||||
if strings.HasSuffix(host, ".") {
|
||||
host = host[:len(host)-1]
|
||||
}
|
||||
p.bypassHosts = append(p.bypassHosts, host)
|
||||
}
|
||||
|
||||
// A Dialer is a means to establish a connection.
|
||||
type proxy_Dialer interface {
|
||||
// Dial connects to the given address via the proxy.
|
||||
Dial(network, addr string) (c net.Conn, err error)
|
||||
}
|
||||
|
||||
// Auth contains authentication parameters that specific Dialers may require.
|
||||
type proxy_Auth struct {
|
||||
User, Password string
|
||||
}
|
||||
|
||||
// FromEnvironment returns the dialer specified by the proxy related variables in
|
||||
// the environment.
|
||||
func proxy_FromEnvironment() proxy_Dialer {
|
||||
allProxy := proxy_allProxyEnv.Get()
|
||||
if len(allProxy) == 0 {
|
||||
return proxy_Direct
|
||||
}
|
||||
|
||||
proxyURL, err := url.Parse(allProxy)
|
||||
if err != nil {
|
||||
return proxy_Direct
|
||||
}
|
||||
proxy, err := proxy_FromURL(proxyURL, proxy_Direct)
|
||||
if err != nil {
|
||||
return proxy_Direct
|
||||
}
|
||||
|
||||
noProxy := proxy_noProxyEnv.Get()
|
||||
if len(noProxy) == 0 {
|
||||
return proxy
|
||||
}
|
||||
|
||||
perHost := proxy_NewPerHost(proxy, proxy_Direct)
|
||||
perHost.AddFromString(noProxy)
|
||||
return perHost
|
||||
}
|
||||
|
||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
||||
// from a URL with such a scheme.
|
||||
var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)
|
||||
|
||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
||||
// by FromURL.
|
||||
func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {
|
||||
if proxy_proxySchemes == nil {
|
||||
proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))
|
||||
}
|
||||
proxy_proxySchemes[scheme] = f
|
||||
}
|
||||
|
||||
// FromURL returns a Dialer given a URL specification and an underlying
|
||||
// Dialer for it to make network requests.
|
||||
func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {
|
||||
var auth *proxy_Auth
|
||||
if u.User != nil {
|
||||
auth = new(proxy_Auth)
|
||||
auth.User = u.User.Username()
|
||||
if p, ok := u.User.Password(); ok {
|
||||
auth.Password = p
|
||||
}
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "socks5":
|
||||
return proxy_SOCKS5("tcp", u.Host, auth, forward)
|
||||
}
|
||||
|
||||
// If the scheme doesn't match any of the built-in schemes, see if it
|
||||
// was registered by another package.
|
||||
if proxy_proxySchemes != nil {
|
||||
if f, ok := proxy_proxySchemes[u.Scheme]; ok {
|
||||
return f(u, forward)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
||||
}
|
||||
|
||||
var (
|
||||
proxy_allProxyEnv = &proxy_envOnce{
|
||||
names: []string{"ALL_PROXY", "all_proxy"},
|
||||
}
|
||||
proxy_noProxyEnv = &proxy_envOnce{
|
||||
names: []string{"NO_PROXY", "no_proxy"},
|
||||
}
|
||||
)
|
||||
|
||||
// envOnce looks up an environment variable (optionally by multiple
|
||||
// names) once. It mitigates expensive lookups on some platforms
|
||||
// (e.g. Windows).
|
||||
// (Borrowed from net/http/transport.go)
|
||||
type proxy_envOnce struct {
|
||||
names []string
|
||||
once sync.Once
|
||||
val string
|
||||
}
|
||||
|
||||
func (e *proxy_envOnce) Get() string {
|
||||
e.once.Do(e.init)
|
||||
return e.val
|
||||
}
|
||||
|
||||
func (e *proxy_envOnce) init() {
|
||||
for _, n := range e.names {
|
||||
e.val = os.Getenv(n)
|
||||
if e.val != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
|
||||
// with an optional username and password. See RFC 1928 and RFC 1929.
|
||||
func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {
|
||||
s := &proxy_socks5{
|
||||
network: network,
|
||||
addr: addr,
|
||||
forward: forward,
|
||||
}
|
||||
if auth != nil {
|
||||
s.user = auth.User
|
||||
s.password = auth.Password
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type proxy_socks5 struct {
|
||||
user, password string
|
||||
network, addr string
|
||||
forward proxy_Dialer
|
||||
}
|
||||
|
||||
const proxy_socks5Version = 5
|
||||
|
||||
const (
|
||||
proxy_socks5AuthNone = 0
|
||||
proxy_socks5AuthPassword = 2
|
||||
)
|
||||
|
||||
const proxy_socks5Connect = 1
|
||||
|
||||
const (
|
||||
proxy_socks5IP4 = 1
|
||||
proxy_socks5Domain = 3
|
||||
proxy_socks5IP6 = 4
|
||||
)
|
||||
|
||||
var proxy_socks5Errors = []string{
|
||||
"",
|
||||
"general failure",
|
||||
"connection forbidden",
|
||||
"network unreachable",
|
||||
"host unreachable",
|
||||
"connection refused",
|
||||
"TTL expired",
|
||||
"command not supported",
|
||||
"address type not supported",
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network via the SOCKS5 proxy.
|
||||
func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
|
||||
}
|
||||
|
||||
conn, err := s.forward.Dial(s.network, s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.connect(conn, addr); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// connect takes an existing connection to a socks5 proxy server,
|
||||
// and commands the server to extend that connection to target,
|
||||
// which must be a canonical address with a host and port.
|
||||
func (s *proxy_socks5) connect(conn net.Conn, target string) error {
|
||||
host, portStr, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||
}
|
||||
if port < 1 || port > 0xffff {
|
||||
return errors.New("proxy: port number out of range: " + portStr)
|
||||
}
|
||||
|
||||
// the size here is just an estimate
|
||||
buf := make([]byte, 0, 6+len(host))
|
||||
|
||||
buf = append(buf, proxy_socks5Version)
|
||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
||||
buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)
|
||||
} else {
|
||||
buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)
|
||||
}
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
if buf[0] != 5 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
if buf[1] == 0xff {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||
}
|
||||
|
||||
// See RFC 1929
|
||||
if buf[1] == proxy_socks5AuthPassword {
|
||||
buf = buf[:0]
|
||||
buf = append(buf, 1 /* password protocol version */)
|
||||
buf = append(buf, uint8(len(s.user)))
|
||||
buf = append(buf, s.user...)
|
||||
buf = append(buf, uint8(len(s.password)))
|
||||
buf = append(buf, s.password...)
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if buf[1] != 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[:0]
|
||||
buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
buf = append(buf, proxy_socks5IP4)
|
||||
ip = ip4
|
||||
} else {
|
||||
buf = append(buf, proxy_socks5IP6)
|
||||
}
|
||||
buf = append(buf, ip...)
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return errors.New("proxy: destination host name too long: " + host)
|
||||
}
|
||||
buf = append(buf, proxy_socks5Domain)
|
||||
buf = append(buf, byte(len(host)))
|
||||
buf = append(buf, host...)
|
||||
}
|
||||
buf = append(buf, byte(port>>8), byte(port))
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
failure := "unknown error"
|
||||
if int(buf[1]) < len(proxy_socks5Errors) {
|
||||
failure = proxy_socks5Errors[buf[1]]
|
||||
}
|
||||
|
||||
if len(failure) > 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||
}
|
||||
|
||||
bytesToDiscard := 0
|
||||
switch buf[3] {
|
||||
case proxy_socks5IP4:
|
||||
bytesToDiscard = net.IPv4len
|
||||
case proxy_socks5IP6:
|
||||
bytesToDiscard = net.IPv6len
|
||||
case proxy_socks5Domain:
|
||||
_, err := io.ReadFull(conn, buf[:1])
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
bytesToDiscard = int(buf[0])
|
||||
default:
|
||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||
}
|
||||
|
||||
if cap(buf) < bytesToDiscard {
|
||||
buf = make([]byte, bytesToDiscard)
|
||||
} else {
|
||||
buf = buf[:bytesToDiscard]
|
||||
}
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Also need to discard the port number
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
19
vendor/go.uber.org/atomic/.codecov.yml
generated
vendored
19
vendor/go.uber.org/atomic/.codecov.yml
generated
vendored
@ -1,19 +0,0 @@
|
||||
coverage:
|
||||
range: 80..100
|
||||
round: down
|
||||
precision: 2
|
||||
|
||||
status:
|
||||
project: # measuring the overall project coverage
|
||||
default: # context, you can create multiple ones with custom titles
|
||||
enabled: yes # must be yes|true to enable this status
|
||||
target: 100 # specify the target coverage for each commit status
|
||||
# option: "auto" (must increase from parent commit or pull request base)
|
||||
# option: "X%" a static target percentage to hit
|
||||
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||
|
||||
# Also update COVER_IGNORE_PKGS in the Makefile.
|
||||
ignore:
|
||||
- /internal/gen-atomicint/
|
||||
- /internal/gen-valuewrapper/
|
12
vendor/go.uber.org/atomic/.gitignore
generated
vendored
12
vendor/go.uber.org/atomic/.gitignore
generated
vendored
@ -1,12 +0,0 @@
|
||||
/bin
|
||||
.DS_Store
|
||||
/vendor
|
||||
cover.html
|
||||
cover.out
|
||||
lint.log
|
||||
|
||||
# Binaries
|
||||
*.test
|
||||
|
||||
# Profiling output
|
||||
*.prof
|
27
vendor/go.uber.org/atomic/.travis.yml
generated
vendored
27
vendor/go.uber.org/atomic/.travis.yml
generated
vendored
@ -1,27 +0,0 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go_import_path: go.uber.org/atomic
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: oldstable
|
||||
- go: stable
|
||||
env: LINT=1
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
|
||||
before_install:
|
||||
- go version
|
||||
|
||||
script:
|
||||
- test -z "$LINT" || make lint
|
||||
- make cover
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
76
vendor/go.uber.org/atomic/CHANGELOG.md
generated
vendored
76
vendor/go.uber.org/atomic/CHANGELOG.md
generated
vendored
@ -1,76 +0,0 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.7.0] - 2020-09-14
|
||||
### Added
|
||||
- Support JSON serialization and deserialization of primitive atomic types.
|
||||
- Support Text marshalling and unmarshalling for string atomics.
|
||||
|
||||
### Changed
|
||||
- Disallow incorrect comparison of atomic values in a non-atomic way.
|
||||
|
||||
### Removed
|
||||
- Remove dependency on `golang.org/x/{lint, tools}`.
|
||||
|
||||
## [1.6.0] - 2020-02-24
|
||||
### Changed
|
||||
- Drop library dependency on `golang.org/x/{lint, tools}`.
|
||||
|
||||
## [1.5.1] - 2019-11-19
|
||||
- Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together
|
||||
causing `CAS` to fail even though the old value matches.
|
||||
|
||||
## [1.5.0] - 2019-10-29
|
||||
### Changed
|
||||
- With Go modules, only the `go.uber.org/atomic` import path is supported now.
|
||||
If you need to use the old import path, please add a `replace` directive to
|
||||
your `go.mod`.
|
||||
|
||||
## [1.4.0] - 2019-05-01
|
||||
### Added
|
||||
- Add `atomic.Error` type for atomic operations on `error` values.
|
||||
|
||||
## [1.3.2] - 2018-05-02
|
||||
### Added
|
||||
- Add `atomic.Duration` type for atomic operations on `time.Duration` values.
|
||||
|
||||
## [1.3.1] - 2017-11-14
|
||||
### Fixed
|
||||
- Revert optimization for `atomic.String.Store("")` which caused data races.
|
||||
|
||||
## [1.3.0] - 2017-11-13
|
||||
### Added
|
||||
- Add `atomic.Bool.CAS` for compare-and-swap semantics on bools.
|
||||
|
||||
### Changed
|
||||
- Optimize `atomic.String.Store("")` by avoiding an allocation.
|
||||
|
||||
## [1.2.0] - 2017-04-12
|
||||
### Added
|
||||
- Shadow `atomic.Value` from `sync/atomic`.
|
||||
|
||||
## [1.1.0] - 2017-03-10
|
||||
### Added
|
||||
- Add atomic `Float64` type.
|
||||
|
||||
### Changed
|
||||
- Support new `go.uber.org/atomic` import path.
|
||||
|
||||
## [1.0.0] - 2016-07-18
|
||||
|
||||
- Initial release.
|
||||
|
||||
[1.7.0]: https://github.com/uber-go/atomic/compare/v1.6.0...v1.7.0
|
||||
[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
|
||||
[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
|
||||
[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
|
||||
[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
|
||||
[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
|
||||
[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
|
||||
[1.3.0]: https://github.com/uber-go/atomic/compare/v1.2.0...v1.3.0
|
||||
[1.2.0]: https://github.com/uber-go/atomic/compare/v1.1.0...v1.2.0
|
||||
[1.1.0]: https://github.com/uber-go/atomic/compare/v1.0.0...v1.1.0
|
||||
[1.0.0]: https://github.com/uber-go/atomic/releases/tag/v1.0.0
|
19
vendor/go.uber.org/atomic/LICENSE.txt
generated
vendored
19
vendor/go.uber.org/atomic/LICENSE.txt
generated
vendored
@ -1,19 +0,0 @@
|
||||
Copyright (c) 2016 Uber Technologies, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
78
vendor/go.uber.org/atomic/Makefile
generated
vendored
78
vendor/go.uber.org/atomic/Makefile
generated
vendored
@ -1,78 +0,0 @@
|
||||
# Directory to place `go install`ed binaries into.
|
||||
export GOBIN ?= $(shell pwd)/bin
|
||||
|
||||
GOLINT = $(GOBIN)/golint
|
||||
GEN_ATOMICINT = $(GOBIN)/gen-atomicint
|
||||
GEN_ATOMICWRAPPER = $(GOBIN)/gen-atomicwrapper
|
||||
STATICCHECK = $(GOBIN)/staticcheck
|
||||
|
||||
GO_FILES ?= $(shell find . '(' -path .git -o -path vendor ')' -prune -o -name '*.go' -print)
|
||||
|
||||
# Also update ignore section in .codecov.yml.
|
||||
COVER_IGNORE_PKGS = \
|
||||
go.uber.org/atomic/internal/gen-atomicint \
|
||||
go.uber.org/atomic/internal/gen-atomicwrapper
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build ./...
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -race ./...
|
||||
|
||||
.PHONY: gofmt
|
||||
gofmt:
|
||||
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
|
||||
gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
|
||||
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" && cat $(FMT_LOG) && false)
|
||||
|
||||
$(GOLINT):
|
||||
cd tools && go install golang.org/x/lint/golint
|
||||
|
||||
$(STATICCHECK):
|
||||
cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
$(GEN_ATOMICWRAPPER): $(wildcard ./internal/gen-atomicwrapper/*)
|
||||
go build -o $@ ./internal/gen-atomicwrapper
|
||||
|
||||
$(GEN_ATOMICINT): $(wildcard ./internal/gen-atomicint/*)
|
||||
go build -o $@ ./internal/gen-atomicint
|
||||
|
||||
.PHONY: golint
|
||||
golint: $(GOLINT)
|
||||
$(GOLINT) ./...
|
||||
|
||||
.PHONY: staticcheck
|
||||
staticcheck: $(STATICCHECK)
|
||||
$(STATICCHECK) ./...
|
||||
|
||||
.PHONY: lint
|
||||
lint: gofmt golint staticcheck generatenodirty
|
||||
|
||||
# comma separated list of packages to consider for code coverage.
|
||||
COVER_PKG = $(shell \
|
||||
go list -find ./... | \
|
||||
grep -v $(foreach pkg,$(COVER_IGNORE_PKGS),-e "^$(pkg)$$") | \
|
||||
paste -sd, -)
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -coverprofile=cover.out -coverpkg $(COVER_PKG) -v ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
.PHONY: generate
|
||||
generate: $(GEN_ATOMICINT) $(GEN_ATOMICWRAPPER)
|
||||
go generate ./...
|
||||
|
||||
.PHONY: generatenodirty
|
||||
generatenodirty:
|
||||
@[ -z "$$(git status --porcelain)" ] || ( \
|
||||
echo "Working tree is dirty. Commit your changes first."; \
|
||||
exit 1 )
|
||||
@make generate
|
||||
@status=$$(git status --porcelain); \
|
||||
[ -z "$$status" ] || ( \
|
||||
echo "Working tree is dirty after `make generate`:"; \
|
||||
echo "$$status"; \
|
||||
echo "Please ensure that the generated code is up-to-date." )
|
63
vendor/go.uber.org/atomic/README.md
generated
vendored
63
vendor/go.uber.org/atomic/README.md
generated
vendored
@ -1,63 +0,0 @@
|
||||
# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard]
|
||||
|
||||
Simple wrappers for primitive types to enforce atomic access.
|
||||
|
||||
## Installation
|
||||
|
||||
```shell
|
||||
$ go get -u go.uber.org/atomic@v1
|
||||
```
|
||||
|
||||
### Legacy Import Path
|
||||
|
||||
As of v1.5.0, the import path `go.uber.org/atomic` is the only supported way
|
||||
of using this package. If you are using Go modules, this package will fail to
|
||||
compile with the legacy import path path `github.com/uber-go/atomic`.
|
||||
|
||||
We recommend migrating your code to the new import path but if you're unable
|
||||
to do so, or if your dependencies are still using the old import path, you
|
||||
will have to add a `replace` directive to your `go.mod` file downgrading the
|
||||
legacy import path to an older version.
|
||||
|
||||
```
|
||||
replace github.com/uber-go/atomic => github.com/uber-go/atomic v1.4.0
|
||||
```
|
||||
|
||||
You can do so automatically by running the following command.
|
||||
|
||||
```shell
|
||||
$ go mod edit -replace github.com/uber-go/atomic=github.com/uber-go/atomic@v1.4.0
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The standard library's `sync/atomic` is powerful, but it's easy to forget which
|
||||
variables must be accessed atomically. `go.uber.org/atomic` preserves all the
|
||||
functionality of the standard library, but wraps the primitive types to
|
||||
provide a safer, more convenient API.
|
||||
|
||||
```go
|
||||
var atom atomic.Uint32
|
||||
atom.Store(42)
|
||||
atom.Sub(2)
|
||||
atom.CAS(40, 11)
|
||||
```
|
||||
|
||||
See the [documentation][doc] for a complete API specification.
|
||||
|
||||
## Development Status
|
||||
|
||||
Stable.
|
||||
|
||||
---
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
|
||||
[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/atomic
|
||||
[ci-img]: https://travis-ci.com/uber-go/atomic.svg?branch=master
|
||||
[ci]: https://travis-ci.com/uber-go/atomic
|
||||
[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg
|
||||
[cov]: https://codecov.io/gh/uber-go/atomic
|
||||
[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic
|
||||
[reportcard]: https://goreportcard.com/report/go.uber.org/atomic
|
81
vendor/go.uber.org/atomic/bool.go
generated
vendored
81
vendor/go.uber.org/atomic/bool.go
generated
vendored
@ -1,81 +0,0 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Bool is an atomic type-safe wrapper for bool values.
|
||||
type Bool struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Uint32
|
||||
}
|
||||
|
||||
var _zeroBool bool
|
||||
|
||||
// NewBool creates a new Bool.
|
||||
func NewBool(v bool) *Bool {
|
||||
x := &Bool{}
|
||||
if v != _zeroBool {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped bool.
|
||||
func (x *Bool) Load() bool {
|
||||
return truthy(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed bool.
|
||||
func (x *Bool) Store(v bool) {
|
||||
x.v.Store(boolToInt(v))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap for bool values.
|
||||
func (x *Bool) CAS(o, n bool) bool {
|
||||
return x.v.CAS(boolToInt(o), boolToInt(n))
|
||||
}
|
||||
|
||||
// Swap atomically stores the given bool and returns the old
|
||||
// value.
|
||||
func (x *Bool) Swap(o bool) bool {
|
||||
return truthy(x.v.Swap(boolToInt(o)))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped bool into JSON.
|
||||
func (x *Bool) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(x.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a bool from JSON.
|
||||
func (x *Bool) UnmarshalJSON(b []byte) error {
|
||||
var v bool
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
x.Store(v)
|
||||
return nil
|
||||
}
|
23
vendor/go.uber.org/atomic/doc.go
generated
vendored
23
vendor/go.uber.org/atomic/doc.go
generated
vendored
@ -1,23 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
// Package atomic provides simple wrappers around numerics to enforce atomic
|
||||
// access.
|
||||
package atomic
|
82
vendor/go.uber.org/atomic/duration.go
generated
vendored
82
vendor/go.uber.org/atomic/duration.go
generated
vendored
@ -1,82 +0,0 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Duration is an atomic type-safe wrapper for time.Duration values.
|
||||
type Duration struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Int64
|
||||
}
|
||||
|
||||
var _zeroDuration time.Duration
|
||||
|
||||
// NewDuration creates a new Duration.
|
||||
func NewDuration(v time.Duration) *Duration {
|
||||
x := &Duration{}
|
||||
if v != _zeroDuration {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped time.Duration.
|
||||
func (x *Duration) Load() time.Duration {
|
||||
return time.Duration(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed time.Duration.
|
||||
func (x *Duration) Store(v time.Duration) {
|
||||
x.v.Store(int64(v))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap for time.Duration values.
|
||||
func (x *Duration) CAS(o, n time.Duration) bool {
|
||||
return x.v.CAS(int64(o), int64(n))
|
||||
}
|
||||
|
||||
// Swap atomically stores the given time.Duration and returns the old
|
||||
// value.
|
||||
func (x *Duration) Swap(o time.Duration) time.Duration {
|
||||
return time.Duration(x.v.Swap(int64(o)))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped time.Duration into JSON.
|
||||
func (x *Duration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(x.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a time.Duration from JSON.
|
||||
func (x *Duration) UnmarshalJSON(b []byte) error {
|
||||
var v time.Duration
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
x.Store(v)
|
||||
return nil
|
||||
}
|
40
vendor/go.uber.org/atomic/duration_ext.go
generated
vendored
40
vendor/go.uber.org/atomic/duration_ext.go
generated
vendored
@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import "time"
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Duration -type=time.Duration -wrapped=Int64 -pack=int64 -unpack=time.Duration -cas -swap -json -imports time -file=duration.go
|
||||
|
||||
// Add atomically adds to the wrapped time.Duration and returns the new value.
|
||||
func (d *Duration) Add(n time.Duration) time.Duration {
|
||||
return time.Duration(d.v.Add(int64(n)))
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped time.Duration and returns the new value.
|
||||
func (d *Duration) Sub(n time.Duration) time.Duration {
|
||||
return time.Duration(d.v.Sub(int64(n)))
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (d *Duration) String() string {
|
||||
return d.Load().String()
|
||||
}
|
51
vendor/go.uber.org/atomic/error.go
generated
vendored
51
vendor/go.uber.org/atomic/error.go
generated
vendored
@ -1,51 +0,0 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
// Error is an atomic type-safe wrapper for error values.
|
||||
type Error struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Value
|
||||
}
|
||||
|
||||
var _zeroError error
|
||||
|
||||
// NewError creates a new Error.
|
||||
func NewError(v error) *Error {
|
||||
x := &Error{}
|
||||
if v != _zeroError {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped error.
|
||||
func (x *Error) Load() error {
|
||||
return unpackError(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed error.
|
||||
func (x *Error) Store(v error) {
|
||||
x.v.Store(packError(v))
|
||||
}
|
76
vendor/go.uber.org/atomic/float64.go
generated
vendored
76
vendor/go.uber.org/atomic/float64.go
generated
vendored
@ -1,76 +0,0 @@
|
||||
// @generated Code generated by gen-atomicwrapper.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Float64 is an atomic type-safe wrapper for float64 values.
|
||||
type Float64 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v Uint64
|
||||
}
|
||||
|
||||
var _zeroFloat64 float64
|
||||
|
||||
// NewFloat64 creates a new Float64.
|
||||
func NewFloat64(v float64) *Float64 {
|
||||
x := &Float64{}
|
||||
if v != _zeroFloat64 {
|
||||
x.Store(v)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped float64.
|
||||
func (x *Float64) Load() float64 {
|
||||
return math.Float64frombits(x.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed float64.
|
||||
func (x *Float64) Store(v float64) {
|
||||
x.v.Store(math.Float64bits(v))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap for float64 values.
|
||||
func (x *Float64) CAS(o, n float64) bool {
|
||||
return x.v.CAS(math.Float64bits(o), math.Float64bits(n))
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped float64 into JSON.
|
||||
func (x *Float64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(x.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a float64 from JSON.
|
||||
func (x *Float64) UnmarshalJSON(b []byte) error {
|
||||
var v float64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
x.Store(v)
|
||||
return nil
|
||||
}
|
47
vendor/go.uber.org/atomic/float64_ext.go
generated
vendored
47
vendor/go.uber.org/atomic/float64_ext.go
generated
vendored
@ -1,47 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import "strconv"
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Float64 -type=float64 -wrapped=Uint64 -pack=math.Float64bits -unpack=math.Float64frombits -cas -json -imports math -file=float64.go
|
||||
|
||||
// Add atomically adds to the wrapped float64 and returns the new value.
|
||||
func (f *Float64) Add(s float64) float64 {
|
||||
for {
|
||||
old := f.Load()
|
||||
new := old + s
|
||||
if f.CAS(old, new) {
|
||||
return new
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped float64 and returns the new value.
|
||||
func (f *Float64) Sub(s float64) float64 {
|
||||
return f.Add(-s)
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (f *Float64) String() string {
|
||||
// 'g' is the behavior for floats with %v.
|
||||
return strconv.FormatFloat(f.Load(), 'g', -1, 64)
|
||||
}
|
26
vendor/go.uber.org/atomic/gen.go
generated
vendored
26
vendor/go.uber.org/atomic/gen.go
generated
vendored
@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
//go:generate bin/gen-atomicint -name=Int32 -wrapped=int32 -file=int32.go
|
||||
//go:generate bin/gen-atomicint -name=Int64 -wrapped=int64 -file=int64.go
|
||||
//go:generate bin/gen-atomicint -name=Uint32 -wrapped=uint32 -unsigned -file=uint32.go
|
||||
//go:generate bin/gen-atomicint -name=Uint64 -wrapped=uint64 -unsigned -file=uint64.go
|
102
vendor/go.uber.org/atomic/int32.go
generated
vendored
102
vendor/go.uber.org/atomic/int32.go
generated
vendored
@ -1,102 +0,0 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Int32 is an atomic wrapper around int32.
|
||||
type Int32 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v int32
|
||||
}
|
||||
|
||||
// NewInt32 creates a new Int32.
|
||||
func NewInt32(i int32) *Int32 {
|
||||
return &Int32{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Int32) Load() int32 {
|
||||
return atomic.LoadInt32(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Add(n int32) int32 {
|
||||
return atomic.AddInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Sub(n int32) int32 {
|
||||
return atomic.AddInt32(&i.v, -n)
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Inc() int32 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Dec() int32 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Int32) CAS(old, new int32) bool {
|
||||
return atomic.CompareAndSwapInt32(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Int32) Store(n int32) {
|
||||
atomic.StoreInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped int32 and returns the old value.
|
||||
func (i *Int32) Swap(n int32) int32 {
|
||||
return atomic.SwapInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped int32 into JSON.
|
||||
func (i *Int32) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped int32.
|
||||
func (i *Int32) UnmarshalJSON(b []byte) error {
|
||||
var v int32
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Int32) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
102
vendor/go.uber.org/atomic/int64.go
generated
vendored
102
vendor/go.uber.org/atomic/int64.go
generated
vendored
@ -1,102 +0,0 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Int64 is an atomic wrapper around int64.
|
||||
type Int64 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v int64
|
||||
}
|
||||
|
||||
// NewInt64 creates a new Int64.
|
||||
func NewInt64(i int64) *Int64 {
|
||||
return &Int64{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Int64) Load() int64 {
|
||||
return atomic.LoadInt64(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Add(n int64) int64 {
|
||||
return atomic.AddInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Sub(n int64) int64 {
|
||||
return atomic.AddInt64(&i.v, -n)
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Inc() int64 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Dec() int64 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Int64) CAS(old, new int64) bool {
|
||||
return atomic.CompareAndSwapInt64(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Int64) Store(n int64) {
|
||||
atomic.StoreInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped int64 and returns the old value.
|
||||
func (i *Int64) Swap(n int64) int64 {
|
||||
return atomic.SwapInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped int64 into JSON.
|
||||
func (i *Int64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped int64.
|
||||
func (i *Int64) UnmarshalJSON(b []byte) error {
|
||||
var v int64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Int64) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
35
vendor/go.uber.org/atomic/nocmp.go
generated
vendored
35
vendor/go.uber.org/atomic/nocmp.go
generated
vendored
@ -1,35 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
// nocmp is an uncomparable struct. Embed this inside another struct to make
|
||||
// it uncomparable.
|
||||
//
|
||||
// type Foo struct {
|
||||
// nocmp
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// This DOES NOT:
|
||||
//
|
||||
// - Disallow shallow copies of structs
|
||||
// - Disallow comparison of pointers to uncomparable structs
|
||||
type nocmp [0]func()
|
43
vendor/go.uber.org/atomic/string_ext.go
generated
vendored
43
vendor/go.uber.org/atomic/string_ext.go
generated
vendored
@ -1,43 +0,0 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=String -type=string -wrapped=Value -file=string.go
|
||||
|
||||
// String returns the wrapped value.
|
||||
func (s *String) String() string {
|
||||
return s.Load()
|
||||
}
|
||||
|
||||
// MarshalText encodes the wrapped string into a textual form.
|
||||
//
|
||||
// This makes it encodable as JSON, YAML, XML, and more.
|
||||
func (s *String) MarshalText() ([]byte, error) {
|
||||
return []byte(s.Load()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText decodes text and replaces the wrapped string with it.
|
||||
//
|
||||
// This makes it decodable from JSON, YAML, XML, and more.
|
||||
func (s *String) UnmarshalText(b []byte) error {
|
||||
s.Store(string(b))
|
||||
return nil
|
||||
}
|
102
vendor/go.uber.org/atomic/uint32.go
generated
vendored
102
vendor/go.uber.org/atomic/uint32.go
generated
vendored
@ -1,102 +0,0 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Uint32 is an atomic wrapper around uint32.
|
||||
type Uint32 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v uint32
|
||||
}
|
||||
|
||||
// NewUint32 creates a new Uint32.
|
||||
func NewUint32(i uint32) *Uint32 {
|
||||
return &Uint32{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Uint32) Load() uint32 {
|
||||
return atomic.LoadUint32(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Add(n uint32) uint32 {
|
||||
return atomic.AddUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Sub(n uint32) uint32 {
|
||||
return atomic.AddUint32(&i.v, ^(n - 1))
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Inc() uint32 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Dec() uint32 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Uint32) CAS(old, new uint32) bool {
|
||||
return atomic.CompareAndSwapUint32(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Uint32) Store(n uint32) {
|
||||
atomic.StoreUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped uint32 and returns the old value.
|
||||
func (i *Uint32) Swap(n uint32) uint32 {
|
||||
return atomic.SwapUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped uint32 into JSON.
|
||||
func (i *Uint32) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped uint32.
|
||||
func (i *Uint32) UnmarshalJSON(b []byte) error {
|
||||
var v uint32
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Uint32) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
102
vendor/go.uber.org/atomic/uint64.go
generated
vendored
102
vendor/go.uber.org/atomic/uint64.go
generated
vendored
@ -1,102 +0,0 @@
|
||||
// @generated Code generated by gen-atomicint.
|
||||
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Uint64 is an atomic wrapper around uint64.
|
||||
type Uint64 struct {
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
|
||||
v uint64
|
||||
}
|
||||
|
||||
// NewUint64 creates a new Uint64.
|
||||
func NewUint64(i uint64) *Uint64 {
|
||||
return &Uint64{v: i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Uint64) Load() uint64 {
|
||||
return atomic.LoadUint64(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Add(n uint64) uint64 {
|
||||
return atomic.AddUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Sub(n uint64) uint64 {
|
||||
return atomic.AddUint64(&i.v, ^(n - 1))
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Inc() uint64 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Dec() uint64 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Uint64) CAS(old, new uint64) bool {
|
||||
return atomic.CompareAndSwapUint64(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Uint64) Store(n uint64) {
|
||||
atomic.StoreUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped uint64 and returns the old value.
|
||||
func (i *Uint64) Swap(n uint64) uint64 {
|
||||
return atomic.SwapUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the wrapped uint64 into JSON.
|
||||
func (i *Uint64) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.Load())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes JSON into the wrapped uint64.
|
||||
func (i *Uint64) UnmarshalJSON(b []byte) error {
|
||||
var v uint64
|
||||
if err := json.Unmarshal(b, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Uint64) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
23
vendor/go.uber.org/multierr/.travis.yml
generated
vendored
23
vendor/go.uber.org/multierr/.travis.yml
generated
vendored
@ -1,23 +0,0 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go_import_path: go.uber.org/multierr
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO111MODULE=on
|
||||
|
||||
go:
|
||||
- oldstable
|
||||
- stable
|
||||
|
||||
before_install:
|
||||
- go version
|
||||
|
||||
script:
|
||||
- |
|
||||
set -e
|
||||
make lint
|
||||
make cover
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
35
vendor/go.uber.org/multierr/CHANGELOG.md
generated
vendored
35
vendor/go.uber.org/multierr/CHANGELOG.md
generated
vendored
@ -1,6 +1,41 @@
|
||||
Releases
|
||||
========
|
||||
|
||||
v1.11.0 (2023-03-28)
|
||||
====================
|
||||
- `Errors` now supports any error that implements multiple-error
|
||||
interface.
|
||||
- Add `Every` function to allow checking if all errors in the chain
|
||||
satisfies `errors.Is` against the target error.
|
||||
|
||||
v1.10.0 (2023-03-08)
|
||||
====================
|
||||
|
||||
- Comply with Go 1.20's multiple-error interface.
|
||||
- Drop Go 1.18 support.
|
||||
Per the support policy, only Go 1.19 and 1.20 are supported now.
|
||||
- Drop all non-test external dependencies.
|
||||
|
||||
v1.9.0 (2022-12-12)
|
||||
===================
|
||||
|
||||
- Add `AppendFunc` that allow passsing functions to similar to
|
||||
`AppendInvoke`.
|
||||
|
||||
- Bump up yaml.v3 dependency to 3.0.1.
|
||||
|
||||
v1.8.0 (2022-02-28)
|
||||
===================
|
||||
|
||||
- `Combine`: perform zero allocations when there are no errors.
|
||||
|
||||
|
||||
v1.7.0 (2021-05-06)
|
||||
===================
|
||||
|
||||
- Add `AppendInvoke` to append into errors from `defer` blocks.
|
||||
|
||||
|
||||
v1.6.0 (2020-09-14)
|
||||
===================
|
||||
|
||||
|
2
vendor/go.uber.org/multierr/LICENSE.txt
generated
vendored
2
vendor/go.uber.org/multierr/LICENSE.txt
generated
vendored
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2017 Uber Technologies, Inc.
|
||||
Copyright (c) 2017-2021 Uber Technologies, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
6
vendor/go.uber.org/multierr/Makefile
generated
vendored
6
vendor/go.uber.org/multierr/Makefile
generated
vendored
@ -34,9 +34,5 @@ lint: gofmt golint staticcheck
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -coverprofile=cover.out -coverpkg=./... -v ./...
|
||||
go test -race -coverprofile=cover.out -coverpkg=./... -v ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
update-license:
|
||||
@cd tools && go install go.uber.org/tools/update-license
|
||||
@$(GOBIN)/update-license $(GO_FILES)
|
||||
|
30
vendor/go.uber.org/multierr/README.md
generated
vendored
30
vendor/go.uber.org/multierr/README.md
generated
vendored
@ -2,9 +2,29 @@
|
||||
|
||||
`multierr` allows combining one or more Go `error`s together.
|
||||
|
||||
## Features
|
||||
|
||||
- **Idiomatic**:
|
||||
multierr follows best practices in Go, and keeps your code idiomatic.
|
||||
- It keeps the underlying error type hidden,
|
||||
allowing you to deal in `error` values exclusively.
|
||||
- It provides APIs to safely append into an error from a `defer` statement.
|
||||
- **Performant**:
|
||||
multierr is optimized for performance:
|
||||
- It avoids allocations where possible.
|
||||
- It utilizes slice resizing semantics to optimize common cases
|
||||
like appending into the same error object from a loop.
|
||||
- **Interoperable**:
|
||||
multierr interoperates with the Go standard library's error APIs seamlessly:
|
||||
- The `errors.Is` and `errors.As` functions *just work*.
|
||||
- **Lightweight**:
|
||||
multierr comes with virtually no dependencies.
|
||||
|
||||
## Installation
|
||||
|
||||
go get -u go.uber.org/multierr
|
||||
```bash
|
||||
go get -u go.uber.org/multierr@latest
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
@ -15,9 +35,9 @@ Stable: No breaking changes will be made before 2.0.
|
||||
Released under the [MIT License].
|
||||
|
||||
[MIT License]: LICENSE.txt
|
||||
[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/multierr
|
||||
[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master
|
||||
[doc-img]: https://pkg.go.dev/badge/go.uber.org/multierr
|
||||
[doc]: https://pkg.go.dev/go.uber.org/multierr
|
||||
[ci-img]: https://github.com/uber-go/multierr/actions/workflows/go.yml/badge.svg
|
||||
[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg
|
||||
[ci]: https://travis-ci.com/uber-go/multierr
|
||||
[ci]: https://github.com/uber-go/multierr/actions/workflows/go.yml
|
||||
[cov]: https://codecov.io/gh/uber-go/multierr
|
||||
|
411
vendor/go.uber.org/multierr/error.go
generated
vendored
411
vendor/go.uber.org/multierr/error.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||
// Copyright (c) 2017-2023 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@ -20,54 +20,109 @@
|
||||
|
||||
// Package multierr allows combining one or more errors together.
|
||||
//
|
||||
// Overview
|
||||
// # Overview
|
||||
//
|
||||
// Errors can be combined with the use of the Combine function.
|
||||
//
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// conn.Close(),
|
||||
// )
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// conn.Close(),
|
||||
// )
|
||||
//
|
||||
// If only two errors are being combined, the Append function may be used
|
||||
// instead.
|
||||
//
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// This makes it possible to record resource cleanup failures from deferred
|
||||
// blocks with the help of named return values.
|
||||
//
|
||||
// func sendRequest(req Request) (err error) {
|
||||
// conn, err := openConnection()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, conn.Close())
|
||||
// }()
|
||||
// // ...
|
||||
// }
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// The underlying list of errors for a returned error object may be retrieved
|
||||
// with the Errors function.
|
||||
//
|
||||
// errors := multierr.Errors(err)
|
||||
// if len(errors) > 0 {
|
||||
// fmt.Println("The following errors occurred:", errors)
|
||||
// }
|
||||
// errors := multierr.Errors(err)
|
||||
// if len(errors) > 0 {
|
||||
// fmt.Println("The following errors occurred:", errors)
|
||||
// }
|
||||
//
|
||||
// Advanced Usage
|
||||
// # Appending from a loop
|
||||
//
|
||||
// You sometimes need to append into an error from a loop.
|
||||
//
|
||||
// var err error
|
||||
// for _, item := range items {
|
||||
// err = multierr.Append(err, process(item))
|
||||
// }
|
||||
//
|
||||
// Cases like this may require knowledge of whether an individual instance
|
||||
// failed. This usually requires introduction of a new variable.
|
||||
//
|
||||
// var err error
|
||||
// for _, item := range items {
|
||||
// if perr := process(item); perr != nil {
|
||||
// log.Warn("skipping item", item)
|
||||
// err = multierr.Append(err, perr)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// multierr includes AppendInto to simplify cases like this.
|
||||
//
|
||||
// var err error
|
||||
// for _, item := range items {
|
||||
// if multierr.AppendInto(&err, process(item)) {
|
||||
// log.Warn("skipping item", item)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This will append the error into the err variable, and return true if that
|
||||
// individual error was non-nil.
|
||||
//
|
||||
// See [AppendInto] for more information.
|
||||
//
|
||||
// # Deferred Functions
|
||||
//
|
||||
// Go makes it possible to modify the return value of a function in a defer
|
||||
// block if the function was using named returns. This makes it possible to
|
||||
// record resource cleanup failures from deferred blocks.
|
||||
//
|
||||
// func sendRequest(req Request) (err error) {
|
||||
// conn, err := openConnection()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, conn.Close())
|
||||
// }()
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// multierr provides the Invoker type and AppendInvoke function to make cases
|
||||
// like the above simpler and obviate the need for a closure. The following is
|
||||
// roughly equivalent to the example above.
|
||||
//
|
||||
// func sendRequest(req Request) (err error) {
|
||||
// conn, err := openConnection()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(conn))
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// See [AppendInvoke] and [Invoker] for more information.
|
||||
//
|
||||
// NOTE: If you're modifying an error from inside a defer, you MUST use a named
|
||||
// return value for that function.
|
||||
//
|
||||
// # Advanced Usage
|
||||
//
|
||||
// Errors returned by Combine and Append MAY implement the following
|
||||
// interface.
|
||||
//
|
||||
// type errorGroup interface {
|
||||
// // Returns a slice containing the underlying list of errors.
|
||||
// //
|
||||
// // This slice MUST NOT be modified by the caller.
|
||||
// Errors() []error
|
||||
// }
|
||||
// type errorGroup interface {
|
||||
// // Returns a slice containing the underlying list of errors.
|
||||
// //
|
||||
// // This slice MUST NOT be modified by the caller.
|
||||
// Errors() []error
|
||||
// }
|
||||
//
|
||||
// Note that if you need access to list of errors behind a multierr error, you
|
||||
// should prefer using the Errors function. That said, if you need cheap
|
||||
@ -76,23 +131,23 @@
|
||||
// because errors returned by Combine and Append are not guaranteed to
|
||||
// implement this interface.
|
||||
//
|
||||
// var errors []error
|
||||
// group, ok := err.(errorGroup)
|
||||
// if ok {
|
||||
// errors = group.Errors()
|
||||
// } else {
|
||||
// errors = []error{err}
|
||||
// }
|
||||
// var errors []error
|
||||
// group, ok := err.(errorGroup)
|
||||
// if ok {
|
||||
// errors = group.Errors()
|
||||
// } else {
|
||||
// errors = []error{err}
|
||||
// }
|
||||
package multierr // import "go.uber.org/multierr"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -132,34 +187,15 @@ type errorGroup interface {
|
||||
// Errors returns a slice containing zero or more errors that the supplied
|
||||
// error is composed of. If the error is nil, a nil slice is returned.
|
||||
//
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
// errors := multierr.Errors(err)
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
// errors := multierr.Errors(err)
|
||||
//
|
||||
// If the error is not composed of other errors, the returned slice contains
|
||||
// just the error that was passed in.
|
||||
//
|
||||
// Callers of this function are free to modify the returned slice.
|
||||
func Errors(err error) []error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Note that we're casting to multiError, not errorGroup. Our contract is
|
||||
// that returned errors MAY implement errorGroup. Errors, however, only
|
||||
// has special behavior for multierr-specific error objects.
|
||||
//
|
||||
// This behavior can be expanded in the future but I think it's prudent to
|
||||
// start with as little as possible in terms of contract and possibility
|
||||
// of misuse.
|
||||
eg, ok := err.(*multiError)
|
||||
if !ok {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
errors := eg.Errors()
|
||||
result := make([]error, len(errors))
|
||||
copy(result, errors)
|
||||
return result
|
||||
return extractErrors(err)
|
||||
}
|
||||
|
||||
// multiError is an error that holds one or more errors.
|
||||
@ -174,8 +210,6 @@ type multiError struct {
|
||||
errors []error
|
||||
}
|
||||
|
||||
var _ errorGroup = (*multiError)(nil)
|
||||
|
||||
// Errors returns the list of underlying errors.
|
||||
//
|
||||
// This slice MUST NOT be modified.
|
||||
@ -201,6 +235,17 @@ func (merr *multiError) Error() string {
|
||||
return result
|
||||
}
|
||||
|
||||
// Every compares every error in the given err against the given target error
|
||||
// using [errors.Is], and returns true only if every comparison returned true.
|
||||
func Every(err error, target error) bool {
|
||||
for _, e := range extractErrors(err) {
|
||||
if !errors.Is(e, target) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (merr *multiError) Format(f fmt.State, c rune) {
|
||||
if c == 'v' && f.Flag('+') {
|
||||
merr.writeMultiline(f)
|
||||
@ -292,6 +337,14 @@ func inspect(errors []error) (res inspectResult) {
|
||||
|
||||
// fromSlice converts the given list of errors into a single error.
|
||||
func fromSlice(errors []error) error {
|
||||
// Don't pay to inspect small slices.
|
||||
switch len(errors) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return errors[0]
|
||||
}
|
||||
|
||||
res := inspect(errors)
|
||||
switch res.Count {
|
||||
case 0:
|
||||
@ -301,8 +354,12 @@ func fromSlice(errors []error) error {
|
||||
return errors[res.FirstErrorIdx]
|
||||
case len(errors):
|
||||
if !res.ContainsMultiError {
|
||||
// already flat
|
||||
return &multiError{errors: errors}
|
||||
// Error list is flat. Make a copy of it
|
||||
// Otherwise "errors" escapes to the heap
|
||||
// unconditionally for all other cases.
|
||||
// This lets us optimize for the "no errors" case.
|
||||
out := append(([]error)(nil), errors...)
|
||||
return &multiError{errors: out}
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,32 +384,32 @@ func fromSlice(errors []error) error {
|
||||
// If zero arguments were passed or if all items are nil, a nil error is
|
||||
// returned.
|
||||
//
|
||||
// Combine(nil, nil) // == nil
|
||||
// Combine(nil, nil) // == nil
|
||||
//
|
||||
// If only a single error was passed, it is returned as-is.
|
||||
//
|
||||
// Combine(err) // == err
|
||||
// Combine(err) // == err
|
||||
//
|
||||
// Combine skips over nil arguments so this function may be used to combine
|
||||
// together errors from operations that fail independently of each other.
|
||||
//
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// pipe.Close(),
|
||||
// )
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// pipe.Close(),
|
||||
// )
|
||||
//
|
||||
// If any of the passed errors is a multierr error, it will be flattened along
|
||||
// with the other errors.
|
||||
//
|
||||
// multierr.Combine(multierr.Combine(err1, err2), err3)
|
||||
// // is the same as
|
||||
// multierr.Combine(err1, err2, err3)
|
||||
// multierr.Combine(multierr.Combine(err1, err2), err3)
|
||||
// // is the same as
|
||||
// multierr.Combine(err1, err2, err3)
|
||||
//
|
||||
// The returned error formats into a readable multi-line error message if
|
||||
// formatted with %+v.
|
||||
//
|
||||
// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
|
||||
// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
|
||||
func Combine(errors ...error) error {
|
||||
return fromSlice(errors)
|
||||
}
|
||||
@ -362,16 +419,19 @@ func Combine(errors ...error) error {
|
||||
// This function is a specialization of Combine for the common case where
|
||||
// there are only two errors.
|
||||
//
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// The following pattern may also be used to record failure of deferred
|
||||
// operations without losing information about the original error.
|
||||
//
|
||||
// func doSomething(..) (err error) {
|
||||
// f := acquireResource()
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, f.Close())
|
||||
// }()
|
||||
// func doSomething(..) (err error) {
|
||||
// f := acquireResource()
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, f.Close())
|
||||
// }()
|
||||
//
|
||||
// Note that the variable MUST be a named return to append an error to it from
|
||||
// the defer statement. See also [AppendInvoke].
|
||||
func Append(left error, right error) error {
|
||||
switch {
|
||||
case left == nil:
|
||||
@ -401,37 +461,37 @@ func Append(left error, right error) error {
|
||||
// AppendInto appends an error into the destination of an error pointer and
|
||||
// returns whether the error being appended was non-nil.
|
||||
//
|
||||
// var err error
|
||||
// multierr.AppendInto(&err, r.Close())
|
||||
// multierr.AppendInto(&err, w.Close())
|
||||
// var err error
|
||||
// multierr.AppendInto(&err, r.Close())
|
||||
// multierr.AppendInto(&err, w.Close())
|
||||
//
|
||||
// The above is equivalent to,
|
||||
//
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
//
|
||||
// As AppendInto reports whether the provided error was non-nil, it may be
|
||||
// used to build a multierr error in a loop more ergonomically. For example:
|
||||
//
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if multierr.AppendInto(&err, parse(line, &item)) {
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if multierr.AppendInto(&err, parse(line, &item)) {
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
//
|
||||
// Compare this with a verison that relies solely on Append:
|
||||
// Compare this with a version that relies solely on Append:
|
||||
//
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if parseErr := parse(line, &item); parseErr != nil {
|
||||
// err = multierr.Append(err, parseErr)
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
// var err error
|
||||
// for line := range lines {
|
||||
// var item Item
|
||||
// if parseErr := parse(line, &item); parseErr != nil {
|
||||
// err = multierr.Append(err, parseErr)
|
||||
// continue
|
||||
// }
|
||||
// items = append(items, item)
|
||||
// }
|
||||
func AppendInto(into *error, err error) (errored bool) {
|
||||
if into == nil {
|
||||
// We panic if 'into' is nil. This is not documented above
|
||||
@ -447,3 +507,140 @@ func AppendInto(into *error, err error) (errored bool) {
|
||||
*into = Append(*into, err)
|
||||
return true
|
||||
}
|
||||
|
||||
// Invoker is an operation that may fail with an error. Use it with
|
||||
// AppendInvoke to append the result of calling the function into an error.
|
||||
// This allows you to conveniently defer capture of failing operations.
|
||||
//
|
||||
// See also, [Close] and [Invoke].
|
||||
type Invoker interface {
|
||||
Invoke() error
|
||||
}
|
||||
|
||||
// Invoke wraps a function which may fail with an error to match the Invoker
|
||||
// interface. Use it to supply functions matching this signature to
|
||||
// AppendInvoke.
|
||||
//
|
||||
// For example,
|
||||
//
|
||||
// func processReader(r io.Reader) (err error) {
|
||||
// scanner := bufio.NewScanner(r)
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
|
||||
// for scanner.Scan() {
|
||||
// // ...
|
||||
// }
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// In this example, the following line will construct the Invoker right away,
|
||||
// but defer the invocation of scanner.Err() until the function returns.
|
||||
//
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
|
||||
//
|
||||
// Note that the error you're appending to from the defer statement MUST be a
|
||||
// named return.
|
||||
type Invoke func() error
|
||||
|
||||
// Invoke calls the supplied function and returns its result.
|
||||
func (i Invoke) Invoke() error { return i() }
|
||||
|
||||
// Close builds an Invoker that closes the provided io.Closer. Use it with
|
||||
// AppendInvoke to close io.Closers and append their results into an error.
|
||||
//
|
||||
// For example,
|
||||
//
|
||||
// func processFile(path string) (err error) {
|
||||
// f, err := os.Open(path)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(f))
|
||||
// return processReader(f)
|
||||
// }
|
||||
//
|
||||
// In this example, multierr.Close will construct the Invoker right away, but
|
||||
// defer the invocation of f.Close until the function returns.
|
||||
//
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(f))
|
||||
//
|
||||
// Note that the error you're appending to from the defer statement MUST be a
|
||||
// named return.
|
||||
func Close(closer io.Closer) Invoker {
|
||||
return Invoke(closer.Close)
|
||||
}
|
||||
|
||||
// AppendInvoke appends the result of calling the given Invoker into the
|
||||
// provided error pointer. Use it with named returns to safely defer
|
||||
// invocation of fallible operations until a function returns, and capture the
|
||||
// resulting errors.
|
||||
//
|
||||
// func doSomething(...) (err error) {
|
||||
// // ...
|
||||
// f, err := openFile(..)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// // multierr will call f.Close() when this function returns and
|
||||
// // if the operation fails, its append its error into the
|
||||
// // returned error.
|
||||
// defer multierr.AppendInvoke(&err, multierr.Close(f))
|
||||
//
|
||||
// scanner := bufio.NewScanner(f)
|
||||
// // Similarly, this scheduled scanner.Err to be called and
|
||||
// // inspected when the function returns and append its error
|
||||
// // into the returned error.
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
|
||||
//
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// NOTE: If used with a defer, the error variable MUST be a named return.
|
||||
//
|
||||
// Without defer, AppendInvoke behaves exactly like AppendInto.
|
||||
//
|
||||
// err := // ...
|
||||
// multierr.AppendInvoke(&err, mutltierr.Invoke(foo))
|
||||
//
|
||||
// // ...is roughly equivalent to...
|
||||
//
|
||||
// err := // ...
|
||||
// multierr.AppendInto(&err, foo())
|
||||
//
|
||||
// The advantage of the indirection introduced by Invoker is to make it easy
|
||||
// to defer the invocation of a function. Without this indirection, the
|
||||
// invoked function will be evaluated at the time of the defer block rather
|
||||
// than when the function returns.
|
||||
//
|
||||
// // BAD: This is likely not what the caller intended. This will evaluate
|
||||
// // foo() right away and append its result into the error when the
|
||||
// // function returns.
|
||||
// defer multierr.AppendInto(&err, foo())
|
||||
//
|
||||
// // GOOD: This will defer invocation of foo unutil the function returns.
|
||||
// defer multierr.AppendInvoke(&err, multierr.Invoke(foo))
|
||||
//
|
||||
// multierr provides a few Invoker implementations out of the box for
|
||||
// convenience. See [Invoker] for more information.
|
||||
func AppendInvoke(into *error, invoker Invoker) {
|
||||
AppendInto(into, invoker.Invoke())
|
||||
}
|
||||
|
||||
// AppendFunc is a shorthand for [AppendInvoke].
|
||||
// It allows using function or method value directly
|
||||
// without having to wrap it into an [Invoker] interface.
|
||||
//
|
||||
// func doSomething(...) (err error) {
|
||||
// w, err := startWorker(...)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// // multierr will call w.Stop() when this function returns and
|
||||
// // if the operation fails, it appends its error into the
|
||||
// // returned error.
|
||||
// defer multierr.AppendFunc(&err, w.Stop)
|
||||
// }
|
||||
func AppendFunc(into *error, fn func() error) {
|
||||
AppendInvoke(into, Invoke(fn))
|
||||
}
|
||||
|
37
vendor/go.uber.org/atomic/error_ext.go → vendor/go.uber.org/multierr/error_post_go120.go
generated
vendored
37
vendor/go.uber.org/atomic/error_ext.go → vendor/go.uber.org/multierr/error_post_go120.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
// Copyright (c) 2017-2023 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@ -18,22 +18,31 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
//go:build go1.20
|
||||
// +build go1.20
|
||||
|
||||
// atomic.Value panics on nil inputs, or if the underlying type changes.
|
||||
// Stabilize by always storing a custom struct that we control.
|
||||
package multierr
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Error -type=error -wrapped=Value -pack=packError -unpack=unpackError -file=error.go
|
||||
|
||||
type packedError struct{ Value error }
|
||||
|
||||
func packError(v error) interface{} {
|
||||
return packedError{v}
|
||||
// Unwrap returns a list of errors wrapped by this multierr.
|
||||
func (merr *multiError) Unwrap() []error {
|
||||
return merr.Errors()
|
||||
}
|
||||
|
||||
func unpackError(v interface{}) error {
|
||||
if err, ok := v.(packedError); ok {
|
||||
return err.Value
|
||||
type multipleErrors interface {
|
||||
Unwrap() []error
|
||||
}
|
||||
|
||||
func extractErrors(err error) []error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
// check if the given err is an Unwrapable error that
|
||||
// implements multipleErrors interface.
|
||||
eg, ok := err.(multipleErrors)
|
||||
if !ok {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
return append(([]error)(nil), eg.Unwrap()...)
|
||||
}
|
31
vendor/go.uber.org/multierr/go113.go → vendor/go.uber.org/multierr/error_pre_go120.go
generated
vendored
31
vendor/go.uber.org/multierr/go113.go → vendor/go.uber.org/multierr/error_pre_go120.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Uber Technologies, Inc.
|
||||
// Copyright (c) 2017-2023 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@ -18,12 +18,19 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
// +build go1.13
|
||||
//go:build !go1.20
|
||||
// +build !go1.20
|
||||
|
||||
package multierr
|
||||
|
||||
import "errors"
|
||||
|
||||
// Versions of Go before 1.20 did not support the Unwrap() []error method.
|
||||
// This provides a similar behavior by implementing the Is(..) and As(..)
|
||||
// methods.
|
||||
// See the errors.Join proposal for details:
|
||||
// https://github.com/golang/go/issues/53435
|
||||
|
||||
// As attempts to find the first error in the error list that matches the type
|
||||
// of the value that target points to.
|
||||
//
|
||||
@ -50,3 +57,23 @@ func (merr *multiError) Is(target error) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func extractErrors(err error) []error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Note that we're casting to multiError, not errorGroup. Our contract is
|
||||
// that returned errors MAY implement errorGroup. Errors, however, only
|
||||
// has special behavior for multierr-specific error objects.
|
||||
//
|
||||
// This behavior can be expanded in the future but I think it's prudent to
|
||||
// start with as little as possible in terms of contract and possibility
|
||||
// of misuse.
|
||||
eg, ok := err.(*multiError)
|
||||
if !ok {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
return append(([]error)(nil), eg.Errors()...)
|
||||
}
|
8
vendor/go.uber.org/multierr/glide.yaml
generated
vendored
8
vendor/go.uber.org/multierr/glide.yaml
generated
vendored
@ -1,8 +0,0 @@
|
||||
package: go.uber.org/multierr
|
||||
import:
|
||||
- package: go.uber.org/atomic
|
||||
version: ^1
|
||||
testImport:
|
||||
- package: github.com/stretchr/testify
|
||||
subpackages:
|
||||
- assert
|
77
vendor/go.uber.org/zap/.golangci.yml
generated
vendored
Normal file
77
vendor/go.uber.org/zap/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
output:
|
||||
# Make output more digestible with quickfix in vim/emacs/etc.
|
||||
sort-results: true
|
||||
print-issued-lines: false
|
||||
|
||||
linters:
|
||||
# We'll track the golangci-lint default linters manually
|
||||
# instead of letting them change without our control.
|
||||
disable-all: true
|
||||
enable:
|
||||
# golangci-lint defaults:
|
||||
- errcheck
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unused
|
||||
|
||||
# Our own extras:
|
||||
- gofumpt
|
||||
- nolintlint # lints nolint directives
|
||||
- revive
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
# These govet checks are disabled by default, but they're useful.
|
||||
enable:
|
||||
- niliness
|
||||
- reflectvaluecompare
|
||||
- sortslice
|
||||
- unusedwrite
|
||||
|
||||
errcheck:
|
||||
exclude-functions:
|
||||
# These methods can not fail.
|
||||
# They operate on an in-memory buffer.
|
||||
- (*go.uber.org/zap/buffer.Buffer).Write
|
||||
- (*go.uber.org/zap/buffer.Buffer).WriteByte
|
||||
- (*go.uber.org/zap/buffer.Buffer).WriteString
|
||||
|
||||
- (*go.uber.org/zap/zapio.Writer).Close
|
||||
- (*go.uber.org/zap/zapio.Writer).Sync
|
||||
- (*go.uber.org/zap/zapio.Writer).Write
|
||||
# Write to zapio.Writer cannot fail,
|
||||
# so io.WriteString on it cannot fail.
|
||||
- io.WriteString(*go.uber.org/zap/zapio.Writer)
|
||||
|
||||
# Writing a plain string to a fmt.State cannot fail.
|
||||
- io.WriteString(fmt.State)
|
||||
|
||||
issues:
|
||||
# Print all issues reported by all linters.
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
# Don't ignore some of the issues that golangci-lint considers okay.
|
||||
# This includes documenting all exported entities.
|
||||
exclude-use-default: false
|
||||
|
||||
exclude-rules:
|
||||
# Don't warn on unused parameters.
|
||||
# Parameter names are useful; replacing them with '_' is undesirable.
|
||||
- linters: [revive]
|
||||
text: 'unused-parameter: parameter \S+ seems to be unused, consider removing or renaming it as _'
|
||||
|
||||
# staticcheck already has smarter checks for empty blocks.
|
||||
# revive's empty-block linter has false positives.
|
||||
# For example, as of writing this, the following is not allowed.
|
||||
# for foo() { }
|
||||
- linters: [revive]
|
||||
text: 'empty-block: this block is empty, you can remove it'
|
||||
|
||||
# Ignore logger.Sync() errcheck failures in example_test.go
|
||||
# since those are intended to be uncomplicated examples.
|
||||
- linters: [errcheck]
|
||||
path: example_test.go
|
||||
text: 'Error return value of `logger.Sync` is not checked'
|
10
vendor/go.uber.org/zap/.readme.tmpl
generated
vendored
10
vendor/go.uber.org/zap/.readme.tmpl
generated
vendored
@ -1,7 +1,15 @@
|
||||
# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
<div align="center">
|
||||
|
||||
Blazing fast, structured, leveled logging in Go.
|
||||
|
||||

|
||||
|
||||
[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
</div>
|
||||
|
||||
## Installation
|
||||
|
||||
`go get -u go.uber.org/zap`
|
||||
@ -92,7 +100,7 @@ standard.
|
||||
|
||||
<hr>
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
Released under the [MIT License](LICENSE).
|
||||
|
||||
<sup id="footnote-versions">1</sup> In particular, keep in mind that we may be
|
||||
benchmarking against slightly older versions of other packages. Versions are
|
||||
|
343
vendor/go.uber.org/zap/CHANGELOG.md
generated
vendored
343
vendor/go.uber.org/zap/CHANGELOG.md
generated
vendored
@ -1,7 +1,107 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 1.27.0 (20 Feb 2024)
|
||||
Enhancements:
|
||||
* [#1378][]: Add `WithLazy` method for `SugaredLogger`.
|
||||
* [#1399][]: zaptest: Add `NewTestingWriter` for customizing TestingWriter with more flexibility than `NewLogger`.
|
||||
* [#1406][]: Add `Log`, `Logw`, `Logln` methods for `SugaredLogger`.
|
||||
* [#1416][]: Add `WithPanicHook` option for testing panic logs.
|
||||
|
||||
Thanks to @defval, @dimmo, @arxeiss, and @MKrupauskas for their contributions to this release.
|
||||
|
||||
[#1378]: https://github.com/uber-go/zap/pull/1378
|
||||
[#1399]: https://github.com/uber-go/zap/pull/1399
|
||||
[#1406]: https://github.com/uber-go/zap/pull/1406
|
||||
[#1416]: https://github.com/uber-go/zap/pull/1416
|
||||
|
||||
## 1.26.0 (14 Sep 2023)
|
||||
Enhancements:
|
||||
* [#1297][]: Add Dict as a Field.
|
||||
* [#1319][]: Add `WithLazy` method to `Logger` which lazily evaluates the structured
|
||||
context.
|
||||
* [#1350][]: String encoding is much (~50%) faster now.
|
||||
|
||||
Thanks to @hhk7734, @jquirke, and @cdvr1993 for their contributions to this release.
|
||||
|
||||
[#1297]: https://github.com/uber-go/zap/pull/1297
|
||||
[#1319]: https://github.com/uber-go/zap/pull/1319
|
||||
[#1350]: https://github.com/uber-go/zap/pull/1350
|
||||
|
||||
## 1.25.0 (1 Aug 2023)
|
||||
|
||||
This release contains several improvements including performance, API additions,
|
||||
and two new experimental packages whose APIs are unstable and may change in the
|
||||
future.
|
||||
|
||||
Enhancements:
|
||||
* [#1246][]: Add `zap/exp/zapslog` package for integration with slog.
|
||||
* [#1273][]: Add `Name` to `Logger` which returns the Logger's name if one is set.
|
||||
* [#1281][]: Add `zap/exp/expfield` package which contains helper methods
|
||||
`Str` and `Strs` for constructing String-like zap.Fields.
|
||||
* [#1310][]: Reduce stack size on `Any`.
|
||||
|
||||
Thanks to @knight42, @dzakaammar, @bcspragu, and @rexywork for their contributions
|
||||
to this release.
|
||||
|
||||
[#1246]: https://github.com/uber-go/zap/pull/1246
|
||||
[#1273]: https://github.com/uber-go/zap/pull/1273
|
||||
[#1281]: https://github.com/uber-go/zap/pull/1281
|
||||
[#1310]: https://github.com/uber-go/zap/pull/1310
|
||||
|
||||
## 1.24.0 (30 Nov 2022)
|
||||
|
||||
Enhancements:
|
||||
* [#1148][]: Add `Level` to both `Logger` and `SugaredLogger` that reports the
|
||||
current minimum enabled log level.
|
||||
* [#1185][]: `SugaredLogger` turns errors to zap.Error automatically.
|
||||
|
||||
Thanks to @Abirdcfly, @craigpastro, @nnnkkk7, and @sashamelentyev for their
|
||||
contributions to this release.
|
||||
|
||||
[#1148]: https://github.coml/uber-go/zap/pull/1148
|
||||
[#1185]: https://github.coml/uber-go/zap/pull/1185
|
||||
|
||||
## 1.23.0 (24 Aug 2022)
|
||||
|
||||
Enhancements:
|
||||
* [#1147][]: Add a `zapcore.LevelOf` function to determine the level of a
|
||||
`LevelEnabler` or `Core`.
|
||||
* [#1155][]: Add `zap.Stringers` field constructor to log arrays of objects
|
||||
that implement `String() string`.
|
||||
|
||||
[#1147]: https://github.com/uber-go/zap/pull/1147
|
||||
[#1155]: https://github.com/uber-go/zap/pull/1155
|
||||
|
||||
## 1.22.0 (8 Aug 2022)
|
||||
|
||||
Enhancements:
|
||||
* [#1071][]: Add `zap.Objects` and `zap.ObjectValues` field constructors to log
|
||||
arrays of objects. With these two constructors, you don't need to implement
|
||||
`zapcore.ArrayMarshaler` for use with `zap.Array` if those objects implement
|
||||
`zapcore.ObjectMarshaler`.
|
||||
* [#1079][]: Add `SugaredLogger.WithOptions` to build a copy of an existing
|
||||
`SugaredLogger` with the provided options applied.
|
||||
* [#1080][]: Add `*ln` variants to `SugaredLogger` for each log level.
|
||||
These functions provide a string joining behavior similar to `fmt.Println`.
|
||||
* [#1088][]: Add `zap.WithFatalHook` option to control the behavior of the
|
||||
logger for `Fatal`-level log entries. This defaults to exiting the program.
|
||||
* [#1108][]: Add a `zap.Must` function that you can use with `NewProduction` or
|
||||
`NewDevelopment` to panic if the system was unable to build the logger.
|
||||
* [#1118][]: Add a `Logger.Log` method that allows specifying the log level for
|
||||
a statement dynamically.
|
||||
|
||||
Thanks to @cardil, @craigpastro, @sashamelentyev, @shota3506, and @zhupeijun
|
||||
for their contributions to this release.
|
||||
|
||||
[#1071]: https://github.com/uber-go/zap/pull/1071
|
||||
[#1079]: https://github.com/uber-go/zap/pull/1079
|
||||
[#1080]: https://github.com/uber-go/zap/pull/1080
|
||||
[#1088]: https://github.com/uber-go/zap/pull/1088
|
||||
[#1108]: https://github.com/uber-go/zap/pull/1108
|
||||
[#1118]: https://github.com/uber-go/zap/pull/1118
|
||||
|
||||
## 1.21.0 (7 Feb 2022)
|
||||
|
||||
@ -123,6 +223,16 @@ Enhancements:
|
||||
|
||||
Thanks to @ash2k, @FMLS, @jimmystewpot, @Oncilla, @tsoslow, @tylitianrui, @withshubh, and @wziww for their contributions to this release.
|
||||
|
||||
[#865]: https://github.com/uber-go/zap/pull/865
|
||||
[#867]: https://github.com/uber-go/zap/pull/867
|
||||
[#881]: https://github.com/uber-go/zap/pull/881
|
||||
[#903]: https://github.com/uber-go/zap/pull/903
|
||||
[#912]: https://github.com/uber-go/zap/pull/912
|
||||
[#913]: https://github.com/uber-go/zap/pull/913
|
||||
[#928]: https://github.com/uber-go/zap/pull/928
|
||||
[#931]: https://github.com/uber-go/zap/pull/931
|
||||
[#936]: https://github.com/uber-go/zap/pull/936
|
||||
|
||||
## 1.16.0 (1 Sep 2020)
|
||||
|
||||
Bugfixes:
|
||||
@ -144,6 +254,17 @@ Enhancements:
|
||||
|
||||
Thanks to @SteelPhase, @tmshn, @lixingwang, @wyxloading, @moul, @segevfiner, @andy-retailnext and @jcorbin for their contributions to this release.
|
||||
|
||||
[#629]: https://github.com/uber-go/zap/pull/629
|
||||
[#697]: https://github.com/uber-go/zap/pull/697
|
||||
[#828]: https://github.com/uber-go/zap/pull/828
|
||||
[#835]: https://github.com/uber-go/zap/pull/835
|
||||
[#843]: https://github.com/uber-go/zap/pull/843
|
||||
[#844]: https://github.com/uber-go/zap/pull/844
|
||||
[#852]: https://github.com/uber-go/zap/pull/852
|
||||
[#854]: https://github.com/uber-go/zap/pull/854
|
||||
[#861]: https://github.com/uber-go/zap/pull/861
|
||||
[#862]: https://github.com/uber-go/zap/pull/862
|
||||
|
||||
## 1.15.0 (23 Apr 2020)
|
||||
|
||||
Bugfixes:
|
||||
@ -160,6 +281,11 @@ Enhancements:
|
||||
|
||||
Thanks to @danielbprice for their contributions to this release.
|
||||
|
||||
[#804]: https://github.com/uber-go/zap/pull/804
|
||||
[#812]: https://github.com/uber-go/zap/pull/812
|
||||
[#806]: https://github.com/uber-go/zap/pull/806
|
||||
[#813]: https://github.com/uber-go/zap/pull/813
|
||||
|
||||
## 1.14.1 (14 Mar 2020)
|
||||
|
||||
Bugfixes:
|
||||
@ -172,6 +298,10 @@ Bugfixes:
|
||||
|
||||
Thanks to @YashishDua for their contributions to this release.
|
||||
|
||||
[#791]: https://github.com/uber-go/zap/pull/791
|
||||
[#795]: https://github.com/uber-go/zap/pull/795
|
||||
[#799]: https://github.com/uber-go/zap/pull/799
|
||||
|
||||
## 1.14.0 (20 Feb 2020)
|
||||
|
||||
Enhancements:
|
||||
@ -182,6 +312,11 @@ Enhancements:
|
||||
|
||||
Thanks to @caibirdme for their contributions to this release.
|
||||
|
||||
[#771]: https://github.com/uber-go/zap/pull/771
|
||||
[#773]: https://github.com/uber-go/zap/pull/773
|
||||
[#775]: https://github.com/uber-go/zap/pull/775
|
||||
[#786]: https://github.com/uber-go/zap/pull/786
|
||||
|
||||
## 1.13.0 (13 Nov 2019)
|
||||
|
||||
Enhancements:
|
||||
@ -190,11 +325,15 @@ Enhancements:
|
||||
|
||||
Thanks to @jbizzle for their contributions to this release.
|
||||
|
||||
[#758]: https://github.com/uber-go/zap/pull/758
|
||||
|
||||
## 1.12.0 (29 Oct 2019)
|
||||
|
||||
Enhancements:
|
||||
* [#751][]: Migrate to Go modules.
|
||||
|
||||
[#751]: https://github.com/uber-go/zap/pull/751
|
||||
|
||||
## 1.11.0 (21 Oct 2019)
|
||||
|
||||
Enhancements:
|
||||
@ -203,6 +342,9 @@ Enhancements:
|
||||
|
||||
Thanks to @juicemia, @uhthomas for their contributions to this release.
|
||||
|
||||
[#725]: https://github.com/uber-go/zap/pull/725
|
||||
[#736]: https://github.com/uber-go/zap/pull/736
|
||||
|
||||
## 1.10.0 (29 Apr 2019)
|
||||
|
||||
Bugfixes:
|
||||
@ -220,13 +362,21 @@ Enhancements:
|
||||
Thanks to @iaroslav-ciupin, @lelenanam, @joa, @NWilson for their contributions
|
||||
to this release.
|
||||
|
||||
## v1.9.1 (06 Aug 2018)
|
||||
[#657]: https://github.com/uber-go/zap/pull/657
|
||||
[#706]: https://github.com/uber-go/zap/pull/706
|
||||
[#610]: https://github.com/uber-go/zap/pull/610
|
||||
[#675]: https://github.com/uber-go/zap/pull/675
|
||||
[#704]: https://github.com/uber-go/zap/pull/704
|
||||
|
||||
## 1.9.1 (06 Aug 2018)
|
||||
|
||||
Bugfixes:
|
||||
|
||||
* [#614][]: MapObjectEncoder should not ignore empty slices.
|
||||
|
||||
## v1.9.0 (19 Jul 2018)
|
||||
[#614]: https://github.com/uber-go/zap/pull/614
|
||||
|
||||
## 1.9.0 (19 Jul 2018)
|
||||
|
||||
Enhancements:
|
||||
* [#602][]: Reduce number of allocations when logging with reflection.
|
||||
@ -235,7 +385,11 @@ Enhancements:
|
||||
Thanks to @nfarah86, @AlekSi, @JeanMertz, @philippgille, @etsangsplk, and
|
||||
@dimroc for their contributions to this release.
|
||||
|
||||
## v1.8.0 (13 Apr 2018)
|
||||
[#602]: https://github.com/uber-go/zap/pull/602
|
||||
[#572]: https://github.com/uber-go/zap/pull/572
|
||||
[#606]: https://github.com/uber-go/zap/pull/606
|
||||
|
||||
## 1.8.0 (13 Apr 2018)
|
||||
|
||||
Enhancements:
|
||||
* [#508][]: Make log level configurable when redirecting the standard
|
||||
@ -248,19 +402,28 @@ Bugfixes:
|
||||
|
||||
Thanks to @DiSiqueira and @djui for their contributions to this release.
|
||||
|
||||
## v1.7.1 (25 Sep 2017)
|
||||
[#508]: https://github.com/uber-go/zap/pull/508
|
||||
[#518]: https://github.com/uber-go/zap/pull/518
|
||||
[#577]: https://github.com/uber-go/zap/pull/577
|
||||
[#574]: https://github.com/uber-go/zap/pull/574
|
||||
|
||||
## 1.7.1 (25 Sep 2017)
|
||||
|
||||
Bugfixes:
|
||||
* [#504][]: Store strings when using AddByteString with the map encoder.
|
||||
|
||||
## v1.7.0 (21 Sep 2017)
|
||||
[#504]: https://github.com/uber-go/zap/pull/504
|
||||
|
||||
## 1.7.0 (21 Sep 2017)
|
||||
|
||||
Enhancements:
|
||||
|
||||
* [#487][]: Add `NewStdLogAt`, which extends `NewStdLog` by allowing the user
|
||||
to specify the level of the logged messages.
|
||||
|
||||
## v1.6.0 (30 Aug 2017)
|
||||
[#487]: https://github.com/uber-go/zap/pull/487
|
||||
|
||||
## 1.6.0 (30 Aug 2017)
|
||||
|
||||
Enhancements:
|
||||
|
||||
@ -268,7 +431,10 @@ Enhancements:
|
||||
* [#490][]: Add a `ContextMap` method to observer logs for simpler
|
||||
field validation in tests.
|
||||
|
||||
## v1.5.0 (22 Jul 2017)
|
||||
[#490]: https://github.com/uber-go/zap/pull/490
|
||||
[#491]: https://github.com/uber-go/zap/pull/491
|
||||
|
||||
## 1.5.0 (22 Jul 2017)
|
||||
|
||||
Enhancements:
|
||||
|
||||
@ -281,7 +447,12 @@ Bugfixes:
|
||||
|
||||
Thanks to @richard-tunein and @pavius for their contributions to this release.
|
||||
|
||||
## v1.4.1 (08 Jun 2017)
|
||||
[#477]: https://github.com/uber-go/zap/pull/477
|
||||
[#465]: https://github.com/uber-go/zap/pull/465
|
||||
[#460]: https://github.com/uber-go/zap/pull/460
|
||||
[#470]: https://github.com/uber-go/zap/pull/470
|
||||
|
||||
## 1.4.1 (08 Jun 2017)
|
||||
|
||||
This release fixes two bugs.
|
||||
|
||||
@ -290,7 +461,10 @@ Bugfixes:
|
||||
* [#435][]: Support a variety of case conventions when unmarshaling levels.
|
||||
* [#444][]: Fix a panic in the observer.
|
||||
|
||||
## v1.4.0 (12 May 2017)
|
||||
[#435]: https://github.com/uber-go/zap/pull/435
|
||||
[#444]: https://github.com/uber-go/zap/pull/444
|
||||
|
||||
## 1.4.0 (12 May 2017)
|
||||
|
||||
This release adds a few small features and is fully backward-compatible.
|
||||
|
||||
@ -302,7 +476,11 @@ Enhancements:
|
||||
* [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a
|
||||
variety of operations a bit simpler.
|
||||
|
||||
## v1.3.0 (25 Apr 2017)
|
||||
[#424]: https://github.com/uber-go/zap/pull/424
|
||||
[#425]: https://github.com/uber-go/zap/pull/425
|
||||
[#431]: https://github.com/uber-go/zap/pull/431
|
||||
|
||||
## 1.3.0 (25 Apr 2017)
|
||||
|
||||
This release adds an enhancement to zap's testing helpers as well as the
|
||||
ability to marshal an AtomicLevel. It is fully backward-compatible.
|
||||
@ -313,7 +491,10 @@ Enhancements:
|
||||
particularly useful when testing the `SugaredLogger`.
|
||||
* [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`.
|
||||
|
||||
## v1.2.0 (13 Apr 2017)
|
||||
[#415]: https://github.com/uber-go/zap/pull/415
|
||||
[#416]: https://github.com/uber-go/zap/pull/416
|
||||
|
||||
## 1.2.0 (13 Apr 2017)
|
||||
|
||||
This release adds a gRPC compatibility wrapper. It is fully backward-compatible.
|
||||
|
||||
@ -322,7 +503,9 @@ Enhancements:
|
||||
* [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements
|
||||
`grpclog.Logger`.
|
||||
|
||||
## v1.1.0 (31 Mar 2017)
|
||||
[#402]: https://github.com/uber-go/zap/pull/402
|
||||
|
||||
## 1.1.0 (31 Mar 2017)
|
||||
|
||||
This release fixes two bugs and adds some enhancements to zap's testing helpers.
|
||||
It is fully backward-compatible.
|
||||
@ -339,7 +522,11 @@ Enhancements:
|
||||
|
||||
Thanks to @moitias for contributing to this release.
|
||||
|
||||
## v1.0.0 (14 Mar 2017)
|
||||
[#385]: https://github.com/uber-go/zap/pull/385
|
||||
[#396]: https://github.com/uber-go/zap/pull/396
|
||||
[#386]: https://github.com/uber-go/zap/pull/386
|
||||
|
||||
## 1.0.0 (14 Mar 2017)
|
||||
|
||||
This is zap's first stable release. All exported APIs are now final, and no
|
||||
further breaking changes will be made in the 1.x release series. Anyone using a
|
||||
@ -384,7 +571,21 @@ Enhancements:
|
||||
Thanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their
|
||||
contributions to this release.
|
||||
|
||||
## v1.0.0-rc.3 (7 Mar 2017)
|
||||
[#366]: https://github.com/uber-go/zap/pull/366
|
||||
[#364]: https://github.com/uber-go/zap/pull/364
|
||||
[#371]: https://github.com/uber-go/zap/pull/371
|
||||
[#362]: https://github.com/uber-go/zap/pull/362
|
||||
[#369]: https://github.com/uber-go/zap/pull/369
|
||||
[#347]: https://github.com/uber-go/zap/pull/347
|
||||
[#373]: https://github.com/uber-go/zap/pull/373
|
||||
[#348]: https://github.com/uber-go/zap/pull/348
|
||||
[#327]: https://github.com/uber-go/zap/pull/327
|
||||
[#376]: https://github.com/uber-go/zap/pull/376
|
||||
[#346]: https://github.com/uber-go/zap/pull/346
|
||||
[#365]: https://github.com/uber-go/zap/pull/365
|
||||
[#372]: https://github.com/uber-go/zap/pull/372
|
||||
|
||||
## 1.0.0-rc.3 (7 Mar 2017)
|
||||
|
||||
This is the third release candidate for zap's stable release. There are no
|
||||
breaking changes.
|
||||
@ -405,7 +606,12 @@ Enhancements:
|
||||
|
||||
Thanks to @ansel1 and @suyash for their contributions to this release.
|
||||
|
||||
## v1.0.0-rc.2 (21 Feb 2017)
|
||||
[#339]: https://github.com/uber-go/zap/pull/339
|
||||
[#307]: https://github.com/uber-go/zap/pull/307
|
||||
[#353]: https://github.com/uber-go/zap/pull/353
|
||||
[#311]: https://github.com/uber-go/zap/pull/311
|
||||
|
||||
## 1.0.0-rc.2 (21 Feb 2017)
|
||||
|
||||
This is the second release candidate for zap's stable release. It includes two
|
||||
breaking changes.
|
||||
@ -442,7 +648,16 @@ Enhancements:
|
||||
|
||||
Thanks to @skipor and @chapsuk for their contributions to this release.
|
||||
|
||||
## v1.0.0-rc.1 (14 Feb 2017)
|
||||
[#316]: https://github.com/uber-go/zap/pull/316
|
||||
[#309]: https://github.com/uber-go/zap/pull/309
|
||||
[#317]: https://github.com/uber-go/zap/pull/317
|
||||
[#321]: https://github.com/uber-go/zap/pull/321
|
||||
[#325]: https://github.com/uber-go/zap/pull/325
|
||||
[#333]: https://github.com/uber-go/zap/pull/333
|
||||
[#326]: https://github.com/uber-go/zap/pull/326
|
||||
[#300]: https://github.com/uber-go/zap/pull/300
|
||||
|
||||
## 1.0.0-rc.1 (14 Feb 2017)
|
||||
|
||||
This is the first release candidate for zap's stable release. There are multiple
|
||||
breaking changes and improvements from the pre-release version. Most notably:
|
||||
@ -462,7 +677,7 @@ breaking changes and improvements from the pre-release version. Most notably:
|
||||
* Sampling is more accurate, and doesn't depend on the standard library's shared
|
||||
timer heap.
|
||||
|
||||
## v0.1.0-beta.1 (6 Feb 2017)
|
||||
## 0.1.0-beta.1 (6 Feb 2017)
|
||||
|
||||
This is a minor version, tagged to allow users to pin to the pre-1.0 APIs and
|
||||
upgrade at their leisure. Since this is the first tagged release, there are no
|
||||
@ -470,95 +685,3 @@ backward compatibility concerns and all functionality is new.
|
||||
|
||||
Early zap adopters should pin to the 0.1.x minor version until they're ready to
|
||||
upgrade to the upcoming stable release.
|
||||
|
||||
[#316]: https://github.com/uber-go/zap/pull/316
|
||||
[#309]: https://github.com/uber-go/zap/pull/309
|
||||
[#317]: https://github.com/uber-go/zap/pull/317
|
||||
[#321]: https://github.com/uber-go/zap/pull/321
|
||||
[#325]: https://github.com/uber-go/zap/pull/325
|
||||
[#333]: https://github.com/uber-go/zap/pull/333
|
||||
[#326]: https://github.com/uber-go/zap/pull/326
|
||||
[#300]: https://github.com/uber-go/zap/pull/300
|
||||
[#339]: https://github.com/uber-go/zap/pull/339
|
||||
[#307]: https://github.com/uber-go/zap/pull/307
|
||||
[#353]: https://github.com/uber-go/zap/pull/353
|
||||
[#311]: https://github.com/uber-go/zap/pull/311
|
||||
[#366]: https://github.com/uber-go/zap/pull/366
|
||||
[#364]: https://github.com/uber-go/zap/pull/364
|
||||
[#371]: https://github.com/uber-go/zap/pull/371
|
||||
[#362]: https://github.com/uber-go/zap/pull/362
|
||||
[#369]: https://github.com/uber-go/zap/pull/369
|
||||
[#347]: https://github.com/uber-go/zap/pull/347
|
||||
[#373]: https://github.com/uber-go/zap/pull/373
|
||||
[#348]: https://github.com/uber-go/zap/pull/348
|
||||
[#327]: https://github.com/uber-go/zap/pull/327
|
||||
[#376]: https://github.com/uber-go/zap/pull/376
|
||||
[#346]: https://github.com/uber-go/zap/pull/346
|
||||
[#365]: https://github.com/uber-go/zap/pull/365
|
||||
[#372]: https://github.com/uber-go/zap/pull/372
|
||||
[#385]: https://github.com/uber-go/zap/pull/385
|
||||
[#396]: https://github.com/uber-go/zap/pull/396
|
||||
[#386]: https://github.com/uber-go/zap/pull/386
|
||||
[#402]: https://github.com/uber-go/zap/pull/402
|
||||
[#415]: https://github.com/uber-go/zap/pull/415
|
||||
[#416]: https://github.com/uber-go/zap/pull/416
|
||||
[#424]: https://github.com/uber-go/zap/pull/424
|
||||
[#425]: https://github.com/uber-go/zap/pull/425
|
||||
[#431]: https://github.com/uber-go/zap/pull/431
|
||||
[#435]: https://github.com/uber-go/zap/pull/435
|
||||
[#444]: https://github.com/uber-go/zap/pull/444
|
||||
[#477]: https://github.com/uber-go/zap/pull/477
|
||||
[#465]: https://github.com/uber-go/zap/pull/465
|
||||
[#460]: https://github.com/uber-go/zap/pull/460
|
||||
[#470]: https://github.com/uber-go/zap/pull/470
|
||||
[#487]: https://github.com/uber-go/zap/pull/487
|
||||
[#490]: https://github.com/uber-go/zap/pull/490
|
||||
[#491]: https://github.com/uber-go/zap/pull/491
|
||||
[#504]: https://github.com/uber-go/zap/pull/504
|
||||
[#508]: https://github.com/uber-go/zap/pull/508
|
||||
[#518]: https://github.com/uber-go/zap/pull/518
|
||||
[#577]: https://github.com/uber-go/zap/pull/577
|
||||
[#574]: https://github.com/uber-go/zap/pull/574
|
||||
[#602]: https://github.com/uber-go/zap/pull/602
|
||||
[#572]: https://github.com/uber-go/zap/pull/572
|
||||
[#606]: https://github.com/uber-go/zap/pull/606
|
||||
[#614]: https://github.com/uber-go/zap/pull/614
|
||||
[#657]: https://github.com/uber-go/zap/pull/657
|
||||
[#706]: https://github.com/uber-go/zap/pull/706
|
||||
[#610]: https://github.com/uber-go/zap/pull/610
|
||||
[#675]: https://github.com/uber-go/zap/pull/675
|
||||
[#704]: https://github.com/uber-go/zap/pull/704
|
||||
[#725]: https://github.com/uber-go/zap/pull/725
|
||||
[#736]: https://github.com/uber-go/zap/pull/736
|
||||
[#751]: https://github.com/uber-go/zap/pull/751
|
||||
[#758]: https://github.com/uber-go/zap/pull/758
|
||||
[#771]: https://github.com/uber-go/zap/pull/771
|
||||
[#773]: https://github.com/uber-go/zap/pull/773
|
||||
[#775]: https://github.com/uber-go/zap/pull/775
|
||||
[#786]: https://github.com/uber-go/zap/pull/786
|
||||
[#791]: https://github.com/uber-go/zap/pull/791
|
||||
[#795]: https://github.com/uber-go/zap/pull/795
|
||||
[#799]: https://github.com/uber-go/zap/pull/799
|
||||
[#804]: https://github.com/uber-go/zap/pull/804
|
||||
[#812]: https://github.com/uber-go/zap/pull/812
|
||||
[#806]: https://github.com/uber-go/zap/pull/806
|
||||
[#813]: https://github.com/uber-go/zap/pull/813
|
||||
[#629]: https://github.com/uber-go/zap/pull/629
|
||||
[#697]: https://github.com/uber-go/zap/pull/697
|
||||
[#828]: https://github.com/uber-go/zap/pull/828
|
||||
[#835]: https://github.com/uber-go/zap/pull/835
|
||||
[#843]: https://github.com/uber-go/zap/pull/843
|
||||
[#844]: https://github.com/uber-go/zap/pull/844
|
||||
[#852]: https://github.com/uber-go/zap/pull/852
|
||||
[#854]: https://github.com/uber-go/zap/pull/854
|
||||
[#861]: https://github.com/uber-go/zap/pull/861
|
||||
[#862]: https://github.com/uber-go/zap/pull/862
|
||||
[#865]: https://github.com/uber-go/zap/pull/865
|
||||
[#867]: https://github.com/uber-go/zap/pull/867
|
||||
[#881]: https://github.com/uber-go/zap/pull/881
|
||||
[#903]: https://github.com/uber-go/zap/pull/903
|
||||
[#912]: https://github.com/uber-go/zap/pull/912
|
||||
[#913]: https://github.com/uber-go/zap/pull/913
|
||||
[#928]: https://github.com/uber-go/zap/pull/928
|
||||
[#931]: https://github.com/uber-go/zap/pull/931
|
||||
[#936]: https://github.com/uber-go/zap/pull/936
|
||||
|
21
vendor/go.uber.org/zap/CONTRIBUTING.md
generated
vendored
21
vendor/go.uber.org/zap/CONTRIBUTING.md
generated
vendored
@ -16,7 +16,7 @@ you to accept the CLA when you open your pull request.
|
||||
|
||||
[Fork][fork], then clone the repository:
|
||||
|
||||
```
|
||||
```bash
|
||||
mkdir -p $GOPATH/src/go.uber.org
|
||||
cd $GOPATH/src/go.uber.org
|
||||
git clone git@github.com:your_github_username/zap.git
|
||||
@ -27,21 +27,16 @@ git fetch upstream
|
||||
|
||||
Make sure that the tests and the linters pass:
|
||||
|
||||
```
|
||||
```bash
|
||||
make test
|
||||
make lint
|
||||
```
|
||||
|
||||
If you're not using the minor version of Go specified in the Makefile's
|
||||
`LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is
|
||||
fine, but it means that you'll only discover lint failures after you open your
|
||||
pull request.
|
||||
|
||||
## Making Changes
|
||||
|
||||
Start by creating a new branch for your changes:
|
||||
|
||||
```
|
||||
```bash
|
||||
cd $GOPATH/src/go.uber.org/zap
|
||||
git checkout master
|
||||
git fetch upstream
|
||||
@ -52,22 +47,22 @@ git checkout -b cool_new_feature
|
||||
Make your changes, then ensure that `make lint` and `make test` still pass. If
|
||||
you're satisfied with your changes, push them to your fork.
|
||||
|
||||
```
|
||||
```bash
|
||||
git push origin cool_new_feature
|
||||
```
|
||||
|
||||
Then use the GitHub UI to open a pull request.
|
||||
|
||||
At this point, you're waiting on us to review your changes. We *try* to respond
|
||||
At this point, you're waiting on us to review your changes. We _try_ to respond
|
||||
to issues and pull requests within a few business days, and we may suggest some
|
||||
improvements or alternatives. Once your changes are approved, one of the
|
||||
project maintainers will merge them.
|
||||
|
||||
We're much more likely to approve your changes if you:
|
||||
|
||||
* Add tests for new functionality.
|
||||
* Write a [good commit message][commit-message].
|
||||
* Maintain backward compatibility.
|
||||
- Add tests for new functionality.
|
||||
- Write a [good commit message][commit-message].
|
||||
- Maintain backward compatibility.
|
||||
|
||||
[fork]: https://github.com/uber-go/zap/fork
|
||||
[open-issue]: https://github.com/uber-go/zap/issues/new
|
||||
|
0
vendor/go.uber.org/zap/LICENSE.txt → vendor/go.uber.org/zap/LICENSE
generated
vendored
0
vendor/go.uber.org/zap/LICENSE.txt → vendor/go.uber.org/zap/LICENSE
generated
vendored
83
vendor/go.uber.org/zap/Makefile
generated
vendored
83
vendor/go.uber.org/zap/Makefile
generated
vendored
@ -1,50 +1,51 @@
|
||||
export GOBIN ?= $(shell pwd)/bin
|
||||
# Directory containing the Makefile.
|
||||
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
GOLINT = $(GOBIN)/golint
|
||||
STATICCHECK = $(GOBIN)/staticcheck
|
||||
export GOBIN ?= $(PROJECT_ROOT)/bin
|
||||
export PATH := $(GOBIN):$(PATH)
|
||||
|
||||
GOVULNCHECK = $(GOBIN)/govulncheck
|
||||
BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem
|
||||
|
||||
# Directories containing independent Go modules.
|
||||
#
|
||||
# We track coverage only for the main module.
|
||||
MODULE_DIRS = . ./benchmarks ./zapgrpc/internal/test
|
||||
MODULE_DIRS = . ./exp ./benchmarks ./zapgrpc/internal/test
|
||||
|
||||
# Many Go tools take file globs or directories as arguments instead of packages.
|
||||
GO_FILES := $(shell \
|
||||
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
|
||||
-o -name '*.go' -print | cut -b3-)
|
||||
# Directories that we want to track coverage for.
|
||||
COVER_DIRS = . ./exp
|
||||
|
||||
.PHONY: all
|
||||
all: lint test
|
||||
|
||||
.PHONY: lint
|
||||
lint: $(GOLINT) $(STATICCHECK)
|
||||
@rm -rf lint.log
|
||||
@echo "Checking formatting..."
|
||||
@gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log
|
||||
@echo "Checking vet..."
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go vet ./... 2>&1) &&) true | tee -a lint.log
|
||||
@echo "Checking lint..."
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(GOLINT) ./... 2>&1) &&) true | tee -a lint.log
|
||||
@echo "Checking staticcheck..."
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(STATICCHECK) ./... 2>&1) &&) true | tee -a lint.log
|
||||
@echo "Checking for unresolved FIXMEs..."
|
||||
@git grep -i fixme | grep -v -e Makefile | tee -a lint.log
|
||||
@echo "Checking for license headers..."
|
||||
@./checklicense.sh | tee -a lint.log
|
||||
@[ ! -s lint.log ]
|
||||
@echo "Checking 'go mod tidy'..."
|
||||
@make tidy
|
||||
@if ! git diff --quiet; then \
|
||||
echo "'go mod tidy' resulted in changes or working tree is dirty:"; \
|
||||
git --no-pager diff; \
|
||||
fi
|
||||
lint: golangci-lint tidy-lint license-lint
|
||||
|
||||
$(GOLINT):
|
||||
cd tools && go install golang.org/x/lint/golint
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint:
|
||||
@$(foreach mod,$(MODULE_DIRS), \
|
||||
(cd $(mod) && \
|
||||
echo "[lint] golangci-lint: $(mod)" && \
|
||||
golangci-lint run --path-prefix $(mod)) &&) true
|
||||
|
||||
$(STATICCHECK):
|
||||
cd tools && go install honnef.co/go/tools/cmd/staticcheck
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
@$(foreach dir,$(MODULE_DIRS), \
|
||||
(cd $(dir) && go mod tidy) &&) true
|
||||
|
||||
.PHONY: tidy-lint
|
||||
tidy-lint:
|
||||
@$(foreach mod,$(MODULE_DIRS), \
|
||||
(cd $(mod) && \
|
||||
echo "[lint] tidy: $(mod)" && \
|
||||
go mod tidy && \
|
||||
git diff --exit-code -- go.mod go.sum) &&) true
|
||||
|
||||
|
||||
.PHONY: license-lint
|
||||
license-lint:
|
||||
./checklicense.sh
|
||||
|
||||
$(GOVULNCHECK):
|
||||
cd tools && go install golang.org/x/vuln/cmd/govulncheck
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@ -52,8 +53,10 @@ test:
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -race -coverprofile=cover.out -coverpkg=./... ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
@$(foreach dir,$(COVER_DIRS), ( \
|
||||
cd $(dir) && \
|
||||
go test -race -coverprofile=cover.out -coverpkg=./... ./... \
|
||||
&& go tool cover -html=cover.out -o cover.html) &&) true
|
||||
|
||||
.PHONY: bench
|
||||
BENCH ?= .
|
||||
@ -68,6 +71,6 @@ updatereadme:
|
||||
rm -f README.md
|
||||
cat .readme.tmpl | go run internal/readme/readme.go > README.md
|
||||
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
@$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go mod tidy) &&) true
|
||||
.PHONY: vulncheck
|
||||
vulncheck: $(GOVULNCHECK)
|
||||
$(GOVULNCHECK) ./...
|
||||
|
63
vendor/go.uber.org/zap/README.md
generated
vendored
63
vendor/go.uber.org/zap/README.md
generated
vendored
@ -1,7 +1,16 @@
|
||||
# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
# :zap: zap
|
||||
|
||||
|
||||
<div align="center">
|
||||
|
||||
Blazing fast, structured, leveled logging in Go.
|
||||
|
||||

|
||||
|
||||
[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
</div>
|
||||
|
||||
## Installation
|
||||
|
||||
`go get -u go.uber.org/zap`
|
||||
@ -66,38 +75,44 @@ Log a message and 10 fields:
|
||||
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------ | :--: | :-----------: | :---------------: |
|
||||
| :zap: zap | 2900 ns/op | +0% | 5 allocs/op
|
||||
| :zap: zap (sugared) | 3475 ns/op | +20% | 10 allocs/op
|
||||
| zerolog | 10639 ns/op | +267% | 32 allocs/op
|
||||
| go-kit | 14434 ns/op | +398% | 59 allocs/op
|
||||
| logrus | 17104 ns/op | +490% | 81 allocs/op
|
||||
| apex/log | 32424 ns/op | +1018% | 66 allocs/op
|
||||
| log15 | 33579 ns/op | +1058% | 76 allocs/op
|
||||
| :zap: zap | 656 ns/op | +0% | 5 allocs/op
|
||||
| :zap: zap (sugared) | 935 ns/op | +43% | 10 allocs/op
|
||||
| zerolog | 380 ns/op | -42% | 1 allocs/op
|
||||
| go-kit | 2249 ns/op | +243% | 57 allocs/op
|
||||
| slog (LogAttrs) | 2479 ns/op | +278% | 40 allocs/op
|
||||
| slog | 2481 ns/op | +278% | 42 allocs/op
|
||||
| apex/log | 9591 ns/op | +1362% | 63 allocs/op
|
||||
| log15 | 11393 ns/op | +1637% | 75 allocs/op
|
||||
| logrus | 11654 ns/op | +1677% | 79 allocs/op
|
||||
|
||||
Log a message with a logger that already has 10 fields of context:
|
||||
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------ | :--: | :-----------: | :---------------: |
|
||||
| :zap: zap | 373 ns/op | +0% | 0 allocs/op
|
||||
| :zap: zap (sugared) | 452 ns/op | +21% | 1 allocs/op
|
||||
| zerolog | 288 ns/op | -23% | 0 allocs/op
|
||||
| go-kit | 11785 ns/op | +3060% | 58 allocs/op
|
||||
| logrus | 19629 ns/op | +5162% | 70 allocs/op
|
||||
| log15 | 21866 ns/op | +5762% | 72 allocs/op
|
||||
| apex/log | 30890 ns/op | +8182% | 55 allocs/op
|
||||
| :zap: zap | 67 ns/op | +0% | 0 allocs/op
|
||||
| :zap: zap (sugared) | 84 ns/op | +25% | 1 allocs/op
|
||||
| zerolog | 35 ns/op | -48% | 0 allocs/op
|
||||
| slog | 193 ns/op | +188% | 0 allocs/op
|
||||
| slog (LogAttrs) | 200 ns/op | +199% | 0 allocs/op
|
||||
| go-kit | 2460 ns/op | +3572% | 56 allocs/op
|
||||
| log15 | 9038 ns/op | +13390% | 70 allocs/op
|
||||
| apex/log | 9068 ns/op | +13434% | 53 allocs/op
|
||||
| logrus | 10521 ns/op | +15603% | 68 allocs/op
|
||||
|
||||
Log a static string, without any context or `printf`-style templating:
|
||||
|
||||
| Package | Time | Time % to zap | Objects Allocated |
|
||||
| :------ | :--: | :-----------: | :---------------: |
|
||||
| :zap: zap | 381 ns/op | +0% | 0 allocs/op
|
||||
| :zap: zap (sugared) | 410 ns/op | +8% | 1 allocs/op
|
||||
| zerolog | 369 ns/op | -3% | 0 allocs/op
|
||||
| standard library | 385 ns/op | +1% | 2 allocs/op
|
||||
| go-kit | 606 ns/op | +59% | 11 allocs/op
|
||||
| logrus | 1730 ns/op | +354% | 25 allocs/op
|
||||
| apex/log | 1998 ns/op | +424% | 7 allocs/op
|
||||
| log15 | 4546 ns/op | +1093% | 22 allocs/op
|
||||
| :zap: zap | 63 ns/op | +0% | 0 allocs/op
|
||||
| :zap: zap (sugared) | 81 ns/op | +29% | 1 allocs/op
|
||||
| zerolog | 32 ns/op | -49% | 0 allocs/op
|
||||
| standard library | 124 ns/op | +97% | 1 allocs/op
|
||||
| slog | 196 ns/op | +211% | 0 allocs/op
|
||||
| slog (LogAttrs) | 200 ns/op | +217% | 0 allocs/op
|
||||
| go-kit | 213 ns/op | +238% | 9 allocs/op
|
||||
| apex/log | 771 ns/op | +1124% | 5 allocs/op
|
||||
| logrus | 1439 ns/op | +2184% | 23 allocs/op
|
||||
| log15 | 2069 ns/op | +3184% | 20 allocs/op
|
||||
|
||||
## Development Status: Stable
|
||||
|
||||
@ -117,7 +132,7 @@ standard.
|
||||
|
||||
<hr>
|
||||
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
Released under the [MIT License](LICENSE).
|
||||
|
||||
<sup id="footnote-versions">1</sup> In particular, keep in mind that we may be
|
||||
benchmarking against slightly older versions of other packages. Versions are
|
||||
|
127
vendor/go.uber.org/zap/array.go
generated
vendored
127
vendor/go.uber.org/zap/array.go
generated
vendored
@ -21,6 +21,7 @@
|
||||
package zap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
@ -94,11 +95,137 @@ func Int8s(key string, nums []int8) Field {
|
||||
return Array(key, int8s(nums))
|
||||
}
|
||||
|
||||
// Objects constructs a field with the given key, holding a list of the
|
||||
// provided objects that can be marshaled by Zap.
|
||||
//
|
||||
// Note that these objects must implement zapcore.ObjectMarshaler directly.
|
||||
// That is, if you're trying to marshal a []Request, the MarshalLogObject
|
||||
// method must be declared on the Request type, not its pointer (*Request).
|
||||
// If it's on the pointer, use ObjectValues.
|
||||
//
|
||||
// Given an object that implements MarshalLogObject on the value receiver, you
|
||||
// can log a slice of those objects with Objects like so:
|
||||
//
|
||||
// type Author struct{ ... }
|
||||
// func (a Author) MarshalLogObject(enc zapcore.ObjectEncoder) error
|
||||
//
|
||||
// var authors []Author = ...
|
||||
// logger.Info("loading article", zap.Objects("authors", authors))
|
||||
//
|
||||
// Similarly, given a type that implements MarshalLogObject on its pointer
|
||||
// receiver, you can log a slice of pointers to that object with Objects like
|
||||
// so:
|
||||
//
|
||||
// type Request struct{ ... }
|
||||
// func (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error
|
||||
//
|
||||
// var requests []*Request = ...
|
||||
// logger.Info("sending requests", zap.Objects("requests", requests))
|
||||
//
|
||||
// If instead, you have a slice of values of such an object, use the
|
||||
// ObjectValues constructor.
|
||||
//
|
||||
// var requests []Request = ...
|
||||
// logger.Info("sending requests", zap.ObjectValues("requests", requests))
|
||||
func Objects[T zapcore.ObjectMarshaler](key string, values []T) Field {
|
||||
return Array(key, objects[T](values))
|
||||
}
|
||||
|
||||
type objects[T zapcore.ObjectMarshaler] []T
|
||||
|
||||
func (os objects[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for _, o := range os {
|
||||
if err := arr.AppendObject(o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ObjectMarshalerPtr is a constraint that specifies that the given type
|
||||
// implements zapcore.ObjectMarshaler on a pointer receiver.
|
||||
type ObjectMarshalerPtr[T any] interface {
|
||||
*T
|
||||
zapcore.ObjectMarshaler
|
||||
}
|
||||
|
||||
// ObjectValues constructs a field with the given key, holding a list of the
|
||||
// provided objects, where pointers to these objects can be marshaled by Zap.
|
||||
//
|
||||
// Note that pointers to these objects must implement zapcore.ObjectMarshaler.
|
||||
// That is, if you're trying to marshal a []Request, the MarshalLogObject
|
||||
// method must be declared on the *Request type, not the value (Request).
|
||||
// If it's on the value, use Objects.
|
||||
//
|
||||
// Given an object that implements MarshalLogObject on the pointer receiver,
|
||||
// you can log a slice of those objects with ObjectValues like so:
|
||||
//
|
||||
// type Request struct{ ... }
|
||||
// func (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error
|
||||
//
|
||||
// var requests []Request = ...
|
||||
// logger.Info("sending requests", zap.ObjectValues("requests", requests))
|
||||
//
|
||||
// If instead, you have a slice of pointers of such an object, use the Objects
|
||||
// field constructor.
|
||||
//
|
||||
// var requests []*Request = ...
|
||||
// logger.Info("sending requests", zap.Objects("requests", requests))
|
||||
func ObjectValues[T any, P ObjectMarshalerPtr[T]](key string, values []T) Field {
|
||||
return Array(key, objectValues[T, P](values))
|
||||
}
|
||||
|
||||
type objectValues[T any, P ObjectMarshalerPtr[T]] []T
|
||||
|
||||
func (os objectValues[T, P]) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for i := range os {
|
||||
// It is necessary for us to explicitly reference the "P" type.
|
||||
// We cannot simply pass "&os[i]" to AppendObject because its type
|
||||
// is "*T", which the type system does not consider as
|
||||
// implementing ObjectMarshaler.
|
||||
// Only the type "P" satisfies ObjectMarshaler, which we have
|
||||
// to convert "*T" to explicitly.
|
||||
var p P = &os[i]
|
||||
if err := arr.AppendObject(p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Strings constructs a field that carries a slice of strings.
|
||||
func Strings(key string, ss []string) Field {
|
||||
return Array(key, stringArray(ss))
|
||||
}
|
||||
|
||||
// Stringers constructs a field with the given key, holding a list of the
|
||||
// output provided by the value's String method
|
||||
//
|
||||
// Given an object that implements String on the value receiver, you
|
||||
// can log a slice of those objects with Objects like so:
|
||||
//
|
||||
// type Request struct{ ... }
|
||||
// func (a Request) String() string
|
||||
//
|
||||
// var requests []Request = ...
|
||||
// logger.Info("sending requests", zap.Stringers("requests", requests))
|
||||
//
|
||||
// Note that these objects must implement fmt.Stringer directly.
|
||||
// That is, if you're trying to marshal a []Request, the String method
|
||||
// must be declared on the Request type, not its pointer (*Request).
|
||||
func Stringers[T fmt.Stringer](key string, values []T) Field {
|
||||
return Array(key, stringers[T](values))
|
||||
}
|
||||
|
||||
type stringers[T fmt.Stringer] []T
|
||||
|
||||
func (os stringers[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
for _, o := range os {
|
||||
arr.AppendString(o.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Times constructs a field that carries a slice of time.Times.
|
||||
func Times(key string, ts []time.Time) Field {
|
||||
return Array(key, times(ts))
|
||||
|
5
vendor/go.uber.org/zap/buffer/buffer.go
generated
vendored
5
vendor/go.uber.org/zap/buffer/buffer.go
generated
vendored
@ -42,6 +42,11 @@ func (b *Buffer) AppendByte(v byte) {
|
||||
b.bs = append(b.bs, v)
|
||||
}
|
||||
|
||||
// AppendBytes writes the given slice of bytes to the Buffer.
|
||||
func (b *Buffer) AppendBytes(v []byte) {
|
||||
b.bs = append(b.bs, v...)
|
||||
}
|
||||
|
||||
// AppendString writes a string to the Buffer.
|
||||
func (b *Buffer) AppendString(s string) {
|
||||
b.bs = append(b.bs, s...)
|
||||
|
20
vendor/go.uber.org/zap/buffer/pool.go
generated
vendored
20
vendor/go.uber.org/zap/buffer/pool.go
generated
vendored
@ -20,25 +20,29 @@
|
||||
|
||||
package buffer
|
||||
|
||||
import "sync"
|
||||
import (
|
||||
"go.uber.org/zap/internal/pool"
|
||||
)
|
||||
|
||||
// A Pool is a type-safe wrapper around a sync.Pool.
|
||||
type Pool struct {
|
||||
p *sync.Pool
|
||||
p *pool.Pool[*Buffer]
|
||||
}
|
||||
|
||||
// NewPool constructs a new Pool.
|
||||
func NewPool() Pool {
|
||||
return Pool{p: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Buffer{bs: make([]byte, 0, _size)}
|
||||
},
|
||||
}}
|
||||
return Pool{
|
||||
p: pool.New(func() *Buffer {
|
||||
return &Buffer{
|
||||
bs: make([]byte, 0, _size),
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a Buffer from the pool, creating one if necessary.
|
||||
func (p Pool) Get() *Buffer {
|
||||
buf := p.p.Get().(*Buffer)
|
||||
buf := p.p.Get()
|
||||
buf.Reset()
|
||||
buf.pool = p
|
||||
return buf
|
||||
|
88
vendor/go.uber.org/zap/config.go
generated
vendored
88
vendor/go.uber.org/zap/config.go
generated
vendored
@ -21,7 +21,7 @@
|
||||
package zap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
@ -95,6 +95,32 @@ type Config struct {
|
||||
|
||||
// NewProductionEncoderConfig returns an opinionated EncoderConfig for
|
||||
// production environments.
|
||||
//
|
||||
// Messages encoded with this configuration will be JSON-formatted
|
||||
// and will have the following keys by default:
|
||||
//
|
||||
// - "level": The logging level (e.g. "info", "error").
|
||||
// - "ts": The current time in number of seconds since the Unix epoch.
|
||||
// - "msg": The message passed to the log statement.
|
||||
// - "caller": If available, a short path to the file and line number
|
||||
// where the log statement was issued.
|
||||
// The logger configuration determines whether this field is captured.
|
||||
// - "stacktrace": If available, a stack trace from the line
|
||||
// where the log statement was issued.
|
||||
// The logger configuration determines whether this field is captured.
|
||||
//
|
||||
// By default, the following formats are used for different types:
|
||||
//
|
||||
// - Time is formatted as floating-point number of seconds since the Unix
|
||||
// epoch.
|
||||
// - Duration is formatted as floating-point number of seconds.
|
||||
//
|
||||
// You may change these by setting the appropriate fields in the returned
|
||||
// object.
|
||||
// For example, use the following to change the time encoding format:
|
||||
//
|
||||
// cfg := zap.NewProductionEncoderConfig()
|
||||
// cfg.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
func NewProductionEncoderConfig() zapcore.EncoderConfig {
|
||||
return zapcore.EncoderConfig{
|
||||
TimeKey: "ts",
|
||||
@ -112,11 +138,22 @@ func NewProductionEncoderConfig() zapcore.EncoderConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// NewProductionConfig is a reasonable production logging configuration.
|
||||
// Logging is enabled at InfoLevel and above.
|
||||
// NewProductionConfig builds a reasonable default production logging
|
||||
// configuration.
|
||||
// Logging is enabled at InfoLevel and above, and uses a JSON encoder.
|
||||
// Logs are written to standard error.
|
||||
// Stacktraces are included on logs of ErrorLevel and above.
|
||||
// DPanicLevel logs will not panic, but will write a stacktrace.
|
||||
//
|
||||
// It uses a JSON encoder, writes to standard error, and enables sampling.
|
||||
// Stacktraces are automatically included on logs of ErrorLevel and above.
|
||||
// Sampling is enabled at 100:100 by default,
|
||||
// meaning that after the first 100 log entries
|
||||
// with the same level and message in the same second,
|
||||
// it will log every 100th entry
|
||||
// with the same level and message in the same second.
|
||||
// You may disable this behavior by setting Sampling to nil.
|
||||
//
|
||||
// See [NewProductionEncoderConfig] for information
|
||||
// on the default encoder configuration.
|
||||
func NewProductionConfig() Config {
|
||||
return Config{
|
||||
Level: NewAtomicLevelAt(InfoLevel),
|
||||
@ -134,6 +171,32 @@ func NewProductionConfig() Config {
|
||||
|
||||
// NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
|
||||
// development environments.
|
||||
//
|
||||
// Messages encoded with this configuration will use Zap's console encoder
|
||||
// intended to print human-readable output.
|
||||
// It will print log messages with the following information:
|
||||
//
|
||||
// - The log level (e.g. "INFO", "ERROR").
|
||||
// - The time in ISO8601 format (e.g. "2017-01-01T12:00:00Z").
|
||||
// - The message passed to the log statement.
|
||||
// - If available, a short path to the file and line number
|
||||
// where the log statement was issued.
|
||||
// The logger configuration determines whether this field is captured.
|
||||
// - If available, a stacktrace from the line
|
||||
// where the log statement was issued.
|
||||
// The logger configuration determines whether this field is captured.
|
||||
//
|
||||
// By default, the following formats are used for different types:
|
||||
//
|
||||
// - Time is formatted in ISO8601 format (e.g. "2017-01-01T12:00:00Z").
|
||||
// - Duration is formatted as a string (e.g. "1.234s").
|
||||
//
|
||||
// You may change these by setting the appropriate fields in the returned
|
||||
// object.
|
||||
// For example, use the following to change the time encoding format:
|
||||
//
|
||||
// cfg := zap.NewDevelopmentEncoderConfig()
|
||||
// cfg.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
|
||||
return zapcore.EncoderConfig{
|
||||
// Keys can be anything except the empty string.
|
||||
@ -152,12 +215,15 @@ func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// NewDevelopmentConfig is a reasonable development logging configuration.
|
||||
// Logging is enabled at DebugLevel and above.
|
||||
// NewDevelopmentConfig builds a reasonable default development logging
|
||||
// configuration.
|
||||
// Logging is enabled at DebugLevel and above, and uses a console encoder.
|
||||
// Logs are written to standard error.
|
||||
// Stacktraces are included on logs of WarnLevel and above.
|
||||
// DPanicLevel logs will panic.
|
||||
//
|
||||
// It enables development mode (which makes DPanicLevel logs panic), uses a
|
||||
// console encoder, writes to standard error, and disables sampling.
|
||||
// Stacktraces are automatically included on logs of WarnLevel and above.
|
||||
// See [NewDevelopmentEncoderConfig] for information
|
||||
// on the default encoder configuration.
|
||||
func NewDevelopmentConfig() Config {
|
||||
return Config{
|
||||
Level: NewAtomicLevelAt(DebugLevel),
|
||||
@ -182,7 +248,7 @@ func (cfg Config) Build(opts ...Option) (*Logger, error) {
|
||||
}
|
||||
|
||||
if cfg.Level == (AtomicLevel{}) {
|
||||
return nil, fmt.Errorf("missing Level")
|
||||
return nil, errors.New("missing Level")
|
||||
}
|
||||
|
||||
log := New(
|
||||
|
60
vendor/go.uber.org/zap/doc.go
generated
vendored
60
vendor/go.uber.org/zap/doc.go
generated
vendored
@ -32,7 +32,7 @@
|
||||
// they need to count every allocation and when they'd prefer a more familiar,
|
||||
// loosely typed API.
|
||||
//
|
||||
// Choosing a Logger
|
||||
// # Choosing a Logger
|
||||
//
|
||||
// In contexts where performance is nice, but not critical, use the
|
||||
// SugaredLogger. It's 4-10x faster than other structured logging packages and
|
||||
@ -41,14 +41,15 @@
|
||||
// variadic number of key-value pairs. (For more advanced use cases, they also
|
||||
// accept strongly typed fields - see the SugaredLogger.With documentation for
|
||||
// details.)
|
||||
// sugar := zap.NewExample().Sugar()
|
||||
// defer sugar.Sync()
|
||||
// sugar.Infow("failed to fetch URL",
|
||||
// "url", "http://example.com",
|
||||
// "attempt", 3,
|
||||
// "backoff", time.Second,
|
||||
// )
|
||||
// sugar.Infof("failed to fetch URL: %s", "http://example.com")
|
||||
//
|
||||
// sugar := zap.NewExample().Sugar()
|
||||
// defer sugar.Sync()
|
||||
// sugar.Infow("failed to fetch URL",
|
||||
// "url", "http://example.com",
|
||||
// "attempt", 3,
|
||||
// "backoff", time.Second,
|
||||
// )
|
||||
// sugar.Infof("failed to fetch URL: %s", "http://example.com")
|
||||
//
|
||||
// By default, loggers are unbuffered. However, since zap's low-level APIs
|
||||
// allow buffering, calling Sync before letting your process exit is a good
|
||||
@ -57,32 +58,35 @@
|
||||
// In the rare contexts where every microsecond and every allocation matter,
|
||||
// use the Logger. It's even faster than the SugaredLogger and allocates far
|
||||
// less, but it only supports strongly-typed, structured logging.
|
||||
// logger := zap.NewExample()
|
||||
// defer logger.Sync()
|
||||
// logger.Info("failed to fetch URL",
|
||||
// zap.String("url", "http://example.com"),
|
||||
// zap.Int("attempt", 3),
|
||||
// zap.Duration("backoff", time.Second),
|
||||
// )
|
||||
//
|
||||
// logger := zap.NewExample()
|
||||
// defer logger.Sync()
|
||||
// logger.Info("failed to fetch URL",
|
||||
// zap.String("url", "http://example.com"),
|
||||
// zap.Int("attempt", 3),
|
||||
// zap.Duration("backoff", time.Second),
|
||||
// )
|
||||
//
|
||||
// Choosing between the Logger and SugaredLogger doesn't need to be an
|
||||
// application-wide decision: converting between the two is simple and
|
||||
// inexpensive.
|
||||
// logger := zap.NewExample()
|
||||
// defer logger.Sync()
|
||||
// sugar := logger.Sugar()
|
||||
// plain := sugar.Desugar()
|
||||
//
|
||||
// Configuring Zap
|
||||
// logger := zap.NewExample()
|
||||
// defer logger.Sync()
|
||||
// sugar := logger.Sugar()
|
||||
// plain := sugar.Desugar()
|
||||
//
|
||||
// # Configuring Zap
|
||||
//
|
||||
// The simplest way to build a Logger is to use zap's opinionated presets:
|
||||
// NewExample, NewProduction, and NewDevelopment. These presets build a logger
|
||||
// with a single function call:
|
||||
// logger, err := zap.NewProduction()
|
||||
// if err != nil {
|
||||
// log.Fatalf("can't initialize zap logger: %v", err)
|
||||
// }
|
||||
// defer logger.Sync()
|
||||
//
|
||||
// logger, err := zap.NewProduction()
|
||||
// if err != nil {
|
||||
// log.Fatalf("can't initialize zap logger: %v", err)
|
||||
// }
|
||||
// defer logger.Sync()
|
||||
//
|
||||
// Presets are fine for small projects, but larger projects and organizations
|
||||
// naturally require a bit more customization. For most users, zap's Config
|
||||
@ -94,7 +98,7 @@
|
||||
// go.uber.org/zap/zapcore. See the package-level AdvancedConfiguration
|
||||
// example for sample code.
|
||||
//
|
||||
// Extending Zap
|
||||
// # Extending Zap
|
||||
//
|
||||
// The zap package itself is a relatively thin wrapper around the interfaces
|
||||
// in go.uber.org/zap/zapcore. Extending zap to support a new encoding (e.g.,
|
||||
@ -106,7 +110,7 @@
|
||||
// Similarly, package authors can use the high-performance Encoder and Core
|
||||
// implementations in the zapcore package to build their own loggers.
|
||||
//
|
||||
// Frequently Asked Questions
|
||||
// # Frequently Asked Questions
|
||||
//
|
||||
// An FAQ covering everything from installation errors to design decisions is
|
||||
// available at https://github.com/uber-go/zap/blob/master/FAQ.md.
|
||||
|
2
vendor/go.uber.org/zap/encoder.go
generated
vendored
2
vendor/go.uber.org/zap/encoder.go
generated
vendored
@ -63,7 +63,7 @@ func RegisterEncoder(name string, constructor func(zapcore.EncoderConfig) (zapco
|
||||
|
||||
func newEncoder(name string, encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
|
||||
if encoderConfig.TimeKey != "" && encoderConfig.EncodeTime == nil {
|
||||
return nil, fmt.Errorf("missing EncodeTime in EncoderConfig")
|
||||
return nil, errors.New("missing EncodeTime in EncoderConfig")
|
||||
}
|
||||
|
||||
_encoderMutex.RLock()
|
||||
|
14
vendor/go.uber.org/zap/error.go
generated
vendored
14
vendor/go.uber.org/zap/error.go
generated
vendored
@ -21,14 +21,13 @@
|
||||
package zap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap/internal/pool"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var _errArrayElemPool = sync.Pool{New: func() interface{} {
|
||||
var _errArrayElemPool = pool.New(func() *errArrayElem {
|
||||
return &errArrayElem{}
|
||||
}}
|
||||
})
|
||||
|
||||
// Error is shorthand for the common idiom NamedError("error", err).
|
||||
func Error(err error) Field {
|
||||
@ -60,11 +59,14 @@ func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
|
||||
// potentially an "errorVerbose" attribute, we need to wrap it in a
|
||||
// type that implements LogObjectMarshaler. To prevent this from
|
||||
// allocating, pool the wrapper type.
|
||||
elem := _errArrayElemPool.Get().(*errArrayElem)
|
||||
elem := _errArrayElemPool.Get()
|
||||
elem.error = errs[i]
|
||||
arr.AppendObject(elem)
|
||||
err := arr.AppendObject(elem)
|
||||
elem.error = nil
|
||||
_errArrayElemPool.Put(elem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
196
vendor/go.uber.org/zap/field.go
generated
vendored
196
vendor/go.uber.org/zap/field.go
generated
vendored
@ -25,6 +25,7 @@ import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap/internal/stacktrace"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
@ -374,7 +375,7 @@ func StackSkip(key string, skip int) Field {
|
||||
// from expanding the zapcore.Field union struct to include a byte slice. Since
|
||||
// taking a stacktrace is already so expensive (~10us), the extra allocation
|
||||
// is okay.
|
||||
return String(key, takeStacktrace(skip+1)) // skip StackSkip
|
||||
return String(key, stacktrace.Take(skip+1)) // skip StackSkip
|
||||
}
|
||||
|
||||
// Duration constructs a field with the given key and value. The encoder
|
||||
@ -410,6 +411,65 @@ func Inline(val zapcore.ObjectMarshaler) Field {
|
||||
}
|
||||
}
|
||||
|
||||
// Dict constructs a field containing the provided key-value pairs.
|
||||
// It acts similar to [Object], but with the fields specified as arguments.
|
||||
func Dict(key string, val ...Field) Field {
|
||||
return dictField(key, val)
|
||||
}
|
||||
|
||||
// We need a function with the signature (string, T) for zap.Any.
|
||||
func dictField(key string, val []Field) Field {
|
||||
return Object(key, dictObject(val))
|
||||
}
|
||||
|
||||
type dictObject []Field
|
||||
|
||||
func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||
for _, f := range d {
|
||||
f.AddTo(enc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// We discovered an issue where zap.Any can cause a performance degradation
|
||||
// when used in new goroutines.
|
||||
//
|
||||
// This happens because the compiler assigns 4.8kb (one zap.Field per arm of
|
||||
// switch statement) of stack space for zap.Any when it takes the form:
|
||||
//
|
||||
// switch v := v.(type) {
|
||||
// case string:
|
||||
// return String(key, v)
|
||||
// case int:
|
||||
// return Int(key, v)
|
||||
// // ...
|
||||
// default:
|
||||
// return Reflect(key, v)
|
||||
// }
|
||||
//
|
||||
// To avoid this, we use the type switch to assign a value to a single local variable
|
||||
// and then call a function on it.
|
||||
// The local variable is just a function reference so it doesn't allocate
|
||||
// when converted to an interface{}.
|
||||
//
|
||||
// A fair bit of experimentation went into this.
|
||||
// See also:
|
||||
//
|
||||
// - https://github.com/uber-go/zap/pull/1301
|
||||
// - https://github.com/uber-go/zap/pull/1303
|
||||
// - https://github.com/uber-go/zap/pull/1304
|
||||
// - https://github.com/uber-go/zap/pull/1305
|
||||
// - https://github.com/uber-go/zap/pull/1308
|
||||
//
|
||||
// See https://github.com/golang/go/issues/62077 for upstream issue.
|
||||
type anyFieldC[T any] func(string, T) Field
|
||||
|
||||
func (f anyFieldC[T]) Any(key string, val any) Field {
|
||||
v, _ := val.(T)
|
||||
// val is guaranteed to be a T, except when it's nil.
|
||||
return f(key, v)
|
||||
}
|
||||
|
||||
// Any takes a key and an arbitrary value and chooses the best way to represent
|
||||
// them as a field, falling back to a reflection-based approach only if
|
||||
// necessary.
|
||||
@ -418,132 +478,138 @@ func Inline(val zapcore.ObjectMarshaler) Field {
|
||||
// them. To minimize surprises, []byte values are treated as binary blobs, byte
|
||||
// values are treated as uint8, and runes are always treated as integers.
|
||||
func Any(key string, value interface{}) Field {
|
||||
switch val := value.(type) {
|
||||
var c interface{ Any(string, any) Field }
|
||||
|
||||
switch value.(type) {
|
||||
case zapcore.ObjectMarshaler:
|
||||
return Object(key, val)
|
||||
c = anyFieldC[zapcore.ObjectMarshaler](Object)
|
||||
case zapcore.ArrayMarshaler:
|
||||
return Array(key, val)
|
||||
c = anyFieldC[zapcore.ArrayMarshaler](Array)
|
||||
case []Field:
|
||||
c = anyFieldC[[]Field](dictField)
|
||||
case bool:
|
||||
return Bool(key, val)
|
||||
c = anyFieldC[bool](Bool)
|
||||
case *bool:
|
||||
return Boolp(key, val)
|
||||
c = anyFieldC[*bool](Boolp)
|
||||
case []bool:
|
||||
return Bools(key, val)
|
||||
c = anyFieldC[[]bool](Bools)
|
||||
case complex128:
|
||||
return Complex128(key, val)
|
||||
c = anyFieldC[complex128](Complex128)
|
||||
case *complex128:
|
||||
return Complex128p(key, val)
|
||||
c = anyFieldC[*complex128](Complex128p)
|
||||
case []complex128:
|
||||
return Complex128s(key, val)
|
||||
c = anyFieldC[[]complex128](Complex128s)
|
||||
case complex64:
|
||||
return Complex64(key, val)
|
||||
c = anyFieldC[complex64](Complex64)
|
||||
case *complex64:
|
||||
return Complex64p(key, val)
|
||||
c = anyFieldC[*complex64](Complex64p)
|
||||
case []complex64:
|
||||
return Complex64s(key, val)
|
||||
c = anyFieldC[[]complex64](Complex64s)
|
||||
case float64:
|
||||
return Float64(key, val)
|
||||
c = anyFieldC[float64](Float64)
|
||||
case *float64:
|
||||
return Float64p(key, val)
|
||||
c = anyFieldC[*float64](Float64p)
|
||||
case []float64:
|
||||
return Float64s(key, val)
|
||||
c = anyFieldC[[]float64](Float64s)
|
||||
case float32:
|
||||
return Float32(key, val)
|
||||
c = anyFieldC[float32](Float32)
|
||||
case *float32:
|
||||
return Float32p(key, val)
|
||||
c = anyFieldC[*float32](Float32p)
|
||||
case []float32:
|
||||
return Float32s(key, val)
|
||||
c = anyFieldC[[]float32](Float32s)
|
||||
case int:
|
||||
return Int(key, val)
|
||||
c = anyFieldC[int](Int)
|
||||
case *int:
|
||||
return Intp(key, val)
|
||||
c = anyFieldC[*int](Intp)
|
||||
case []int:
|
||||
return Ints(key, val)
|
||||
c = anyFieldC[[]int](Ints)
|
||||
case int64:
|
||||
return Int64(key, val)
|
||||
c = anyFieldC[int64](Int64)
|
||||
case *int64:
|
||||
return Int64p(key, val)
|
||||
c = anyFieldC[*int64](Int64p)
|
||||
case []int64:
|
||||
return Int64s(key, val)
|
||||
c = anyFieldC[[]int64](Int64s)
|
||||
case int32:
|
||||
return Int32(key, val)
|
||||
c = anyFieldC[int32](Int32)
|
||||
case *int32:
|
||||
return Int32p(key, val)
|
||||
c = anyFieldC[*int32](Int32p)
|
||||
case []int32:
|
||||
return Int32s(key, val)
|
||||
c = anyFieldC[[]int32](Int32s)
|
||||
case int16:
|
||||
return Int16(key, val)
|
||||
c = anyFieldC[int16](Int16)
|
||||
case *int16:
|
||||
return Int16p(key, val)
|
||||
c = anyFieldC[*int16](Int16p)
|
||||
case []int16:
|
||||
return Int16s(key, val)
|
||||
c = anyFieldC[[]int16](Int16s)
|
||||
case int8:
|
||||
return Int8(key, val)
|
||||
c = anyFieldC[int8](Int8)
|
||||
case *int8:
|
||||
return Int8p(key, val)
|
||||
c = anyFieldC[*int8](Int8p)
|
||||
case []int8:
|
||||
return Int8s(key, val)
|
||||
c = anyFieldC[[]int8](Int8s)
|
||||
case string:
|
||||
return String(key, val)
|
||||
c = anyFieldC[string](String)
|
||||
case *string:
|
||||
return Stringp(key, val)
|
||||
c = anyFieldC[*string](Stringp)
|
||||
case []string:
|
||||
return Strings(key, val)
|
||||
c = anyFieldC[[]string](Strings)
|
||||
case uint:
|
||||
return Uint(key, val)
|
||||
c = anyFieldC[uint](Uint)
|
||||
case *uint:
|
||||
return Uintp(key, val)
|
||||
c = anyFieldC[*uint](Uintp)
|
||||
case []uint:
|
||||
return Uints(key, val)
|
||||
c = anyFieldC[[]uint](Uints)
|
||||
case uint64:
|
||||
return Uint64(key, val)
|
||||
c = anyFieldC[uint64](Uint64)
|
||||
case *uint64:
|
||||
return Uint64p(key, val)
|
||||
c = anyFieldC[*uint64](Uint64p)
|
||||
case []uint64:
|
||||
return Uint64s(key, val)
|
||||
c = anyFieldC[[]uint64](Uint64s)
|
||||
case uint32:
|
||||
return Uint32(key, val)
|
||||
c = anyFieldC[uint32](Uint32)
|
||||
case *uint32:
|
||||
return Uint32p(key, val)
|
||||
c = anyFieldC[*uint32](Uint32p)
|
||||
case []uint32:
|
||||
return Uint32s(key, val)
|
||||
c = anyFieldC[[]uint32](Uint32s)
|
||||
case uint16:
|
||||
return Uint16(key, val)
|
||||
c = anyFieldC[uint16](Uint16)
|
||||
case *uint16:
|
||||
return Uint16p(key, val)
|
||||
c = anyFieldC[*uint16](Uint16p)
|
||||
case []uint16:
|
||||
return Uint16s(key, val)
|
||||
c = anyFieldC[[]uint16](Uint16s)
|
||||
case uint8:
|
||||
return Uint8(key, val)
|
||||
c = anyFieldC[uint8](Uint8)
|
||||
case *uint8:
|
||||
return Uint8p(key, val)
|
||||
c = anyFieldC[*uint8](Uint8p)
|
||||
case []byte:
|
||||
return Binary(key, val)
|
||||
c = anyFieldC[[]byte](Binary)
|
||||
case uintptr:
|
||||
return Uintptr(key, val)
|
||||
c = anyFieldC[uintptr](Uintptr)
|
||||
case *uintptr:
|
||||
return Uintptrp(key, val)
|
||||
c = anyFieldC[*uintptr](Uintptrp)
|
||||
case []uintptr:
|
||||
return Uintptrs(key, val)
|
||||
c = anyFieldC[[]uintptr](Uintptrs)
|
||||
case time.Time:
|
||||
return Time(key, val)
|
||||
c = anyFieldC[time.Time](Time)
|
||||
case *time.Time:
|
||||
return Timep(key, val)
|
||||
c = anyFieldC[*time.Time](Timep)
|
||||
case []time.Time:
|
||||
return Times(key, val)
|
||||
c = anyFieldC[[]time.Time](Times)
|
||||
case time.Duration:
|
||||
return Duration(key, val)
|
||||
c = anyFieldC[time.Duration](Duration)
|
||||
case *time.Duration:
|
||||
return Durationp(key, val)
|
||||
c = anyFieldC[*time.Duration](Durationp)
|
||||
case []time.Duration:
|
||||
return Durations(key, val)
|
||||
c = anyFieldC[[]time.Duration](Durations)
|
||||
case error:
|
||||
return NamedError(key, val)
|
||||
c = anyFieldC[error](NamedError)
|
||||
case []error:
|
||||
return Errors(key, val)
|
||||
c = anyFieldC[[]error](Errors)
|
||||
case fmt.Stringer:
|
||||
return Stringer(key, val)
|
||||
c = anyFieldC[fmt.Stringer](Stringer)
|
||||
default:
|
||||
return Reflect(key, val)
|
||||
c = anyFieldC[any](Reflect)
|
||||
}
|
||||
|
||||
return c.Any(key, value)
|
||||
}
|
||||
|
44
vendor/go.uber.org/zap/http_handler.go
generated
vendored
44
vendor/go.uber.org/zap/http_handler.go
generated
vendored
@ -22,6 +22,7 @@ package zap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -32,22 +33,23 @@ import (
|
||||
// ServeHTTP is a simple JSON endpoint that can report on or change the current
|
||||
// logging level.
|
||||
//
|
||||
// GET
|
||||
// # GET
|
||||
//
|
||||
// The GET request returns a JSON description of the current logging level like:
|
||||
// {"level":"info"}
|
||||
//
|
||||
// PUT
|
||||
// {"level":"info"}
|
||||
//
|
||||
// # PUT
|
||||
//
|
||||
// The PUT request changes the logging level. It is perfectly safe to change the
|
||||
// logging level while a program is running. Two content types are supported:
|
||||
//
|
||||
// Content-Type: application/x-www-form-urlencoded
|
||||
// Content-Type: application/x-www-form-urlencoded
|
||||
//
|
||||
// With this content type, the level can be provided through the request body or
|
||||
// a query parameter. The log level is URL encoded like:
|
||||
//
|
||||
// level=debug
|
||||
// level=debug
|
||||
//
|
||||
// The request body takes precedence over the query parameter, if both are
|
||||
// specified.
|
||||
@ -55,19 +57,25 @@ import (
|
||||
// This content type is the default for a curl PUT request. Following are two
|
||||
// example curl requests that both set the logging level to debug.
|
||||
//
|
||||
// curl -X PUT localhost:8080/log/level?level=debug
|
||||
// curl -X PUT localhost:8080/log/level -d level=debug
|
||||
// curl -X PUT localhost:8080/log/level?level=debug
|
||||
// curl -X PUT localhost:8080/log/level -d level=debug
|
||||
//
|
||||
// For any other content type, the payload is expected to be JSON encoded and
|
||||
// look like:
|
||||
//
|
||||
// {"level":"info"}
|
||||
// {"level":"info"}
|
||||
//
|
||||
// An example curl request could look like this:
|
||||
//
|
||||
// curl -X PUT localhost:8080/log/level -H "Content-Type: application/json" -d '{"level":"debug"}'
|
||||
//
|
||||
// curl -X PUT localhost:8080/log/level -H "Content-Type: application/json" -d '{"level":"debug"}'
|
||||
func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if err := lvl.serveHTTP(w, r); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "internal error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (lvl AtomicLevel) serveHTTP(w http.ResponseWriter, r *http.Request) error {
|
||||
type errorResponse struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
@ -79,19 +87,20 @@ func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
enc.Encode(payload{Level: lvl.Level()})
|
||||
return enc.Encode(payload{Level: lvl.Level()})
|
||||
|
||||
case http.MethodPut:
|
||||
requestedLvl, err := decodePutRequest(r.Header.Get("Content-Type"), r)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
enc.Encode(errorResponse{Error: err.Error()})
|
||||
return
|
||||
return enc.Encode(errorResponse{Error: err.Error()})
|
||||
}
|
||||
lvl.SetLevel(requestedLvl)
|
||||
enc.Encode(payload{Level: lvl.Level()})
|
||||
return enc.Encode(payload{Level: lvl.Level()})
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
enc.Encode(errorResponse{
|
||||
return enc.Encode(errorResponse{
|
||||
Error: "Only GET and PUT are supported.",
|
||||
})
|
||||
}
|
||||
@ -108,7 +117,7 @@ func decodePutRequest(contentType string, r *http.Request) (zapcore.Level, error
|
||||
func decodePutURL(r *http.Request) (zapcore.Level, error) {
|
||||
lvl := r.FormValue("level")
|
||||
if lvl == "" {
|
||||
return 0, fmt.Errorf("must specify logging level")
|
||||
return 0, errors.New("must specify logging level")
|
||||
}
|
||||
var l zapcore.Level
|
||||
if err := l.UnmarshalText([]byte(lvl)); err != nil {
|
||||
@ -125,8 +134,7 @@ func decodePutJSON(body io.Reader) (zapcore.Level, error) {
|
||||
return 0, fmt.Errorf("malformed request body: %v", err)
|
||||
}
|
||||
if pld.Level == nil {
|
||||
return 0, fmt.Errorf("must specify logging level")
|
||||
return 0, errors.New("must specify logging level")
|
||||
}
|
||||
return *pld.Level, nil
|
||||
|
||||
}
|
||||
|
22
vendor/go.uber.org/zap/internal/exit/exit.go
generated
vendored
22
vendor/go.uber.org/zap/internal/exit/exit.go
generated
vendored
@ -24,24 +24,25 @@ package exit
|
||||
|
||||
import "os"
|
||||
|
||||
var real = func() { os.Exit(1) }
|
||||
var _exit = os.Exit
|
||||
|
||||
// Exit normally terminates the process by calling os.Exit(1). If the package
|
||||
// is stubbed, it instead records a call in the testing spy.
|
||||
func Exit() {
|
||||
real()
|
||||
// With terminates the process by calling os.Exit(code). If the package is
|
||||
// stubbed, it instead records a call in the testing spy.
|
||||
func With(code int) {
|
||||
_exit(code)
|
||||
}
|
||||
|
||||
// A StubbedExit is a testing fake for os.Exit.
|
||||
type StubbedExit struct {
|
||||
Exited bool
|
||||
prev func()
|
||||
Code int
|
||||
prev func(code int)
|
||||
}
|
||||
|
||||
// Stub substitutes a fake for the call to os.Exit(1).
|
||||
func Stub() *StubbedExit {
|
||||
s := &StubbedExit{prev: real}
|
||||
real = s.exit
|
||||
s := &StubbedExit{prev: _exit}
|
||||
_exit = s.exit
|
||||
return s
|
||||
}
|
||||
|
||||
@ -56,9 +57,10 @@ func WithStub(f func()) *StubbedExit {
|
||||
|
||||
// Unstub restores the previous exit function.
|
||||
func (se *StubbedExit) Unstub() {
|
||||
real = se.prev
|
||||
_exit = se.prev
|
||||
}
|
||||
|
||||
func (se *StubbedExit) exit() {
|
||||
func (se *StubbedExit) exit(code int) {
|
||||
se.Exited = true
|
||||
se.Code = code
|
||||
}
|
||||
|
22
vendor/go.uber.org/atomic/value.go → vendor/go.uber.org/zap/internal/level_enabler.go
generated
vendored
22
vendor/go.uber.org/atomic/value.go → vendor/go.uber.org/zap/internal/level_enabler.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
// Copyright (c) 2022 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@ -18,14 +18,20 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
// Package internal and its subpackages hold types and functionality
|
||||
// that are not part of Zap's public API.
|
||||
package internal
|
||||
|
||||
import "sync/atomic"
|
||||
import "go.uber.org/zap/zapcore"
|
||||
|
||||
// Value shadows the type of the same name from sync/atomic
|
||||
// https://godoc.org/sync/atomic#Value
|
||||
type Value struct {
|
||||
atomic.Value
|
||||
// LeveledEnabler is an interface satisfied by LevelEnablers that are able to
|
||||
// report their own level.
|
||||
//
|
||||
// This interface is defined to use more conveniently in tests and non-zapcore
|
||||
// packages.
|
||||
// This cannot be imported from zapcore because of the cyclic dependency.
|
||||
type LeveledEnabler interface {
|
||||
zapcore.LevelEnabler
|
||||
|
||||
_ nocmp // disallow non-atomic comparison
|
||||
Level() zapcore.Level
|
||||
}
|
53
vendor/go.uber.org/atomic/bool_ext.go → vendor/go.uber.org/zap/internal/pool/pool.go
generated
vendored
53
vendor/go.uber.org/atomic/bool_ext.go → vendor/go.uber.org/zap/internal/pool/pool.go
generated
vendored
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020 Uber Technologies, Inc.
|
||||
// Copyright (c) 2023 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@ -18,36 +18,41 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package atomic
|
||||
// Package pool provides internal pool utilities.
|
||||
package pool
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//go:generate bin/gen-atomicwrapper -name=Bool -type=bool -wrapped=Uint32 -pack=boolToInt -unpack=truthy -cas -swap -json -file=bool.go
|
||||
|
||||
func truthy(n uint32) bool {
|
||||
return n == 1
|
||||
// A Pool is a generic wrapper around [sync.Pool] to provide strongly-typed
|
||||
// object pooling.
|
||||
//
|
||||
// Note that SA6002 (ref: https://staticcheck.io/docs/checks/#SA6002) will
|
||||
// not be detected, so all internal pool use must take care to only store
|
||||
// pointer types.
|
||||
type Pool[T any] struct {
|
||||
pool sync.Pool
|
||||
}
|
||||
|
||||
func boolToInt(b bool) uint32 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Toggle atomically negates the Boolean and returns the previous value.
|
||||
func (b *Bool) Toggle() bool {
|
||||
for {
|
||||
old := b.Load()
|
||||
if b.CAS(old, !old) {
|
||||
return old
|
||||
}
|
||||
// New returns a new [Pool] for T, and will use fn to construct new Ts when
|
||||
// the pool is empty.
|
||||
func New[T any](fn func() T) *Pool[T] {
|
||||
return &Pool[T]{
|
||||
pool: sync.Pool{
|
||||
New: func() any {
|
||||
return fn()
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (b *Bool) String() string {
|
||||
return strconv.FormatBool(b.Load())
|
||||
// Get gets a T from the pool, or creates a new one if the pool is empty.
|
||||
func (p *Pool[T]) Get() T {
|
||||
return p.pool.Get().(T)
|
||||
}
|
||||
|
||||
// Put returns x into the pool.
|
||||
func (p *Pool[T]) Put(x T) {
|
||||
p.pool.Put(x)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||
// Copyright (c) 2023 Uber Technologies, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@ -18,25 +18,26 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package zap
|
||||
// Package stacktrace provides support for gathering stack traces
|
||||
// efficiently.
|
||||
package stacktrace
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap/buffer"
|
||||
"go.uber.org/zap/internal/bufferpool"
|
||||
"go.uber.org/zap/internal/pool"
|
||||
)
|
||||
|
||||
var _stacktracePool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &stacktrace{
|
||||
storage: make([]uintptr, 64),
|
||||
}
|
||||
},
|
||||
}
|
||||
var _stackPool = pool.New(func() *Stack {
|
||||
return &Stack{
|
||||
storage: make([]uintptr, 64),
|
||||
}
|
||||
})
|
||||
|
||||
type stacktrace struct {
|
||||
// Stack is a captured stack trace.
|
||||
type Stack struct {
|
||||
pcs []uintptr // program counters; always a subslice of storage
|
||||
frames *runtime.Frames
|
||||
|
||||
@ -50,30 +51,30 @@ type stacktrace struct {
|
||||
storage []uintptr
|
||||
}
|
||||
|
||||
// stacktraceDepth specifies how deep of a stack trace should be captured.
|
||||
type stacktraceDepth int
|
||||
// Depth specifies how deep of a stack trace should be captured.
|
||||
type Depth int
|
||||
|
||||
const (
|
||||
// stacktraceFirst captures only the first frame.
|
||||
stacktraceFirst stacktraceDepth = iota
|
||||
// First captures only the first frame.
|
||||
First Depth = iota
|
||||
|
||||
// stacktraceFull captures the entire call stack, allocating more
|
||||
// Full captures the entire call stack, allocating more
|
||||
// storage for it if needed.
|
||||
stacktraceFull
|
||||
Full
|
||||
)
|
||||
|
||||
// captureStacktrace captures a stack trace of the specified depth, skipping
|
||||
// Capture captures a stack trace of the specified depth, skipping
|
||||
// the provided number of frames. skip=0 identifies the caller of
|
||||
// captureStacktrace.
|
||||
// Capture.
|
||||
//
|
||||
// The caller must call Free on the returned stacktrace after using it.
|
||||
func captureStacktrace(skip int, depth stacktraceDepth) *stacktrace {
|
||||
stack := _stacktracePool.Get().(*stacktrace)
|
||||
func Capture(skip int, depth Depth) *Stack {
|
||||
stack := _stackPool.Get()
|
||||
|
||||
switch depth {
|
||||
case stacktraceFirst:
|
||||
case First:
|
||||
stack.pcs = stack.storage[:1]
|
||||
case stacktraceFull:
|
||||
case Full:
|
||||
stack.pcs = stack.storage
|
||||
}
|
||||
|
||||
@ -87,7 +88,7 @@ func captureStacktrace(skip int, depth stacktraceDepth) *stacktrace {
|
||||
// runtime.Callers truncates the recorded stacktrace if there is no
|
||||
// room in the provided slice. For the full stack trace, keep expanding
|
||||
// storage until there are fewer frames than there is room.
|
||||
if depth == stacktraceFull {
|
||||
if depth == Full {
|
||||
pcs := stack.pcs
|
||||
for numFrames == len(pcs) {
|
||||
pcs = make([]uintptr, len(pcs)*2)
|
||||
@ -109,52 +110,56 @@ func captureStacktrace(skip int, depth stacktraceDepth) *stacktrace {
|
||||
|
||||
// Free releases resources associated with this stacktrace
|
||||
// and returns it back to the pool.
|
||||
func (st *stacktrace) Free() {
|
||||
func (st *Stack) Free() {
|
||||
st.frames = nil
|
||||
st.pcs = nil
|
||||
_stacktracePool.Put(st)
|
||||
_stackPool.Put(st)
|
||||
}
|
||||
|
||||
// Count reports the total number of frames in this stacktrace.
|
||||
// Count DOES NOT change as Next is called.
|
||||
func (st *stacktrace) Count() int {
|
||||
func (st *Stack) Count() int {
|
||||
return len(st.pcs)
|
||||
}
|
||||
|
||||
// Next returns the next frame in the stack trace,
|
||||
// and a boolean indicating whether there are more after it.
|
||||
func (st *stacktrace) Next() (_ runtime.Frame, more bool) {
|
||||
func (st *Stack) Next() (_ runtime.Frame, more bool) {
|
||||
return st.frames.Next()
|
||||
}
|
||||
|
||||
func takeStacktrace(skip int) string {
|
||||
stack := captureStacktrace(skip+1, stacktraceFull)
|
||||
// Take returns a string representation of the current stacktrace.
|
||||
//
|
||||
// skip is the number of frames to skip before recording the stack trace.
|
||||
// skip=0 identifies the caller of Take.
|
||||
func Take(skip int) string {
|
||||
stack := Capture(skip+1, Full)
|
||||
defer stack.Free()
|
||||
|
||||
buffer := bufferpool.Get()
|
||||
defer buffer.Free()
|
||||
|
||||
stackfmt := newStackFormatter(buffer)
|
||||
stackfmt := NewFormatter(buffer)
|
||||
stackfmt.FormatStack(stack)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// stackFormatter formats a stack trace into a readable string representation.
|
||||
type stackFormatter struct {
|
||||
// Formatter formats a stack trace into a readable string representation.
|
||||
type Formatter struct {
|
||||
b *buffer.Buffer
|
||||
nonEmpty bool // whehther we've written at least one frame already
|
||||
}
|
||||
|
||||
// newStackFormatter builds a new stackFormatter.
|
||||
func newStackFormatter(b *buffer.Buffer) stackFormatter {
|
||||
return stackFormatter{b: b}
|
||||
// NewFormatter builds a new Formatter.
|
||||
func NewFormatter(b *buffer.Buffer) Formatter {
|
||||
return Formatter{b: b}
|
||||
}
|
||||
|
||||
// FormatStack formats all remaining frames in the provided stacktrace -- minus
|
||||
// the final runtime.main/runtime.goexit frame.
|
||||
func (sf *stackFormatter) FormatStack(stack *stacktrace) {
|
||||
func (sf *Formatter) FormatStack(stack *Stack) {
|
||||
// Note: On the last iteration, frames.Next() returns false, with a valid
|
||||
// frame, but we ignore this frame. The last frame is a a runtime frame which
|
||||
// frame, but we ignore this frame. The last frame is a runtime frame which
|
||||
// adds noise, since it's only either runtime.main or runtime.goexit.
|
||||
for frame, more := stack.Next(); more; frame, more = stack.Next() {
|
||||
sf.FormatFrame(frame)
|
||||
@ -162,7 +167,7 @@ func (sf *stackFormatter) FormatStack(stack *stacktrace) {
|
||||
}
|
||||
|
||||
// FormatFrame formats the given frame.
|
||||
func (sf *stackFormatter) FormatFrame(frame runtime.Frame) {
|
||||
func (sf *Formatter) FormatFrame(frame runtime.Frame) {
|
||||
if sf.nonEmpty {
|
||||
sf.b.AppendByte('\n')
|
||||
}
|
12
vendor/go.uber.org/zap/level.go
generated
vendored
12
vendor/go.uber.org/zap/level.go
generated
vendored
@ -21,7 +21,9 @@
|
||||
package zap
|
||||
|
||||
import (
|
||||
"go.uber.org/atomic"
|
||||
"sync/atomic"
|
||||
|
||||
"go.uber.org/zap/internal"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
@ -70,12 +72,14 @@ type AtomicLevel struct {
|
||||
l *atomic.Int32
|
||||
}
|
||||
|
||||
var _ internal.LeveledEnabler = AtomicLevel{}
|
||||
|
||||
// NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging
|
||||
// enabled.
|
||||
func NewAtomicLevel() AtomicLevel {
|
||||
return AtomicLevel{
|
||||
l: atomic.NewInt32(int32(InfoLevel)),
|
||||
}
|
||||
lvl := AtomicLevel{l: new(atomic.Int32)}
|
||||
lvl.l.Store(int32(InfoLevel))
|
||||
return lvl
|
||||
}
|
||||
|
||||
// NewAtomicLevelAt is a convenience function that creates an AtomicLevel
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user