mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
Upgrading server dependancies (#8154)
This commit is contained in:
committed by
GitHub
parent
8d66523ba7
commit
961c04cae9
@@ -21,6 +21,9 @@ import (
|
||||
_ "github.com/go-ldap/ldap"
|
||||
_ "github.com/hashicorp/memberlist"
|
||||
_ "github.com/mattermost/rsc/qr"
|
||||
_ "github.com/prometheus/client_golang/prometheus"
|
||||
_ "github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
_ "github.com/tylerb/graceful"
|
||||
_ "gopkg.in/olivere/elastic.v5"
|
||||
)
|
||||
|
||||
|
||||
111
glide.lock
generated
111
glide.lock
generated
@@ -1,23 +1,19 @@
|
||||
hash: fa27dd8f4fd1b15c505a3d6f2023662471391e4a3ea22e67c829376c7e6e90c8
|
||||
updated: 2018-01-05T10:08:00.418197689+01:00
|
||||
hash: 5e8ab6acb5c3bb7dbedfc6e837cc529e72affea22384ec59e82f0e3c13379b6f
|
||||
updated: 2018-01-25T15:20:13.899302483-08:00
|
||||
imports:
|
||||
- name: github.com/alecthomas/log4go
|
||||
version: 3fbce08846379ec7f4f6bc7fce6dd01ce28fae4c
|
||||
repo: https://github.com/mattermost/log4go.git
|
||||
- name: github.com/armon/go-metrics
|
||||
version: 7aa49fde808223f8dadfdbfd3a20ff6c19e5f9ec
|
||||
- name: github.com/avct/uasurfer
|
||||
version: 7b6f7205267b5b81d20da3f09ec3dea3b3d90cf8
|
||||
- name: github.com/beorn7/perks
|
||||
version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
|
||||
subpackages:
|
||||
- quantile
|
||||
- name: github.com/corpix/uarand
|
||||
version: 2b8494104d86337cdd41d0a49cbed8e4583c0ab4
|
||||
- name: github.com/cpanato/html2text
|
||||
version: d47a5532a7bc36ad7b2b8ec3eebe24e975154f94
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
|
||||
version: ecdeabc65495df2dec95d7c4a4c3e021903035e5
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/dgryski/dgoogauth
|
||||
@@ -25,19 +21,19 @@ imports:
|
||||
- name: github.com/dimchansky/utfbom
|
||||
version: 6c6132ff69f0f6c088739067407b5d32c52e1d0f
|
||||
- name: github.com/disintegration/imaging
|
||||
version: dd50a3ee9985ccd313a2f03c398fcaedc96dc707
|
||||
version: 1884593a19ddc6f2ea050403430d02c1d0fc1283
|
||||
- name: github.com/dyatlov/go-opengraph
|
||||
version: 41a3523719dfbe7e8f853fbd4061867543db5270
|
||||
subpackages:
|
||||
- opengraph
|
||||
- name: github.com/fsnotify/fsnotify
|
||||
version: 629574ca2a5df945712d3079857300b5e4da0236
|
||||
version: c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9
|
||||
- name: github.com/go-ini/ini
|
||||
version: 32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a
|
||||
- name: github.com/go-ldap/ldap
|
||||
version: 8168ee085ee43257585e50c6441aadf54ecb2c9f
|
||||
version: bb7a9ca6e4fbc2129e3db588a34bc970ffe811a9
|
||||
- name: github.com/go-redis/redis
|
||||
version: d0f86971b5d61de9cebd2616b932acb7ba14d957
|
||||
version: 4021ace05686f632ff17fd824bbed229fc474cf8
|
||||
subpackages:
|
||||
- internal
|
||||
- internal/consistenthash
|
||||
@@ -45,14 +41,14 @@ imports:
|
||||
- internal/pool
|
||||
- internal/proto
|
||||
- name: github.com/go-sql-driver/mysql
|
||||
version: a0583e0143b1624142adab07e0e97fe106d99561
|
||||
version: bc14601d1bd56421dd60f561e6052c9ed77f9daf
|
||||
- name: github.com/golang/freetype
|
||||
version: e2365dfdc4a05e4b8299a783240d4a7d5a65d4e4
|
||||
subpackages:
|
||||
- raster
|
||||
- truetype
|
||||
- name: github.com/golang/protobuf
|
||||
version: 1e59b77b52bf8e4b449a57e6f79f21226d571845
|
||||
version: 925541529c1fa6821df4e44ce2723319eb2be768
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/gorilla/context
|
||||
@@ -60,13 +56,13 @@ imports:
|
||||
- name: github.com/gorilla/handlers
|
||||
version: 90663712d74cb411cbef281bc1e08c19d1a76145
|
||||
- name: github.com/gorilla/mux
|
||||
version: 7f08801859139f86dfafd1c296e2cba9a80d292e
|
||||
version: 53c1911da2b537f792e7cafcb446b05ffe33b996
|
||||
- name: github.com/gorilla/websocket
|
||||
version: ea4d1f681babbce9545c9c5f3d5194a789c89f5b
|
||||
version: 91f589db023d66e4aba7112d44cc0d2fb091c553
|
||||
- name: github.com/hashicorp/errwrap
|
||||
version: 7554cd9344cec97297fa6649b055a8c98c2a1e55
|
||||
- name: github.com/hashicorp/go-immutable-radix
|
||||
version: 8aac2701530899b64bdea735a1de8da899815220
|
||||
version: 59b67882ec612f43b9d4c4fd97cebd507be4b3ee
|
||||
- name: github.com/hashicorp/go-msgpack
|
||||
version: fa3f63826f7c23912c15263591e65d54d080b458
|
||||
subpackages:
|
||||
@@ -92,23 +88,25 @@ imports:
|
||||
- json/scanner
|
||||
- json/token
|
||||
- name: github.com/hashicorp/memberlist
|
||||
<<<<<<< HEAD
|
||||
version: caa5d20d6a642b7543b3745e54031a96008bee57
|
||||
version: 3d8438da9589e7b608a83ffac1ef8211486bcb7c
|
||||
- name: github.com/icrowley/fake
|
||||
version: e64cc2cf92049a299f359734c6ea76073f2a8b2c
|
||||
=======
|
||||
version: 3d8438da9589e7b608a83ffac1ef8211486bcb7c
|
||||
>>>>>>> Updated dependencies and added avct/uasurfer
|
||||
- name: github.com/inconshreveable/mousetrap
|
||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
- name: github.com/jehiah/go-strftime
|
||||
version: 834e15c05a45371503440cc195bbd05c9a0968d9
|
||||
- name: github.com/lib/pq
|
||||
version: 83612a56d3dd153a94a629cd64925371c9adad78
|
||||
version: 19c8e9ad00952ce0c64489b60e8df88bb16dd514
|
||||
subpackages:
|
||||
- oid
|
||||
- name: github.com/magiconair/properties
|
||||
version: 49d762b9817ba1c2e9d0c69183c2b4a8b8f1d934
|
||||
- name: github.com/mailru/easyjson
|
||||
version: 32fa128f234d041f196a9f3e0fea5ac9772c08e1
|
||||
subpackages:
|
||||
- buffer
|
||||
- jlexer
|
||||
- jwriter
|
||||
- name: github.com/mattermost/gorp
|
||||
version: 995ddf2264c4ad45fbaf342f7500e4787ebae84a
|
||||
- name: github.com/mattermost/html2text
|
||||
@@ -120,15 +118,13 @@ imports:
|
||||
- qr
|
||||
- qr/coding
|
||||
- name: github.com/matttproud/golang_protobuf_extensions
|
||||
version: 3247c84500bff8d9fb6d579d800f20b3e091582c
|
||||
version: c12348ce28de40eed0136aa2b644d0ee0650e56c
|
||||
subpackages:
|
||||
- pbutil
|
||||
- name: github.com/miekg/dns
|
||||
version: f5ac34d755eb6975958108413c7802a782ca16c8
|
||||
- name: github.com/minio/go-homedir
|
||||
version: 4d76aabb80b22bad8695d3904e943f1fb5e6199f
|
||||
version: 5364553f1ee9cddc7ac8b62dce148309c386695b
|
||||
- name: github.com/minio/minio-go
|
||||
version: 4e0f567303d4cc90ceb055a451959fb9fc391fb9
|
||||
version: 14f1d472d115bac5ca4804094aa87484a72ced61
|
||||
subpackages:
|
||||
- pkg/credentials
|
||||
- pkg/encrypt
|
||||
@@ -136,8 +132,10 @@ imports:
|
||||
- pkg/s3signer
|
||||
- pkg/s3utils
|
||||
- pkg/set
|
||||
- name: github.com/mitchellh/go-homedir
|
||||
version: b8bc1bf767474819792c23f32d8286a45736f1c6
|
||||
- name: github.com/mitchellh/mapstructure
|
||||
version: 06020f85339e21b2478f756a78e295255ffa4d6a
|
||||
version: b4575eea38cca1123ec2dc90c26529b5c5acfcff
|
||||
- name: github.com/mssola/user_agent
|
||||
version: 5243daae23628aeae9b6268541406bd5e95d5964
|
||||
- name: github.com/nicksnyder/go-i18n
|
||||
@@ -148,21 +146,27 @@ imports:
|
||||
- i18n/language
|
||||
- i18n/translation
|
||||
- name: github.com/NYTimes/gziphandler
|
||||
version: 47ca22a0aeea4c9ceddfb935d818d636d934c312
|
||||
version: 289a3b81f5aedc99f8d6eb0f67827c142f1310d8
|
||||
- name: github.com/olivere/elastic
|
||||
version: c51e74f9bcab8906a2f6cf5660dac396ba51b3d6
|
||||
subpackages:
|
||||
- config
|
||||
- uritemplates
|
||||
- name: github.com/pborman/uuid
|
||||
version: e790cca94e6cc75c7064b1332e63811d4aae1a53
|
||||
- name: github.com/pelletier/go-toml
|
||||
version: 0131db6d737cfbbfb678f8b7d92e55e27ce46224
|
||||
version: acdc4509485b587f5e675510c4f2c63e90ff68a8
|
||||
- name: github.com/pkg/errors
|
||||
version: e881fd58d78e04cf6d0de1217f8707c8cc2249bc
|
||||
version: 645ef00459ed84a119197bfb8d8205042c6df63d
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
|
||||
version: 792786c7400a136282c1664665ae0a8db921c6c2
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/prometheus/client_golang
|
||||
version: c5b7fccd204277076155f10851dad72b76a49317
|
||||
version: 06bc6e01f4baf4ee783ffcd23abfcb0b0f9dfada
|
||||
subpackages:
|
||||
- prometheus
|
||||
- prometheus/promhttp
|
||||
- name: github.com/prometheus/client_model
|
||||
version: 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
|
||||
subpackages:
|
||||
@@ -171,8 +175,14 @@ imports:
|
||||
version: 89604d197083d4781071d3c65855d24ecfb0a563
|
||||
subpackages:
|
||||
- expfmt
|
||||
- internal/bitbucket.org/ww/goautoneg
|
||||
- model
|
||||
- name: github.com/prometheus/procfs
|
||||
version: b15cd069a83443be3154b719d0cc9fe8117f09fb
|
||||
version: cb4147076ac75738c9a7d279075a253c0cc5acbd
|
||||
subpackages:
|
||||
- internal/util
|
||||
- nfs
|
||||
- xfs
|
||||
- name: github.com/rsc/letsencrypt
|
||||
version: 33926faef6d434b854ea994228f11d0185faa0c1
|
||||
- name: github.com/rwcarlsen/goexif
|
||||
@@ -187,38 +197,38 @@ imports:
|
||||
- name: github.com/segmentio/backo-go
|
||||
version: 204274ad699c0983a70203a566887f17a717fef4
|
||||
- name: github.com/spf13/afero
|
||||
version: 57afd63c68602b63ed976de00dd066ccb3c319db
|
||||
version: bb8f1927f2a9d3ab41c9340aa034f6b803f4359c
|
||||
subpackages:
|
||||
- mem
|
||||
- name: github.com/spf13/cast
|
||||
version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4
|
||||
- name: github.com/spf13/cobra
|
||||
version: b95ab734e27d33e0d8fbabf71ca990568d4e2020
|
||||
version: f91529fc609202eededff4de2dc0ba2f662240a3
|
||||
- name: github.com/spf13/jwalterweatherman
|
||||
version: 7c0cea34c8ece3fbeb2b27ab9b59511d360fb394
|
||||
- name: github.com/spf13/pflag
|
||||
version: e57e3eeb33f795204c1ca35f56c44f83227c6e66
|
||||
version: 4c012f6dcd9546820e378d0bdda4d8fc772cdfea
|
||||
- name: github.com/spf13/viper
|
||||
version: aafc9e6bc7b7bb53ddaa75a5ef49a17d6e654be5
|
||||
- name: github.com/stretchr/objx
|
||||
version: cbeaeb16a013161a98496fad62933b1d21786672
|
||||
version: 477a77ecc69700c7cdeb1fa9e129548e1c1c393c
|
||||
- name: github.com/stretchr/testify
|
||||
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
|
||||
version: b91bfb9ebec76498946beb6af7c0230c7cc7ba6c
|
||||
subpackages:
|
||||
- assert
|
||||
- mock
|
||||
- require
|
||||
- suite
|
||||
- name: github.com/tylerb/graceful
|
||||
version: 4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb
|
||||
version: d72b0151351a13d0421b763b88f791469c4f5dc7
|
||||
- name: github.com/xenolf/lego
|
||||
version: b929aa5aab5ad2e197bb3d74ef99fac61bfa47bc
|
||||
version: 6bddbfd17a6e1ab782617eeab2f2007c6550b160
|
||||
subpackages:
|
||||
- acme
|
||||
- name: github.com/xtgo/uuid
|
||||
version: a0b114877d4caeffbd7f87e3757c17fce570fea7
|
||||
- name: golang.org/x/crypto
|
||||
version: b3c9a1d25cfbbbab0ff4780b71c4f54e6e92a0de
|
||||
version: 3d37316aaa6bd9929127ac9a527abf408178ea7b
|
||||
subpackages:
|
||||
- bcrypt
|
||||
- blowfish
|
||||
@@ -234,29 +244,37 @@ imports:
|
||||
- tiff
|
||||
- tiff/lzw
|
||||
- name: golang.org/x/net
|
||||
version: 434ec0c7fe3742c984919a691b2018a6e9694425
|
||||
version: 0ed95abb35c445290478a5348a7b38bb154135fd
|
||||
subpackages:
|
||||
- bpf
|
||||
- context
|
||||
- html
|
||||
- html/atom
|
||||
- idna
|
||||
- internal/iana
|
||||
- internal/socket
|
||||
- ipv4
|
||||
- ipv6
|
||||
- lex/httplex
|
||||
- name: golang.org/x/sys
|
||||
version: 810d7000345868fc619eb81f46307107118f4ae1
|
||||
version: 03467258950d845cd1877eab69461b98e8c09219
|
||||
subpackages:
|
||||
- unix
|
||||
- name: golang.org/x/text
|
||||
version: e19ae1496984b1c655b8044a65c0300a3c878dd3
|
||||
subpackages:
|
||||
- secure/bidirule
|
||||
- transform
|
||||
- unicode/bidi
|
||||
- unicode/norm
|
||||
- name: golang.org/x/time
|
||||
version: 6dc17368e09b0e8634d71cac8168d853e869a0c7
|
||||
subpackages:
|
||||
- rate
|
||||
- name: google.golang.org/appengine
|
||||
version: 5bee14b453b4c71be47ec1781b0fa61c2ea182db
|
||||
subpackages:
|
||||
- cloudsql
|
||||
- name: gopkg.in/alexcesaro/quotedprintable.v3
|
||||
version: 2caba252f4dc53eaf6b553000885530023f54623
|
||||
- name: gopkg.in/asn1-ber.v1
|
||||
@@ -264,10 +282,7 @@ imports:
|
||||
- name: gopkg.in/gomail.v2
|
||||
version: 41f3572897373c5538c50a2402db15db079fa4fd
|
||||
- name: gopkg.in/olivere/elastic.v5
|
||||
version: 171ce647da4acfb30ffc99981d66d80bdea6bcee
|
||||
subpackages:
|
||||
- config
|
||||
- uritemplates
|
||||
version: c51e74f9bcab8906a2f6cf5660dac396ba51b3d6
|
||||
- name: gopkg.in/square/go-jose.v1
|
||||
version: aa2e30fdd1fe9dd3394119af66451ae790d50e0d
|
||||
subpackages:
|
||||
|
||||
81
glide.yaml
81
glide.yaml
@@ -5,29 +5,35 @@ import:
|
||||
repo: https://github.com/mattermost/log4go.git
|
||||
- package: github.com/dgryski/dgoogauth
|
||||
- package: github.com/disintegration/imaging
|
||||
version: v1.2.4
|
||||
version: v1.3.0
|
||||
- package: github.com/dyatlov/go-opengraph
|
||||
subpackages:
|
||||
- opengraph
|
||||
- package: github.com/fsnotify/fsnotify
|
||||
version: v1.4.2
|
||||
version: v1.4.7
|
||||
- package: github.com/go-ldap/ldap
|
||||
version: v2.5.0
|
||||
version: v2.5.1
|
||||
- package: github.com/go-redis/redis
|
||||
version: v6.8.2
|
||||
- package: github.com/go-sql-driver/mysql
|
||||
version: v1.3
|
||||
- package: github.com/golang/freetype
|
||||
- package: github.com/gorilla/handlers
|
||||
version: v1.3.0
|
||||
- package: github.com/gorilla/mux
|
||||
version: v1.6.0
|
||||
version: v1.6.1
|
||||
- package: github.com/gorilla/websocket
|
||||
version: v1.2.0
|
||||
- package: github.com/hashicorp/memberlist
|
||||
- package: github.com/icrowley/fake
|
||||
- package: github.com/lib/pq
|
||||
- package: github.com/mattermost/gorp
|
||||
- package: github.com/mattermost/html2text
|
||||
- package: github.com/mattermost/rsc
|
||||
subpackages:
|
||||
- qr
|
||||
- package: github.com/minio/minio-go
|
||||
version: v3.0.3
|
||||
version: 4.0.6
|
||||
subpackages:
|
||||
- pkg/credentials
|
||||
- package: github.com/mssola/user_agent
|
||||
- package: github.com/nicksnyder/go-i18n
|
||||
version: v1.10.0
|
||||
@@ -35,6 +41,8 @@ import:
|
||||
- i18n
|
||||
- package: github.com/pborman/uuid
|
||||
version: v1.1
|
||||
- package: github.com/pkg/errors
|
||||
version: v0.8.0
|
||||
- package: github.com/rsc/letsencrypt
|
||||
version: v0.0.1
|
||||
- package: github.com/rwcarlsen/goexif
|
||||
@@ -43,56 +51,31 @@ import:
|
||||
- package: github.com/segmentio/analytics-go
|
||||
version: 2.1.1
|
||||
- package: github.com/spf13/cobra
|
||||
- package: github.com/spf13/pflag
|
||||
version: v1.0.0
|
||||
- package: github.com/spf13/viper
|
||||
- package: github.com/tylerb/graceful
|
||||
version: v1.2.15
|
||||
- package: github.com/stretchr/testify
|
||||
version: v1.2.0
|
||||
subpackages:
|
||||
- assert
|
||||
- mock
|
||||
- require
|
||||
- package: golang.org/x/crypto
|
||||
subpackages:
|
||||
- bcrypt
|
||||
- package: golang.org/x/image
|
||||
subpackages:
|
||||
- bmp
|
||||
- package: golang.org/x/net
|
||||
subpackages:
|
||||
- bpf
|
||||
- package: golang.org/x/sys
|
||||
subpackages:
|
||||
- unix
|
||||
- package: gopkg.in/gomail.v2
|
||||
version: 2.0.0
|
||||
- package: gopkg.in/olivere/elastic.v5
|
||||
version: v6.1.4
|
||||
- package: gopkg.in/throttled/throttled.v2
|
||||
version: v2.1.0
|
||||
subpackages:
|
||||
- store/memstore
|
||||
- package: github.com/prometheus/client_golang
|
||||
version: v0.8.0
|
||||
subpackages:
|
||||
- prometheus
|
||||
- package: github.com/beorn7/perks
|
||||
subpackages:
|
||||
- quantile
|
||||
- package: github.com/golang/protobuf
|
||||
subpackages:
|
||||
- proto
|
||||
- package: github.com/prometheus/client_model
|
||||
subpackages:
|
||||
- go
|
||||
- package: github.com/prometheus/common
|
||||
subpackages:
|
||||
- expfmt
|
||||
- package: github.com/matttproud/golang_protobuf_extensions
|
||||
version: v1.0.0
|
||||
subpackages:
|
||||
- pbutil
|
||||
- package: github.com/prometheus/procfs
|
||||
- package: github.com/cpanato/html2text
|
||||
- package: gopkg.in/olivere/elastic.v5
|
||||
version: v5.0.53
|
||||
- package: github.com/mattermost/gorp
|
||||
version: 995ddf2264c4ad45fbaf342f7500e4787ebae84a
|
||||
- package: github.com/go-redis/redis
|
||||
version: v6.7.3
|
||||
- package: github.com/stretchr/testify
|
||||
version: v1.1.4
|
||||
subpackages:
|
||||
- assert
|
||||
- require
|
||||
- package: gopkg.in/gomail.v2
|
||||
version: 2.0.0
|
||||
- package: github.com/mattermost/html2text
|
||||
- package: github.com/icrowley/fake
|
||||
- package: github.com/avct/uasurfer
|
||||
- package: gopkg.in/yaml.v2
|
||||
|
||||
@@ -83,7 +83,7 @@ func (b *S3FileBackend) ReadFile(path string) ([]byte, *model.AppError) {
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("ReadFile", "api.file.read_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
minioObject, err := s3Clnt.GetObject(b.bucket, path)
|
||||
minioObject, err := s3Clnt.GetObject(b.bucket, path, s3.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("ReadFile", "api.file.read_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -138,13 +138,18 @@ func (b *S3FileBackend) WriteFile(f []byte, path string) *model.AppError {
|
||||
return model.NewAppError("WriteFile", "api.file.write_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
ext := filepath.Ext(path)
|
||||
metaData := s3Metadata(b.encrypt, "binary/octet-stream")
|
||||
if model.IsFileExtImage(ext) {
|
||||
metaData = s3Metadata(b.encrypt, model.GetImageMimeType(ext))
|
||||
options := s3.PutObjectOptions{}
|
||||
if b.encrypt {
|
||||
options.UserMetadata["x-amz-server-side-encryption"] = "AES256"
|
||||
}
|
||||
|
||||
if _, err = s3Clnt.PutObjectWithMetadata(b.bucket, path, bytes.NewReader(f), metaData, nil); err != nil {
|
||||
if ext := filepath.Ext(path); model.IsFileExtImage(ext) {
|
||||
options.ContentType = model.GetImageMimeType(ext)
|
||||
} else {
|
||||
options.ContentType = "binary/octet-stream"
|
||||
}
|
||||
|
||||
if _, err = s3Clnt.PutObject(b.bucket, path, bytes.NewReader(f), -1, options); err != nil {
|
||||
return model.NewAppError("WriteFile", "api.file.write_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -225,17 +230,6 @@ func (b *S3FileBackend) RemoveDirectory(path string) *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func s3Metadata(encrypt bool, contentType string) map[string][]string {
|
||||
metaData := make(map[string][]string)
|
||||
if contentType != "" {
|
||||
metaData["Content-Type"] = []string{"contentType"}
|
||||
}
|
||||
if encrypt {
|
||||
metaData["x-amz-server-side-encryption"] = []string{"AES256"}
|
||||
}
|
||||
return metaData
|
||||
}
|
||||
|
||||
func s3CopyMetadata(encrypt bool) map[string]string {
|
||||
metaData := make(map[string]string)
|
||||
metaData["x-amz-server-side-encryption"] = "AES256"
|
||||
|
||||
2
vendor/github.com/NYTimes/gziphandler/gzip.go
generated
vendored
2
vendor/github.com/NYTimes/gziphandler/gzip.go
generated
vendored
@@ -88,7 +88,7 @@ type GzipResponseWriterWithCloseNotify struct {
|
||||
*GzipResponseWriter
|
||||
}
|
||||
|
||||
func (w *GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
|
||||
func (w GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
|
||||
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
|
||||
19
vendor/github.com/NYTimes/gziphandler/gzip_test.go
generated
vendored
19
vendor/github.com/NYTimes/gziphandler/gzip_test.go
generated
vendored
@@ -325,17 +325,32 @@ func TestFlushBeforeWrite(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestImplementCloseNotifier(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
request.Header.Set(acceptEncoding, "gzip")
|
||||
GzipHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request){
|
||||
_, ok := rw.(http.CloseNotifier)
|
||||
assert.True(t, ok, "response writer must implement http.CloseNotifier")
|
||||
})).ServeHTTP(&mockRWCloseNotify{}, &http.Request{})
|
||||
})).ServeHTTP(&mockRWCloseNotify{}, request)
|
||||
}
|
||||
|
||||
func TestImplementFlusherAndCloseNotifier(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
request.Header.Set(acceptEncoding, "gzip")
|
||||
GzipHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request){
|
||||
_, okCloseNotifier := rw.(http.CloseNotifier)
|
||||
assert.True(t, okCloseNotifier, "response writer must implement http.CloseNotifier")
|
||||
_, okFlusher := rw.(http.Flusher)
|
||||
assert.True(t, okFlusher, "response writer must implement http.Flusher")
|
||||
})).ServeHTTP(&mockRWCloseNotify{}, request)
|
||||
}
|
||||
|
||||
func TestNotImplementCloseNotifier(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
request.Header.Set(acceptEncoding, "gzip")
|
||||
GzipHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request){
|
||||
_, ok := rw.(http.CloseNotifier)
|
||||
assert.False(t, ok, "response writer must not implement http.CloseNotifier")
|
||||
})).ServeHTTP(httptest.NewRecorder(), &http.Request{})
|
||||
})).ServeHTTP(httptest.NewRecorder(), request)
|
||||
}
|
||||
|
||||
|
||||
|
||||
56
vendor/github.com/avct/uasurfer/.gitignore
generated
vendored
56
vendor/github.com/avct/uasurfer/.gitignore
generated
vendored
@@ -1,56 +0,0 @@
|
||||
# Compiled bin #
|
||||
###################
|
||||
|
||||
|
||||
# Compiled source #
|
||||
###################
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Configuration Files #
|
||||
#######################
|
||||
*.cfg
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
logs
|
||||
coverage.html
|
||||
coverage.out
|
||||
|
||||
# Test Files #
|
||||
#######################
|
||||
*.test
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# go.rice generated files
|
||||
*.rice-box.go
|
||||
|
||||
# Dev Tools #
|
||||
######################
|
||||
.vagrant
|
||||
11
vendor/github.com/avct/uasurfer/.travis.yml
generated
vendored
11
vendor/github.com/avct/uasurfer/.travis.yml
generated
vendored
@@ -1,11 +0,0 @@
|
||||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.8.x
|
||||
- 1.7.x
|
||||
|
||||
script:
|
||||
- go test
|
||||
169
vendor/github.com/avct/uasurfer/README.md
generated
vendored
169
vendor/github.com/avct/uasurfer/README.md
generated
vendored
@@ -1,169 +0,0 @@
|
||||
[](https://travis-ci.org/avct/uasurfer) [](https://godoc.org/github.com/avct/uasurfer) [](https://goreportcard.com/report/github.com/avct/uasurfer)
|
||||
|
||||
# uasurfer
|
||||
|
||||

|
||||
|
||||
**User Agent Surfer** (uasurfer) is a lightweight Golang package that parses and abstracts [HTTP User-Agent strings](https://en.wikipedia.org/wiki/User_agent) with particular attention to device type.
|
||||
|
||||
The following information is returned by uasurfer from a raw HTTP User-Agent string:
|
||||
|
||||
| Name | Example | Coverage in 192,792 parses |
|
||||
|----------------|---------|--------------------------------|
|
||||
| Browser name | `chrome` | 99.85% |
|
||||
| Browser version | `53` | 99.17% |
|
||||
| Platform | `ipad` | 99.97% |
|
||||
| OS name | `ios` | 99.96% |
|
||||
| OS version | `10` | 98.81% |
|
||||
| Device type | `tablet` | 99.98% |
|
||||
|
||||
Layout engine, browser language, and other esoteric attributes are not parsed.
|
||||
|
||||
Coverage is estimated from a random sample of real UA strings collected across thousands of sources in US and EU mid-2016.
|
||||
|
||||
## Usage
|
||||
|
||||
### Parse(ua string) Function
|
||||
|
||||
The `Parse()` function accepts a user agent `string` and returns UserAgent struct with named constants and integers for versions (minor, major and patch separately), and the full UA string that was parsed (lowercase). A string can be retrieved by adding `.String()` to a variable, such as `uasurfer.BrowserName.String()`.
|
||||
|
||||
```
|
||||
// Define a user agent string
|
||||
myUA := "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36"
|
||||
|
||||
// Parse() returns all attributes, including returning the full UA string last
|
||||
ua, uaString := uasurfer.Parse(myUA)
|
||||
```
|
||||
|
||||
where example UserAgent is:
|
||||
```
|
||||
{
|
||||
Browser {
|
||||
BrowserName: BrowserChrome,
|
||||
Version: {
|
||||
Major: 45,
|
||||
Minor: 0,
|
||||
Patch: 2454,
|
||||
},
|
||||
},
|
||||
OS {
|
||||
Platform: PlatformMac,
|
||||
Name: OSMacOSX,
|
||||
Version: {
|
||||
Major: 10,
|
||||
Minor: 10,
|
||||
Patch: 5,
|
||||
},
|
||||
},
|
||||
DeviceType: DeviceComputer,
|
||||
}
|
||||
```
|
||||
|
||||
**Usage note:** There are some OSes that do not return a version, see docs below. Linux is typically not reported with a specific Linux distro name or version.
|
||||
|
||||
#### Browser Name
|
||||
* `BrowserChrome` - Google [Chrome](https://en.wikipedia.org/wiki/Google_Chrome), [Chromium](https://en.wikipedia.org/wiki/Chromium_(web_browser))
|
||||
* `BrowserSafari` - Apple [Safari](https://en.wikipedia.org/wiki/Safari_(web_browser)), Google Search ([GSA](https://itunes.apple.com/us/app/google/id284815942))
|
||||
* `BrowserIE` - Microsoft [Internet Explorer](https://en.wikipedia.org/wiki/Internet_Explorer), [Edge](https://en.wikipedia.org/wiki/Microsoft_Edge)
|
||||
* `BrowserFirefox` - Mozilla [Firefox](https://en.wikipedia.org/wiki/Firefox), GNU [IceCat](https://en.wikipedia.org/wiki/GNU_IceCat), [Iceweasel](https://en.wikipedia.org/wiki/Mozilla_Corporation_software_rebranded_by_the_Debian_project#Iceweasel), [Seamonkey](https://en.wikipedia.org/wiki/SeaMonkey)
|
||||
* `BrowserAndroid` - Android [WebView](https://developer.chrome.com/multidevice/webview/overview) (Android OS <4.4 only)
|
||||
* `BrowserOpera` - [Opera](https://en.wikipedia.org/wiki/Opera_(web_browser))
|
||||
* `BrowserUCBrowser` - [UC Browser](https://en.wikipedia.org/wiki/UC_Browser)
|
||||
* `BrowserSilk` - Amazon [Silk](https://en.wikipedia.org/wiki/Amazon_Silk)
|
||||
* `BrowserSpotify` - [Spotify](https://en.wikipedia.org/wiki/Spotify#Clients) desktop client
|
||||
* `BrowserBlackberry` - RIM [BlackBerry](https://en.wikipedia.org/wiki/BlackBerry)
|
||||
* `BrowserUnknown` - Unknown
|
||||
|
||||
#### Browser Version
|
||||
|
||||
Browser version returns an `unint8` of the major version attribute of the User-Agent String. For example Chrome 45.0.23423 would return `45`. The intention is to support math operators with versions, such as "do XYZ for Chrome version >23".
|
||||
|
||||
Unknown version is returned as `0`.
|
||||
|
||||
#### Platform
|
||||
* `PlatformWindows` - Microsoft Windows
|
||||
* `PlatformMac` - Apple Macintosh
|
||||
* `PlatformLinux` - Linux, including Android and other OSes
|
||||
* `PlatformiPad` - Apple iPad
|
||||
* `PlatformiPhone` - Apple iPhone
|
||||
* `PlatformBlackberry` - RIM Blackberry
|
||||
* `PlatformWindowsPhone` Microsoft Windows Phone & Mobile
|
||||
* `PlatformKindle` - Amazon Kindle & Kindle Fire
|
||||
* `PlatformPlaystation` - Sony Playstation, Vita, PSP
|
||||
* `PlatformXbox` - Microsoft Xbox - `PlatformXbox`
|
||||
* `PlatformNintendo` - Nintendo DS, Wii, etc.
|
||||
* `PlatformUnknown` - Unknown
|
||||
|
||||
#### OS Name
|
||||
* `OSWindows`
|
||||
* `OSMacOSX` - includes "macOS Sierra"
|
||||
* `OSiOS`
|
||||
* `OSAndroid`
|
||||
* `OSChromeOS`
|
||||
* `OSWebOS`
|
||||
* `OSLinux`
|
||||
* `OSPlaystation`
|
||||
* `OSXbox`
|
||||
* `OSNintendo`
|
||||
* `OSUnknown`
|
||||
|
||||
#### OS Version
|
||||
|
||||
OS X major version is alway 10 with consecutive minor versions indicating release releases (10 - Yosemite, 11 - El Capitain, 12 Sierra, etc). Windows version is NT version. `Version{0, 0, 0}` indicated version is unknown or not evaluated.
|
||||
Versions can be compared using `Less` function: `if ver1.Less(ver2) {}`
|
||||
|
||||
Here are some examples across the platform, os.name, and os.version:
|
||||
|
||||
* For Windows XP (Windows NT 5.1), "`PlatformWindows`" is the platform, "`OSWindows`" is the name, and `{5, 1, 0}` the version.
|
||||
* For OS X 10.5.1, "`PlatformMac`" is the platform, "`OSMacOSX`" the name, and `{10, 5, 1}` the version.
|
||||
* For Android 5.1, "`PlatformLinux`" is the platform, "`OSAndroid`" is the name, and `{5, 1, 0}` the version.
|
||||
* For iOS 5.1, "`PlatformiPhone`" or "`PlatformiPad`" is the platform, "`OSiOS`" is the name, and `{5, 1, 0}` the version.
|
||||
|
||||
###### Windows Version Guide
|
||||
|
||||
* Windows 10 - `{10, 0, 0}`
|
||||
* Windows 8.1 - `{6, 3, 0}`
|
||||
* Windows 8 - `{6, 2, 0}`
|
||||
* Windows 7 - `{6, 1, 0}`
|
||||
* Windows Vista - `{6, 0, 0}`
|
||||
* Windows XP - `{5, 1, 0}` or `{5, 2, 0}`
|
||||
* Windows 2000 - `{5, 0, 0}`
|
||||
|
||||
Windows 95, 98, and ME represent 0.01% of traffic worldwide and are not available through this package at this time.
|
||||
|
||||
#### DeviceType
|
||||
DeviceType is typically quite accurate, though determining between phones and tablets on Android is not always possible due to how some vendors design their UA strings. A mobile Android device without tablet indicator defaults to being classified as a phone. DeviceTV supports major brands such as Philips, Sharp, Vizio and steaming boxes such as Apple, Google, Roku, Amazon.
|
||||
|
||||
* `DeviceComputer`
|
||||
* `DevicePhone`
|
||||
* `DeviceTablet`
|
||||
* `DeviceTV`
|
||||
* `DeviceConsole`
|
||||
* `DeviceWearable`
|
||||
* `DeviceUnknown`
|
||||
|
||||
## Example Combinations of Attributes
|
||||
* Surface RT -> `OSWindows8`, `DeviceTablet`, OSVersion >= `6`
|
||||
* Android Tablet -> `OSAndroid`, `DeviceTablet`
|
||||
* Microsoft Edge -> `BrowserIE`, BrowserVersion >= `12.0.0`
|
||||
|
||||
## To do
|
||||
|
||||
* Remove compiled regexp in favor of string.Contains wherever possible (lowers mem/alloc)
|
||||
* Better version support on Firefox derivatives (e.g. SeaMonkey)
|
||||
* Potential additional browser support:
|
||||
* "NetFront" (1% share in India)
|
||||
* "QQ Browser" (6.5% share in China)
|
||||
* "Sogou Explorer" (5% share in China)
|
||||
* "Maxthon" (1.5% share in China)
|
||||
* "Nokia"
|
||||
* Potential additional OS support:
|
||||
* "Nokia" (5% share in India)
|
||||
* "Series 40" (5.5% share in India)
|
||||
* Windows 2003 Server
|
||||
* iOS safari browser identification based on iOS version
|
||||
* Add android version to browser identification
|
||||
* old Macs
|
||||
* "opera/9.64 (macintosh; ppc mac os x; u; en) presto/2.1.1"
|
||||
* old Windows
|
||||
* "mozilla/5.0 (windows nt 4.0; wow64) applewebkit/537.36 (khtml, like gecko) chrome/37.0.2049.0 safari/537.36"
|
||||
192
vendor/github.com/avct/uasurfer/browser.go
generated
vendored
192
vendor/github.com/avct/uasurfer/browser.go
generated
vendored
@@ -1,192 +0,0 @@
|
||||
package uasurfer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Browser struct contains the lowercase name of the browser, along
|
||||
// with its browser version number. Browser are grouped together without
|
||||
// consideration for device. For example, Chrome (Chrome/43.0) and Chrome for iOS
|
||||
// (CriOS/43.0) would both return as "chrome" (name) and 43.0 (version). Similarly
|
||||
// Internet Explorer 11 and Edge 12 would return as "ie" and "11" or "12", respectively.
|
||||
// type Browser struct {
|
||||
// Name BrowserName
|
||||
// Version struct {
|
||||
// Major int
|
||||
// Minor int
|
||||
// Patch int
|
||||
// }
|
||||
// }
|
||||
|
||||
// Retrieve browser name from UA strings
|
||||
func (u *UserAgent) evalBrowserName(ua string) bool {
|
||||
// Blackberry goes first because it reads as MSIE & Safari
|
||||
if strings.Contains(ua, "blackberry") || strings.Contains(ua, "playbook") || strings.Contains(ua, "bb10") || strings.Contains(ua, "rim ") {
|
||||
u.Browser.Name = BrowserBlackberry
|
||||
return u.isBot()
|
||||
}
|
||||
|
||||
if strings.Contains(ua, "applewebkit") {
|
||||
switch {
|
||||
case strings.Contains(ua, "opr/") || strings.Contains(ua, "opios/"):
|
||||
u.Browser.Name = BrowserOpera
|
||||
|
||||
case strings.Contains(ua, "silk/"):
|
||||
u.Browser.Name = BrowserSilk
|
||||
|
||||
case strings.Contains(ua, "edge/") || strings.Contains(ua, "iemobile/") || strings.Contains(ua, "msie "):
|
||||
u.Browser.Name = BrowserIE
|
||||
|
||||
case strings.Contains(ua, "ucbrowser/") || strings.Contains(ua, "ucweb/"):
|
||||
u.Browser.Name = BrowserUCBrowser
|
||||
|
||||
// Edge, Silk and other chrome-identifying browsers must evaluate before chrome, unless we want to add more overhead
|
||||
case strings.Contains(ua, "chrome/") || strings.Contains(ua, "crios/") || strings.Contains(ua, "chromium/") || strings.Contains(ua, "crmo/"):
|
||||
u.Browser.Name = BrowserChrome
|
||||
|
||||
case strings.Contains(ua, "android") && !strings.Contains(ua, "chrome/") && strings.Contains(ua, "version/") && !strings.Contains(ua, "like android"):
|
||||
// Android WebView on Android >= 4.4 is purposefully being identified as Chrome above -- https://developer.chrome.com/multidevice/webview/overview
|
||||
u.Browser.Name = BrowserAndroid
|
||||
|
||||
case strings.Contains(ua, "fxios"):
|
||||
u.Browser.Name = BrowserFirefox
|
||||
|
||||
case strings.Contains(ua, " spotify/"):
|
||||
u.Browser.Name = BrowserSpotify
|
||||
|
||||
// AppleBot uses webkit signature as well
|
||||
case strings.Contains(ua, "applebot"):
|
||||
u.Browser.Name = BrowserAppleBot
|
||||
|
||||
// presume it's safari unless an esoteric browser is being specified (webOSBrowser, SamsungBrowser, etc.)
|
||||
case strings.Contains(ua, "like gecko") && strings.Contains(ua, "mozilla/") && strings.Contains(ua, "safari/") && !strings.Contains(ua, "linux") && !strings.Contains(ua, "android") && !strings.Contains(ua, "browser/") && !strings.Contains(ua, "os/"):
|
||||
u.Browser.Name = BrowserSafari
|
||||
|
||||
// if we got this far and the device is iPhone or iPad, assume safari. Some agents don't actually contain the word "safari"
|
||||
case strings.Contains(ua, "iphone") || strings.Contains(ua, "ipad"):
|
||||
u.Browser.Name = BrowserSafari
|
||||
|
||||
// Google's search app on iPhone, leverages native Safari rather than Chrome
|
||||
case strings.Contains(ua, " gsa/"):
|
||||
u.Browser.Name = BrowserSafari
|
||||
|
||||
default:
|
||||
goto notwebkit
|
||||
|
||||
}
|
||||
return u.isBot()
|
||||
}
|
||||
|
||||
notwebkit:
|
||||
switch {
|
||||
case strings.Contains(ua, "msie") || strings.Contains(ua, "trident"):
|
||||
u.Browser.Name = BrowserIE
|
||||
|
||||
case strings.Contains(ua, "gecko") && (strings.Contains(ua, "firefox") || strings.Contains(ua, "iceweasel") || strings.Contains(ua, "seamonkey") || strings.Contains(ua, "icecat")):
|
||||
u.Browser.Name = BrowserFirefox
|
||||
|
||||
case strings.Contains(ua, "presto") || strings.Contains(ua, "opera"):
|
||||
u.Browser.Name = BrowserOpera
|
||||
|
||||
case strings.Contains(ua, "ucbrowser"):
|
||||
u.Browser.Name = BrowserUCBrowser
|
||||
|
||||
case strings.Contains(ua, "applebot"):
|
||||
u.Browser.Name = BrowserAppleBot
|
||||
|
||||
case strings.Contains(ua, "baiduspider"):
|
||||
u.Browser.Name = BrowserBaiduBot
|
||||
|
||||
case strings.Contains(ua, "adidxbot") || strings.Contains(ua, "bingbot") || strings.Contains(ua, "bingpreview"):
|
||||
u.Browser.Name = BrowserBingBot
|
||||
|
||||
case strings.Contains(ua, "duckduckbot"):
|
||||
u.Browser.Name = BrowserDuckDuckGoBot
|
||||
|
||||
case strings.Contains(ua, "facebot") || strings.Contains(ua, "facebookexternalhit"):
|
||||
u.Browser.Name = BrowserFacebookBot
|
||||
|
||||
case strings.Contains(ua, "googlebot"):
|
||||
u.Browser.Name = BrowserGoogleBot
|
||||
|
||||
case strings.Contains(ua, "linkedinbot"):
|
||||
u.Browser.Name = BrowserLinkedInBot
|
||||
|
||||
case strings.Contains(ua, "msnbot"):
|
||||
u.Browser.Name = BrowserMsnBot
|
||||
|
||||
case strings.Contains(ua, "pingdom.com_bot"):
|
||||
u.Browser.Name = BrowserPingdomBot
|
||||
|
||||
case strings.Contains(ua, "twitterbot"):
|
||||
u.Browser.Name = BrowserTwitterBot
|
||||
|
||||
case strings.Contains(ua, "yandex") || strings.Contains(ua, "yadirectfetcher"):
|
||||
u.Browser.Name = BrowserYandexBot
|
||||
|
||||
case strings.Contains(ua, "yahoo"):
|
||||
u.Browser.Name = BrowserYahooBot
|
||||
|
||||
case strings.Contains(ua, "phantomjs"):
|
||||
u.Browser.Name = BrowserBot
|
||||
|
||||
default:
|
||||
u.Browser.Name = BrowserUnknown
|
||||
|
||||
}
|
||||
|
||||
return u.isBot()
|
||||
}
|
||||
|
||||
// Retrieve browser version
|
||||
// Methods used in order:
|
||||
// 1st: look for generic version/#
|
||||
// 2nd: look for browser-specific instructions (e.g. chrome/34)
|
||||
// 3rd: infer from OS (iOS only)
|
||||
func (u *UserAgent) evalBrowserVersion(ua string) {
|
||||
// if there is a 'version/#' attribute with numeric version, use it -- except for Chrome since Android vendors sometimes hijack version/#
|
||||
if u.Browser.Name != BrowserChrome && u.Browser.Version.findVersionNumber(ua, "version/") {
|
||||
return
|
||||
}
|
||||
|
||||
switch u.Browser.Name {
|
||||
case BrowserChrome:
|
||||
// match both chrome and crios
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "chrome/") || u.Browser.Version.findVersionNumber(ua, "crios/") || u.Browser.Version.findVersionNumber(ua, "crmo/")
|
||||
|
||||
case BrowserIE:
|
||||
if u.Browser.Version.findVersionNumber(ua, "msie ") || u.Browser.Version.findVersionNumber(ua, "edge/") {
|
||||
return
|
||||
}
|
||||
|
||||
// get MSIE version from trident version https://en.wikipedia.org/wiki/Trident_(layout_engine)
|
||||
if u.Browser.Version.findVersionNumber(ua, "trident/") {
|
||||
// convert trident versions 3-7 to MSIE version
|
||||
if (u.Browser.Version.Major >= 3) && (u.Browser.Version.Major <= 7) {
|
||||
u.Browser.Version.Major += 4
|
||||
}
|
||||
}
|
||||
|
||||
case BrowserFirefox:
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "firefox/") || u.Browser.Version.findVersionNumber(ua, "fxios/")
|
||||
|
||||
case BrowserSafari: // executes typically if we're on iOS and not using a familiar browser
|
||||
u.Browser.Version = u.OS.Version
|
||||
// early Safari used a version number +1 to OS version
|
||||
if (u.Browser.Version.Major <= 3) && (u.Browser.Version.Major >= 1) {
|
||||
u.Browser.Version.Major++
|
||||
}
|
||||
|
||||
case BrowserUCBrowser:
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "ucbrowser/")
|
||||
|
||||
case BrowserOpera:
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "opr/") || u.Browser.Version.findVersionNumber(ua, "opios/") || u.Browser.Version.findVersionNumber(ua, "opera/")
|
||||
|
||||
case BrowserSilk:
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "silk/")
|
||||
|
||||
case BrowserSpotify:
|
||||
_ = u.Browser.Version.findVersionNumber(ua, "spotify/")
|
||||
}
|
||||
}
|
||||
49
vendor/github.com/avct/uasurfer/const_string.go
generated
vendored
49
vendor/github.com/avct/uasurfer/const_string.go
generated
vendored
@@ -1,49 +0,0 @@
|
||||
// Code generated by "stringer -type=DeviceType,BrowserName,OSName,Platform -output=const_string.go"; DO NOT EDIT.
|
||||
|
||||
package uasurfer
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _DeviceType_name = "DeviceUnknownDeviceComputerDeviceTabletDevicePhoneDeviceConsoleDeviceWearableDeviceTV"
|
||||
|
||||
var _DeviceType_index = [...]uint8{0, 13, 27, 39, 50, 63, 77, 85}
|
||||
|
||||
func (i DeviceType) String() string {
|
||||
if i < 0 || i >= DeviceType(len(_DeviceType_index)-1) {
|
||||
return fmt.Sprintf("DeviceType(%d)", i)
|
||||
}
|
||||
return _DeviceType_name[_DeviceType_index[i]:_DeviceType_index[i+1]]
|
||||
}
|
||||
|
||||
const _BrowserName_name = "BrowserUnknownBrowserChromeBrowserIEBrowserSafariBrowserFirefoxBrowserAndroidBrowserOperaBrowserBlackberryBrowserUCBrowserBrowserSilkBrowserNokiaBrowserNetFrontBrowserQQBrowserMaxthonBrowserSogouExplorerBrowserSpotifyBrowserBotBrowserAppleBotBrowserBaiduBotBrowserBingBotBrowserDuckDuckGoBotBrowserFacebookBotBrowserGoogleBotBrowserLinkedInBotBrowserMsnBotBrowserPingdomBotBrowserTwitterBotBrowserYandexBotBrowserYahooBot"
|
||||
|
||||
var _BrowserName_index = [...]uint16{0, 14, 27, 36, 49, 63, 77, 89, 106, 122, 133, 145, 160, 169, 183, 203, 217, 227, 242, 257, 271, 291, 309, 325, 343, 356, 373, 390, 406, 421}
|
||||
|
||||
func (i BrowserName) String() string {
|
||||
if i < 0 || i >= BrowserName(len(_BrowserName_index)-1) {
|
||||
return fmt.Sprintf("BrowserName(%d)", i)
|
||||
}
|
||||
return _BrowserName_name[_BrowserName_index[i]:_BrowserName_index[i+1]]
|
||||
}
|
||||
|
||||
const _OSName_name = "OSUnknownOSWindowsPhoneOSWindowsOSMacOSXOSiOSOSAndroidOSBlackberryOSChromeOSOSKindleOSWebOSOSLinuxOSPlaystationOSXboxOSNintendoOSBot"
|
||||
|
||||
var _OSName_index = [...]uint8{0, 9, 23, 32, 40, 45, 54, 66, 76, 84, 91, 98, 111, 117, 127, 132}
|
||||
|
||||
func (i OSName) String() string {
|
||||
if i < 0 || i >= OSName(len(_OSName_index)-1) {
|
||||
return fmt.Sprintf("OSName(%d)", i)
|
||||
}
|
||||
return _OSName_name[_OSName_index[i]:_OSName_index[i+1]]
|
||||
}
|
||||
|
||||
const _Platform_name = "PlatformUnknownPlatformWindowsPlatformMacPlatformLinuxPlatformiPadPlatformiPhonePlatformiPodPlatformBlackberryPlatformWindowsPhonePlatformPlaystationPlatformXboxPlatformNintendoPlatformBot"
|
||||
|
||||
var _Platform_index = [...]uint8{0, 15, 30, 41, 54, 66, 80, 92, 110, 130, 149, 161, 177, 188}
|
||||
|
||||
func (i Platform) String() string {
|
||||
if i < 0 || i >= Platform(len(_Platform_index)-1) {
|
||||
return fmt.Sprintf("Platform(%d)", i)
|
||||
}
|
||||
return _Platform_name[_Platform_index[i]:_Platform_index[i+1]]
|
||||
}
|
||||
60
vendor/github.com/avct/uasurfer/device.go
generated
vendored
60
vendor/github.com/avct/uasurfer/device.go
generated
vendored
@@ -1,60 +0,0 @@
|
||||
package uasurfer
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (u *UserAgent) evalDevice(ua string) {
|
||||
switch {
|
||||
|
||||
case u.OS.Platform == PlatformWindows || u.OS.Platform == PlatformMac || u.OS.Name == OSChromeOS:
|
||||
if strings.Contains(ua, "mobile") || strings.Contains(ua, "touch") {
|
||||
u.DeviceType = DeviceTablet // windows rt, linux haxor tablets
|
||||
return
|
||||
}
|
||||
u.DeviceType = DeviceComputer
|
||||
|
||||
case u.OS.Platform == PlatformiPad || u.OS.Platform == PlatformiPod || strings.Contains(ua, "tablet") || strings.Contains(ua, "kindle/") || strings.Contains(ua, "playbook"):
|
||||
u.DeviceType = DeviceTablet
|
||||
|
||||
case u.OS.Platform == PlatformiPhone || u.OS.Platform == PlatformBlackberry || strings.Contains(ua, "phone"):
|
||||
u.DeviceType = DevicePhone
|
||||
|
||||
// long list of smarttv and tv dongle identifiers
|
||||
case strings.Contains(ua, "tv") || strings.Contains(ua, "crkey") || strings.Contains(ua, "googletv") || strings.Contains(ua, "aftb") || strings.Contains(ua, "adt-") || strings.Contains(ua, "roku") || strings.Contains(ua, "viera") || strings.Contains(ua, "aquos") || strings.Contains(ua, "dtv") || strings.Contains(ua, "appletv") || strings.Contains(ua, "smarttv") || strings.Contains(ua, "tuner") || strings.Contains(ua, "smart-tv") || strings.Contains(ua, "hbbtv") || strings.Contains(ua, "netcast") || strings.Contains(ua, "vizio"):
|
||||
u.DeviceType = DeviceTV
|
||||
|
||||
case u.OS.Name == OSAndroid:
|
||||
// android phones report as "mobile", android tablets should not but often do -- http://android-developers.blogspot.com/2010/12/android-browser-user-agent-issues.html
|
||||
if strings.Contains(ua, "mobile") {
|
||||
u.DeviceType = DevicePhone
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(ua, "tablet") || strings.Contains(ua, "nexus 7") || strings.Contains(ua, "nexus 9") || strings.Contains(ua, "nexus 10") || strings.Contains(ua, "xoom") {
|
||||
u.DeviceType = DeviceTablet
|
||||
return
|
||||
}
|
||||
|
||||
u.DeviceType = DevicePhone // default to phone
|
||||
|
||||
case u.OS.Platform == PlatformPlaystation || u.OS.Platform == PlatformXbox || u.OS.Platform == PlatformNintendo:
|
||||
u.DeviceType = DeviceConsole
|
||||
|
||||
case strings.Contains(ua, "glass") || strings.Contains(ua, "watch") || strings.Contains(ua, "sm-v"):
|
||||
u.DeviceType = DeviceWearable
|
||||
|
||||
// specifically above "mobile" string check as Kindle Fire tablets report as "mobile"
|
||||
case u.Browser.Name == BrowserSilk || u.OS.Name == OSKindle && !strings.Contains(ua, "sd4930ur"):
|
||||
u.DeviceType = DeviceTablet
|
||||
|
||||
case strings.Contains(ua, "mobile") || strings.Contains(ua, "touch") || strings.Contains(ua, " mobi") || strings.Contains(ua, "webos"): //anything "mobile"/"touch" that didn't get captured as tablet, console or wearable is presumed a phone
|
||||
u.DeviceType = DevicePhone
|
||||
|
||||
case u.OS.Name == OSLinux: // linux goes last since it's in so many other device types (tvs, wearables, android-based stuff)
|
||||
u.DeviceType = DeviceComputer
|
||||
|
||||
default:
|
||||
u.DeviceType = DeviceUnknown
|
||||
}
|
||||
}
|
||||
332
vendor/github.com/avct/uasurfer/system.go
generated
vendored
332
vendor/github.com/avct/uasurfer/system.go
generated
vendored
@@ -1,332 +0,0 @@
|
||||
package uasurfer
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
amazonFireFingerprint = regexp.MustCompile("\\s(k[a-z]{3,5}|sd\\d{4}ur)\\s") //tablet or phone
|
||||
)
|
||||
|
||||
func (u *UserAgent) evalOS(ua string) bool {
|
||||
|
||||
s := strings.IndexRune(ua, '(')
|
||||
e := strings.IndexRune(ua, ')')
|
||||
if s > e {
|
||||
s = 0
|
||||
e = len(ua)
|
||||
}
|
||||
if e == -1 {
|
||||
e = len(ua)
|
||||
}
|
||||
|
||||
agentPlatform := ua[s+1 : e]
|
||||
specsEnd := strings.Index(agentPlatform, ";")
|
||||
var specs string
|
||||
if specsEnd != -1 {
|
||||
specs = agentPlatform[:specsEnd]
|
||||
} else {
|
||||
specs = agentPlatform
|
||||
}
|
||||
|
||||
//strict OS & version identification
|
||||
switch specs {
|
||||
case "android":
|
||||
u.evalLinux(ua, agentPlatform)
|
||||
|
||||
case "bb10", "playbook":
|
||||
u.OS.Platform = PlatformBlackberry
|
||||
u.OS.Name = OSBlackberry
|
||||
|
||||
case "x11", "linux":
|
||||
u.evalLinux(ua, agentPlatform)
|
||||
|
||||
case "ipad", "iphone", "ipod touch", "ipod":
|
||||
u.evaliOS(specs, agentPlatform)
|
||||
|
||||
case "macintosh":
|
||||
u.evalMacintosh(ua)
|
||||
|
||||
default:
|
||||
switch {
|
||||
// Blackberry
|
||||
case strings.Contains(ua, "blackberry") || strings.Contains(ua, "playbook"):
|
||||
u.OS.Platform = PlatformBlackberry
|
||||
u.OS.Name = OSBlackberry
|
||||
|
||||
// Windows Phone
|
||||
case strings.Contains(agentPlatform, "windows phone "):
|
||||
u.evalWindowsPhone(agentPlatform)
|
||||
|
||||
// Windows, Xbox
|
||||
case strings.Contains(ua, "windows "):
|
||||
u.evalWindows(ua)
|
||||
|
||||
// Kindle
|
||||
case strings.Contains(ua, "kindle/") || amazonFireFingerprint.MatchString(agentPlatform):
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSKindle
|
||||
|
||||
// Linux (broader attempt)
|
||||
case strings.Contains(ua, "linux"):
|
||||
u.evalLinux(ua, agentPlatform)
|
||||
|
||||
// WebOS (non-linux flagged)
|
||||
case strings.Contains(ua, "webos") || strings.Contains(ua, "hpwos"):
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSWebOS
|
||||
|
||||
// Nintendo
|
||||
case strings.Contains(ua, "nintendo"):
|
||||
u.OS.Platform = PlatformNintendo
|
||||
u.OS.Name = OSNintendo
|
||||
|
||||
// Playstation
|
||||
case strings.Contains(ua, "playstation") || strings.Contains(ua, "vita") || strings.Contains(ua, "psp"):
|
||||
u.OS.Platform = PlatformPlaystation
|
||||
u.OS.Name = OSPlaystation
|
||||
|
||||
// Android
|
||||
case strings.Contains(ua, "android"):
|
||||
u.evalLinux(ua, agentPlatform)
|
||||
|
||||
default:
|
||||
u.OS.Platform = PlatformUnknown
|
||||
u.OS.Name = OSUnknown
|
||||
}
|
||||
}
|
||||
|
||||
return u.isBot()
|
||||
}
|
||||
|
||||
func (u *UserAgent) isBot() bool {
|
||||
|
||||
if u.OS.Platform == PlatformBot || u.OS.Name == OSBot {
|
||||
u.DeviceType = DeviceComputer
|
||||
return true
|
||||
}
|
||||
|
||||
if u.Browser.Name >= BrowserBot && u.Browser.Name <= BrowserYahooBot {
|
||||
u.OS.Platform = PlatformBot
|
||||
u.OS.Name = OSBot
|
||||
u.DeviceType = DeviceComputer
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// evalLinux returns the `Platform`, `OSName` and Version of UAs with
|
||||
// 'linux' listed as their platform.
|
||||
func (u *UserAgent) evalLinux(ua string, agentPlatform string) {
|
||||
|
||||
switch {
|
||||
// Kindle Fire
|
||||
case strings.Contains(ua, "kindle") || amazonFireFingerprint.MatchString(agentPlatform):
|
||||
// get the version of Android if available, though we don't call this OSAndroid
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSKindle
|
||||
u.OS.Version.findVersionNumber(agentPlatform, "android ")
|
||||
|
||||
// Android, Kindle Fire
|
||||
case strings.Contains(ua, "android") || strings.Contains(ua, "googletv"):
|
||||
// Android
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSAndroid
|
||||
u.OS.Version.findVersionNumber(agentPlatform, "android ")
|
||||
|
||||
// ChromeOS
|
||||
case strings.Contains(ua, "cros"):
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSChromeOS
|
||||
|
||||
// WebOS
|
||||
case strings.Contains(ua, "webos") || strings.Contains(ua, "hpwos"):
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSWebOS
|
||||
|
||||
// Linux, "Linux-like"
|
||||
case strings.Contains(ua, "x11") || strings.Contains(ua, "bsd") || strings.Contains(ua, "suse") || strings.Contains(ua, "debian") || strings.Contains(ua, "ubuntu"):
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSLinux
|
||||
|
||||
default:
|
||||
u.OS.Platform = PlatformLinux
|
||||
u.OS.Name = OSLinux
|
||||
}
|
||||
}
|
||||
|
||||
// evaliOS returns the `Platform`, `OSName` and Version of UAs with
|
||||
// 'ipad' or 'iphone' listed as their platform.
|
||||
func (u *UserAgent) evaliOS(uaPlatform string, agentPlatform string) {
|
||||
|
||||
switch uaPlatform {
|
||||
// iPhone
|
||||
case "iphone":
|
||||
u.OS.Platform = PlatformiPhone
|
||||
u.OS.Name = OSiOS
|
||||
u.OS.getiOSVersion(agentPlatform)
|
||||
|
||||
// iPad
|
||||
case "ipad":
|
||||
u.OS.Platform = PlatformiPad
|
||||
u.OS.Name = OSiOS
|
||||
u.OS.getiOSVersion(agentPlatform)
|
||||
|
||||
// iPod
|
||||
case "ipod touch", "ipod":
|
||||
u.OS.Platform = PlatformiPod
|
||||
u.OS.Name = OSiOS
|
||||
u.OS.getiOSVersion(agentPlatform)
|
||||
|
||||
default:
|
||||
u.OS.Platform = PlatformiPad
|
||||
u.OS.Name = OSUnknown
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserAgent) evalWindowsPhone(agentPlatform string) {
|
||||
u.OS.Platform = PlatformWindowsPhone
|
||||
|
||||
if u.OS.Version.findVersionNumber(agentPlatform, "windows phone os ") || u.OS.Version.findVersionNumber(agentPlatform, "windows phone ") {
|
||||
u.OS.Name = OSWindowsPhone
|
||||
} else {
|
||||
u.OS.Name = OSUnknown
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserAgent) evalWindows(ua string) {
|
||||
|
||||
switch {
|
||||
//Xbox -- it reads just like Windows
|
||||
case strings.Contains(ua, "xbox"):
|
||||
u.OS.Platform = PlatformXbox
|
||||
u.OS.Name = OSXbox
|
||||
if !u.OS.Version.findVersionNumber(ua, "windows nt ") {
|
||||
u.OS.Version.Major = 6
|
||||
u.OS.Version.Minor = 0
|
||||
u.OS.Version.Patch = 0
|
||||
}
|
||||
|
||||
// No windows version
|
||||
case !strings.Contains(ua, "windows "):
|
||||
u.OS.Platform = PlatformWindows
|
||||
u.OS.Name = OSUnknown
|
||||
|
||||
case strings.Contains(ua, "windows nt ") && u.OS.Version.findVersionNumber(ua, "windows nt "):
|
||||
u.OS.Platform = PlatformWindows
|
||||
u.OS.Name = OSWindows
|
||||
|
||||
case strings.Contains(ua, "windows xp"):
|
||||
u.OS.Platform = PlatformWindows
|
||||
u.OS.Name = OSWindows
|
||||
u.OS.Version.Major = 5
|
||||
u.OS.Version.Minor = 1
|
||||
u.OS.Version.Patch = 0
|
||||
|
||||
default:
|
||||
u.OS.Platform = PlatformWindows
|
||||
u.OS.Name = OSUnknown
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserAgent) evalMacintosh(uaPlatformGroup string) {
|
||||
u.OS.Platform = PlatformMac
|
||||
if i := strings.Index(uaPlatformGroup, "os x 10"); i != -1 {
|
||||
u.OS.Name = OSMacOSX
|
||||
u.OS.Version.parse(uaPlatformGroup[i+5:])
|
||||
|
||||
return
|
||||
}
|
||||
u.OS.Name = OSUnknown
|
||||
}
|
||||
|
||||
func (v *Version) findVersionNumber(s string, m string) bool {
|
||||
if ind := strings.Index(s, m); ind != -1 {
|
||||
return v.parse(s[ind+len(m):])
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getiOSVersion accepts the platform portion of a UA string and returns
|
||||
// a Version.
|
||||
func (o *OS) getiOSVersion(uaPlatformGroup string) {
|
||||
if i := strings.Index(uaPlatformGroup, "cpu iphone os "); i != -1 {
|
||||
o.Version.parse(uaPlatformGroup[i+14:])
|
||||
return
|
||||
}
|
||||
|
||||
if i := strings.Index(uaPlatformGroup, "cpu os "); i != -1 {
|
||||
o.Version.parse(uaPlatformGroup[i+7:])
|
||||
return
|
||||
}
|
||||
|
||||
o.Version.parse(uaPlatformGroup)
|
||||
}
|
||||
|
||||
// strToInt simply accepts a string and returns a `int`,
|
||||
// with '0' being default.
|
||||
func strToInt(str string) int {
|
||||
i, _ := strconv.Atoi(str)
|
||||
return i
|
||||
}
|
||||
|
||||
// strToVer accepts a string and returns a Version,
|
||||
// with {0, 0, 0} being default.
|
||||
func (v *Version) parse(str string) bool {
|
||||
if len(str) == 0 || str[0] < '0' || str[0] > '9' {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
empty := true
|
||||
val := 0
|
||||
l := len(str) - 1
|
||||
|
||||
for k, c := range str {
|
||||
if c >= '0' && c <= '9' {
|
||||
if empty {
|
||||
val = int(c) - 48
|
||||
empty = false
|
||||
if k == l {
|
||||
str = str[:0]
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if val == 0 {
|
||||
if c == '0' {
|
||||
if k == l {
|
||||
str = str[:0]
|
||||
}
|
||||
continue
|
||||
}
|
||||
str = str[k:]
|
||||
break
|
||||
}
|
||||
|
||||
val = 10*val + int(c) - 48
|
||||
if k == l {
|
||||
str = str[:0]
|
||||
}
|
||||
continue
|
||||
}
|
||||
str = str[k+1:]
|
||||
break
|
||||
}
|
||||
|
||||
switch i {
|
||||
case 0:
|
||||
v.Major = val
|
||||
|
||||
case 1:
|
||||
v.Minor = val
|
||||
|
||||
case 2:
|
||||
v.Patch = val
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
227
vendor/github.com/avct/uasurfer/uasurfer.go
generated
vendored
227
vendor/github.com/avct/uasurfer/uasurfer.go
generated
vendored
@@ -1,227 +0,0 @@
|
||||
// Package uasurfer provides fast and reliable abstraction
|
||||
// of HTTP User-Agent strings. The philosophy is to identify
|
||||
// technologies that holds >1% market share, and to avoid
|
||||
// expending resources and accuracy on guessing at esoteric UA
|
||||
// strings.
|
||||
package uasurfer
|
||||
|
||||
import "strings"
|
||||
|
||||
//go:generate stringer -type=DeviceType,BrowserName,OSName,Platform -output=const_string.go
|
||||
|
||||
// DeviceType (int) returns a constant.
|
||||
type DeviceType int
|
||||
|
||||
// A complete list of supported devices in the
|
||||
// form of constants.
|
||||
const (
|
||||
DeviceUnknown DeviceType = iota
|
||||
DeviceComputer
|
||||
DeviceTablet
|
||||
DevicePhone
|
||||
DeviceConsole
|
||||
DeviceWearable
|
||||
DeviceTV
|
||||
)
|
||||
|
||||
// BrowserName (int) returns a constant.
|
||||
type BrowserName int
|
||||
|
||||
// A complete list of supported web browsers in the
|
||||
// form of constants.
|
||||
const (
|
||||
BrowserUnknown BrowserName = iota
|
||||
BrowserChrome
|
||||
BrowserIE
|
||||
BrowserSafari
|
||||
BrowserFirefox
|
||||
BrowserAndroid
|
||||
BrowserOpera
|
||||
BrowserBlackberry
|
||||
BrowserUCBrowser
|
||||
BrowserSilk
|
||||
BrowserNokia
|
||||
BrowserNetFront
|
||||
BrowserQQ
|
||||
BrowserMaxthon
|
||||
BrowserSogouExplorer
|
||||
BrowserSpotify
|
||||
BrowserBot // Bot list begins here
|
||||
BrowserAppleBot
|
||||
BrowserBaiduBot
|
||||
BrowserBingBot
|
||||
BrowserDuckDuckGoBot
|
||||
BrowserFacebookBot
|
||||
BrowserGoogleBot
|
||||
BrowserLinkedInBot
|
||||
BrowserMsnBot
|
||||
BrowserPingdomBot
|
||||
BrowserTwitterBot
|
||||
BrowserYandexBot
|
||||
BrowserYahooBot // Bot list ends here
|
||||
)
|
||||
|
||||
// OSName (int) returns a constant.
|
||||
type OSName int
|
||||
|
||||
// A complete list of supported OSes in the
|
||||
// form of constants. For handling particular versions
|
||||
// of operating systems (e.g. Windows 2000), see
|
||||
// the README.md file.
|
||||
const (
|
||||
OSUnknown OSName = iota
|
||||
OSWindowsPhone
|
||||
OSWindows
|
||||
OSMacOSX
|
||||
OSiOS
|
||||
OSAndroid
|
||||
OSBlackberry
|
||||
OSChromeOS
|
||||
OSKindle
|
||||
OSWebOS
|
||||
OSLinux
|
||||
OSPlaystation
|
||||
OSXbox
|
||||
OSNintendo
|
||||
OSBot
|
||||
)
|
||||
|
||||
// Platform (int) returns a constant.
|
||||
type Platform int
|
||||
|
||||
// A complete list of supported platforms in the
|
||||
// form of constants. Many OSes report their
|
||||
// true platform, such as Android OS being Linux
|
||||
// platform.
|
||||
const (
|
||||
PlatformUnknown Platform = iota
|
||||
PlatformWindows
|
||||
PlatformMac
|
||||
PlatformLinux
|
||||
PlatformiPad
|
||||
PlatformiPhone
|
||||
PlatformiPod
|
||||
PlatformBlackberry
|
||||
PlatformWindowsPhone
|
||||
PlatformPlaystation
|
||||
PlatformXbox
|
||||
PlatformNintendo
|
||||
PlatformBot
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
Major int
|
||||
Minor int
|
||||
Patch int
|
||||
}
|
||||
|
||||
func (v Version) Less(c Version) bool {
|
||||
if v.Major < c.Major {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Major > c.Major {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.Minor < c.Minor {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Minor > c.Minor {
|
||||
return false
|
||||
}
|
||||
|
||||
return v.Patch < c.Patch
|
||||
}
|
||||
|
||||
type UserAgent struct {
|
||||
Browser Browser
|
||||
OS OS
|
||||
DeviceType DeviceType
|
||||
}
|
||||
|
||||
type Browser struct {
|
||||
Name BrowserName
|
||||
Version Version
|
||||
}
|
||||
|
||||
type OS struct {
|
||||
Platform Platform
|
||||
Name OSName
|
||||
Version Version
|
||||
}
|
||||
|
||||
// Reset resets the UserAgent to it's zero value
|
||||
func (ua *UserAgent) Reset() {
|
||||
ua.Browser = Browser{}
|
||||
ua.OS = OS{}
|
||||
ua.DeviceType = DeviceUnknown
|
||||
}
|
||||
|
||||
// Parse accepts a raw user agent (string) and returns the UserAgent.
|
||||
func Parse(ua string) *UserAgent {
|
||||
dest := new(UserAgent)
|
||||
parse(ua, dest)
|
||||
return dest
|
||||
}
|
||||
|
||||
// ParseUserAgent is the same as Parse, but populates the supplied UserAgent.
|
||||
// It is the caller's responsibility to call Reset() on the UserAgent before
|
||||
// passing it to this function.
|
||||
func ParseUserAgent(ua string, dest *UserAgent) {
|
||||
parse(ua, dest)
|
||||
}
|
||||
|
||||
func parse(ua string, dest *UserAgent) {
|
||||
ua = normalise(ua)
|
||||
switch {
|
||||
case len(ua) == 0:
|
||||
dest.OS.Platform = PlatformUnknown
|
||||
dest.OS.Name = OSUnknown
|
||||
dest.Browser.Name = BrowserUnknown
|
||||
dest.DeviceType = DeviceUnknown
|
||||
|
||||
// stop on on first case returning true
|
||||
case dest.evalOS(ua):
|
||||
case dest.evalBrowserName(ua):
|
||||
default:
|
||||
dest.evalBrowserVersion(ua)
|
||||
dest.evalDevice(ua)
|
||||
}
|
||||
}
|
||||
|
||||
// normalise normalises the user supplied agent string so that
|
||||
// we can more easily parse it.
|
||||
func normalise(ua string) string {
|
||||
if len(ua) <= 1024 {
|
||||
var buf [1024]byte
|
||||
ascii := copyLower(buf[:len(ua)], ua)
|
||||
if !ascii {
|
||||
// Fall back for non ascii characters
|
||||
return strings.ToLower(ua)
|
||||
}
|
||||
return string(buf[:len(ua)])
|
||||
}
|
||||
// Fallback for unusually long strings
|
||||
return strings.ToLower(ua)
|
||||
}
|
||||
|
||||
// copyLower copies a lowercase version of s to b. It assumes s contains only single byte characters
|
||||
// and will panic if b is nil or is not long enough to contain all the bytes from s.
|
||||
// It returns early with false if any characters were non ascii.
|
||||
func copyLower(b []byte, s string) bool {
|
||||
for j := 0; j < len(s); j++ {
|
||||
c := s[j]
|
||||
if c > 127 {
|
||||
return false
|
||||
}
|
||||
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
c += 'a' - 'A'
|
||||
}
|
||||
|
||||
b[j] = c
|
||||
}
|
||||
return true
|
||||
}
|
||||
1064
vendor/github.com/avct/uasurfer/uasurfer_test.go
generated
vendored
1064
vendor/github.com/avct/uasurfer/uasurfer_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
14
vendor/github.com/cpanato/html2text/.travis.yml
generated
vendored
14
vendor/github.com/cpanato/html2text/.travis.yml
generated
vendored
@@ -1,14 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- tip
|
||||
- 1.8
|
||||
- 1.7
|
||||
- 1.6
|
||||
- 1.5
|
||||
- 1.4
|
||||
- 1.3
|
||||
- 1.2
|
||||
notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
108
vendor/github.com/cpanato/html2text/README.md
generated
vendored
108
vendor/github.com/cpanato/html2text/README.md
generated
vendored
@@ -1,108 +0,0 @@
|
||||
# html2text
|
||||
|
||||
[](https://godoc.org/github.com/cpanato/html2text)
|
||||
[](https://travis-ci.org/cpanato/html2text)
|
||||
[](https://goreportcard.com/report/github.com/cpanato/html2text)
|
||||
|
||||
### Initial information
|
||||
This project was forked from [github.com/jaytaylor/html2text](https://github.com/jaytaylor/html2text) in order to use another clean bom library due the original one has no license.
|
||||
|
||||
|
||||
### Converts HTML into text
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
Ensure your emails are readable by all!
|
||||
|
||||
Turns HTML into raw text, useful for sending fancy HTML emails with a equivalently nicely formatted TXT document as a fallback (e.g. for people who don't allow HTML emails or have other display issues).
|
||||
|
||||
html2text is a simple golang package for rendering HTML into plaintext.
|
||||
|
||||
There are still lots of improvements to be had, but FWIW this has worked fine for my [basic] HTML-2-text needs.
|
||||
|
||||
It requires go 1.x or newer ;)
|
||||
|
||||
|
||||
## Download the package
|
||||
|
||||
```bash
|
||||
go get github.com/cpanato/html2text
|
||||
```
|
||||
|
||||
## Example usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cpanato/html2text"
|
||||
)
|
||||
|
||||
func main() {
|
||||
inputHtml := `
|
||||
<html>
|
||||
<head>
|
||||
<title>My Mega Service</title>
|
||||
<link rel=\"stylesheet\" href=\"main.css\">
|
||||
<style type=\"text/css\">body { color: #fff; }</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="logo">
|
||||
<a href="http://mymegaservice.com/"><img src="/logo-image.jpg" alt="Mega Service"/></a>
|
||||
</div>
|
||||
|
||||
<h1>Welcome to your new account on my service!</h1>
|
||||
|
||||
<p>
|
||||
Here is some more information:
|
||||
|
||||
<ul>
|
||||
<li>Link 1: <a href="https://example.com">Example.com</a></li>
|
||||
<li>Link 2: <a href="https://example2.com">Example2.com</a></li>
|
||||
<li>Something else</li>
|
||||
</ul>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
text, err := html2text.FromString(inputHtml)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(text)
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Mega Service ( http://mymegaservice.com/ )
|
||||
|
||||
******************************************
|
||||
Welcome to your new account on my service!
|
||||
******************************************
|
||||
|
||||
Here is some more information:
|
||||
|
||||
* Link 1: Example.com ( https://example.com )
|
||||
* Link 2: Example2.com ( https://example2.com )
|
||||
* Something else
|
||||
```
|
||||
|
||||
|
||||
## Unit-tests
|
||||
|
||||
Running the unit-tests is straightforward and standard:
|
||||
|
||||
```bash
|
||||
go test
|
||||
```
|
||||
|
||||
|
||||
# License
|
||||
|
||||
Permissive MIT license.
|
||||
312
vendor/github.com/cpanato/html2text/html2text.go
generated
vendored
312
vendor/github.com/cpanato/html2text/html2text.go
generated
vendored
@@ -1,312 +0,0 @@
|
||||
package html2text
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/dimchansky/utfbom"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"golang.org/x/net/html/atom"
|
||||
)
|
||||
|
||||
var (
|
||||
spacingRe = regexp.MustCompile(`[ \r\n\t]+`)
|
||||
newlineRe = regexp.MustCompile(`\n\n+`)
|
||||
)
|
||||
|
||||
type textifyTraverseCtx struct {
|
||||
Buf bytes.Buffer
|
||||
|
||||
prefix string
|
||||
blockquoteLevel int
|
||||
lineLength int
|
||||
endsWithSpace bool
|
||||
endsWithNewline bool
|
||||
justClosedDiv bool
|
||||
}
|
||||
|
||||
func (ctx *textifyTraverseCtx) traverse(node *html.Node) error {
|
||||
switch node.Type {
|
||||
default:
|
||||
return ctx.traverseChildren(node)
|
||||
|
||||
case html.TextNode:
|
||||
data := strings.Trim(spacingRe.ReplaceAllString(node.Data, " "), " ")
|
||||
return ctx.emit(data)
|
||||
|
||||
case html.ElementNode:
|
||||
return ctx.handleElementNode(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *textifyTraverseCtx) handleElementNode(node *html.Node) error {
|
||||
ctx.justClosedDiv = false
|
||||
switch node.DataAtom {
|
||||
case atom.Br:
|
||||
return ctx.emit("\n")
|
||||
|
||||
case atom.H1, atom.H2, atom.H3:
|
||||
subCtx := textifyTraverseCtx{}
|
||||
if err := subCtx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str := subCtx.Buf.String()
|
||||
dividerLen := 0
|
||||
for _, line := range strings.Split(str, "\n") {
|
||||
if lineLen := len([]rune(line)); lineLen-1 > dividerLen {
|
||||
dividerLen = lineLen - 1
|
||||
}
|
||||
}
|
||||
divider := ""
|
||||
if node.DataAtom == atom.H1 {
|
||||
divider = strings.Repeat("*", dividerLen)
|
||||
} else {
|
||||
divider = strings.Repeat("-", dividerLen)
|
||||
}
|
||||
|
||||
if node.DataAtom == atom.H3 {
|
||||
return ctx.emit("\n\n" + str + "\n" + divider + "\n\n")
|
||||
}
|
||||
return ctx.emit("\n\n" + divider + "\n" + str + "\n" + divider + "\n\n")
|
||||
|
||||
case atom.Blockquote:
|
||||
ctx.blockquoteLevel++
|
||||
ctx.prefix = strings.Repeat(">", ctx.blockquoteLevel) + " "
|
||||
if err := ctx.emit("\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.blockquoteLevel == 1 {
|
||||
if err := ctx.emit("\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.blockquoteLevel--
|
||||
ctx.prefix = strings.Repeat(">", ctx.blockquoteLevel)
|
||||
if ctx.blockquoteLevel > 0 {
|
||||
ctx.prefix += " "
|
||||
}
|
||||
return ctx.emit("\n\n")
|
||||
|
||||
case atom.Div:
|
||||
if ctx.lineLength > 0 {
|
||||
if err := ctx.emit("\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
if ctx.justClosedDiv == false {
|
||||
err = ctx.emit("\n")
|
||||
}
|
||||
ctx.justClosedDiv = true
|
||||
return err
|
||||
|
||||
case atom.Li:
|
||||
if err := ctx.emit("* "); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.emit("\n")
|
||||
|
||||
case atom.B, atom.Strong:
|
||||
subCtx := textifyTraverseCtx{}
|
||||
subCtx.endsWithSpace = true
|
||||
if err := subCtx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
str := subCtx.Buf.String()
|
||||
return ctx.emit("*" + str + "*")
|
||||
|
||||
case atom.A:
|
||||
// If image is the only child, take its alt text as the link text
|
||||
if img := node.FirstChild; img != nil && node.LastChild == img && img.DataAtom == atom.Img {
|
||||
if altText := getAttrVal(img, "alt"); altText != "" {
|
||||
ctx.emit(altText)
|
||||
}
|
||||
} else if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hrefLink := ""
|
||||
if attrVal := getAttrVal(node, "href"); attrVal != "" {
|
||||
attrVal = ctx.normalizeHrefLink(attrVal)
|
||||
if attrVal != "" {
|
||||
hrefLink = "( " + attrVal + " )"
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.emit(hrefLink)
|
||||
|
||||
case atom.P, atom.Ul, atom.Table:
|
||||
if err := ctx.emit("\n\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.emit("\n\n")
|
||||
|
||||
case atom.Tr:
|
||||
if err := ctx.traverseChildren(node); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.emit("\n")
|
||||
|
||||
case atom.Style, atom.Script, atom.Head:
|
||||
// Ignore the subtree
|
||||
return nil
|
||||
|
||||
default:
|
||||
return ctx.traverseChildren(node)
|
||||
}
|
||||
}
|
||||
func (ctx *textifyTraverseCtx) traverseChildren(node *html.Node) error {
|
||||
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
||||
if err := ctx.traverse(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *textifyTraverseCtx) emit(data string) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
lines := ctx.breakLongLines(data)
|
||||
var err error
|
||||
for _, line := range lines {
|
||||
runes := []rune(line)
|
||||
startsWithSpace := unicode.IsSpace(runes[0])
|
||||
if !startsWithSpace && !ctx.endsWithSpace {
|
||||
ctx.Buf.WriteByte(' ')
|
||||
ctx.lineLength++
|
||||
}
|
||||
ctx.endsWithSpace = unicode.IsSpace(runes[len(runes)-1])
|
||||
for _, c := range line {
|
||||
_, err = ctx.Buf.WriteString(string(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.lineLength++
|
||||
if c == '\n' {
|
||||
ctx.lineLength = 0
|
||||
if ctx.prefix != "" {
|
||||
_, err = ctx.Buf.WriteString(ctx.prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *textifyTraverseCtx) breakLongLines(data string) []string {
|
||||
// only break lines when we are in blockquotes
|
||||
if ctx.blockquoteLevel == 0 {
|
||||
return []string{data}
|
||||
}
|
||||
var ret []string
|
||||
runes := []rune(data)
|
||||
l := len(runes)
|
||||
existing := ctx.lineLength
|
||||
if existing >= 74 {
|
||||
ret = append(ret, "\n")
|
||||
existing = 0
|
||||
}
|
||||
for l+existing > 74 {
|
||||
i := 74 - existing
|
||||
for i >= 0 && !unicode.IsSpace(runes[i]) {
|
||||
i--
|
||||
}
|
||||
if i == -1 {
|
||||
// no spaces, so go the other way
|
||||
i = 74 - existing
|
||||
for i < l && !unicode.IsSpace(runes[i]) {
|
||||
i++
|
||||
}
|
||||
}
|
||||
ret = append(ret, string(runes[:i])+"\n")
|
||||
for i < l && unicode.IsSpace(runes[i]) {
|
||||
i++
|
||||
}
|
||||
runes = runes[i:]
|
||||
l = len(runes)
|
||||
existing = 0
|
||||
}
|
||||
if len(runes) > 0 {
|
||||
ret = append(ret, string(runes))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ctx *textifyTraverseCtx) normalizeHrefLink(link string) string {
|
||||
link = strings.TrimSpace(link)
|
||||
link = strings.TrimPrefix(link, "mailto:")
|
||||
return link
|
||||
}
|
||||
|
||||
func getAttrVal(node *html.Node, attrName string) string {
|
||||
for _, attr := range node.Attr {
|
||||
if attr.Key == attrName {
|
||||
return attr.Val
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func FromHtmlNode(doc *html.Node) (string, error) {
|
||||
ctx := textifyTraverseCtx{
|
||||
Buf: bytes.Buffer{},
|
||||
}
|
||||
if err := ctx.traverse(doc); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
text := strings.TrimSpace(newlineRe.ReplaceAllString(
|
||||
strings.Replace(ctx.Buf.String(), "\n ", "\n", -1), "\n\n"))
|
||||
return text, nil
|
||||
|
||||
}
|
||||
|
||||
func FromReader(reader io.Reader) (string, error) {
|
||||
bs, err := ioutil.ReadAll(reader)
|
||||
newReader, _ := utfbom.Skip(bytes.NewReader(bs))
|
||||
|
||||
doc, err := html.Parse(newReader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return FromHtmlNode(doc)
|
||||
}
|
||||
|
||||
func FromString(input string) (string, error) {
|
||||
bs := utfbom.SkipOnly(bytes.NewReader([]byte(input)))
|
||||
text, err := FromReader(bs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return text, nil
|
||||
}
|
||||
674
vendor/github.com/cpanato/html2text/html2text_test.go
generated
vendored
674
vendor/github.com/cpanato/html2text/html2text_test.go
generated
vendored
@@ -1,674 +0,0 @@
|
||||
package html2text
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
destPath = "testdata"
|
||||
)
|
||||
|
||||
func TestParseUTF8(t *testing.T) {
|
||||
htmlFiles := []struct {
|
||||
file string
|
||||
keywordShouldNotExist string
|
||||
keywordShouldExist string
|
||||
}{
|
||||
{
|
||||
"utf8.html",
|
||||
"学习之道:美国公认学习第一书title",
|
||||
"次世界冠军赛上,我几近疯狂",
|
||||
},
|
||||
{
|
||||
"utf8_with_bom.xhtml",
|
||||
"1892年波兰文版序言title",
|
||||
"种新的波兰文本已成为必要",
|
||||
},
|
||||
}
|
||||
|
||||
for _, htmlFile := range htmlFiles {
|
||||
bs, err := ioutil.ReadFile(path.Join(destPath, htmlFile.file))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
text, err := FromReader(bytes.NewReader(bs))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(text, htmlFile.keywordShouldExist) {
|
||||
t.Fatalf("keyword %s should exists in file %s", htmlFile.keywordShouldExist, htmlFile.file)
|
||||
}
|
||||
if strings.Contains(text, htmlFile.keywordShouldNotExist) {
|
||||
t.Fatalf("keyword %s should not exists in file %s", htmlFile.keywordShouldNotExist, htmlFile.file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrippingWhitespace(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"test text",
|
||||
"test text",
|
||||
},
|
||||
{
|
||||
" \ttext\ntext\n",
|
||||
"text text",
|
||||
},
|
||||
{
|
||||
" \na \n\t \n \n a \t",
|
||||
"a a",
|
||||
},
|
||||
{
|
||||
"test text",
|
||||
"test text",
|
||||
},
|
||||
{
|
||||
"test text ",
|
||||
"test text",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParagraphsAndBreaks(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"Test text",
|
||||
"Test text",
|
||||
},
|
||||
{
|
||||
"Test text<br>",
|
||||
"Test text",
|
||||
},
|
||||
{
|
||||
"Test text<br>Test",
|
||||
"Test text\nTest",
|
||||
},
|
||||
{
|
||||
"<p>Test text</p>",
|
||||
"Test text",
|
||||
},
|
||||
{
|
||||
"<p>Test text</p><p>Test text</p>",
|
||||
"Test text\n\nTest text",
|
||||
},
|
||||
{
|
||||
"\n<p>Test text</p>\n\n\n\t<p>Test text</p>\n",
|
||||
"Test text\n\nTest text",
|
||||
},
|
||||
{
|
||||
"\n<p>Test text<br/>Test text</p>\n",
|
||||
"Test text\nTest text",
|
||||
},
|
||||
{
|
||||
"\n<p>Test text<br> \tTest text<br></p>\n",
|
||||
"Test text\nTest text",
|
||||
},
|
||||
{
|
||||
"Test text<br><BR />Test text",
|
||||
"Test text\n\nTest text",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTables(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"<table><tr><td></td><td></td></tr></table>",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"<table><tr><td>cell1</td><td>cell2</td></tr></table>",
|
||||
"cell1 cell2",
|
||||
},
|
||||
{
|
||||
"<table><tr><td>row1</td></tr><tr><td>row2</td></tr></table>",
|
||||
"row1\nrow2",
|
||||
},
|
||||
{
|
||||
`<table>
|
||||
<tr><td>cell1-1</td><td>cell1-2</td></tr>
|
||||
<tr><td>cell2-1</td><td>cell2-2</td></tr>
|
||||
</table>`,
|
||||
"cell1-1 cell1-2\ncell2-1 cell2-2",
|
||||
},
|
||||
{
|
||||
"_<table><tr><td>cell</td></tr></table>_",
|
||||
"_\n\ncell\n\n_",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrippingLists(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"<ul></ul>",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"<ul><li>item</li></ul>_",
|
||||
"* item\n\n_",
|
||||
},
|
||||
{
|
||||
"<li class='123'>item 1</li> <li>item 2</li>\n_",
|
||||
"* item 1\n* item 2\n_",
|
||||
},
|
||||
{
|
||||
"<li>item 1</li> \t\n <li>item 2</li> <li> item 3</li>\n_",
|
||||
"* item 1\n* item 2\n* item 3\n_",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinks(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
`<a></a>`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
`<a href=""></a>`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
`<a href="http://example.com/"></a>`,
|
||||
`( http://example.com/ )`,
|
||||
},
|
||||
{
|
||||
`<a href="">Link</a>`,
|
||||
`Link`,
|
||||
},
|
||||
{
|
||||
`<a href="http://example.com/">Link</a>`,
|
||||
`Link ( http://example.com/ )`,
|
||||
},
|
||||
{
|
||||
`<a href="http://example.com/"><span class="a">Link</span></a>`,
|
||||
`Link ( http://example.com/ )`,
|
||||
},
|
||||
{
|
||||
"<a href='http://example.com/'>\n\t<span class='a'>Link</span>\n\t</a>",
|
||||
`Link ( http://example.com/ )`,
|
||||
},
|
||||
{
|
||||
"<a href='mailto:contact@example.org'>Contact Us</a>",
|
||||
`Contact Us ( contact@example.org )`,
|
||||
},
|
||||
{
|
||||
"<a href=\"http://example.com:80/~user?aaa=bb&c=d,e,f#foo\">Link</a>",
|
||||
`Link ( http://example.com:80/~user?aaa=bb&c=d,e,f#foo )`,
|
||||
},
|
||||
{
|
||||
"<a title='title' href=\"http://example.com/\">Link</a>",
|
||||
`Link ( http://example.com/ )`,
|
||||
},
|
||||
{
|
||||
"<a href=\" http://example.com/ \"> Link </a>",
|
||||
`Link ( http://example.com/ )`,
|
||||
},
|
||||
{
|
||||
"<a href=\"http://example.com/a/\">Link A</a> <a href=\"http://example.com/b/\">Link B</a>",
|
||||
`Link A ( http://example.com/a/ ) Link B ( http://example.com/b/ )`,
|
||||
},
|
||||
{
|
||||
"<a href=\"%%LINK%%\">Link</a>",
|
||||
`Link ( %%LINK%% )`,
|
||||
},
|
||||
{
|
||||
"<a href=\"[LINK]\">Link</a>",
|
||||
`Link ( [LINK] )`,
|
||||
},
|
||||
{
|
||||
"<a href=\"{LINK}\">Link</a>",
|
||||
`Link ( {LINK} )`,
|
||||
},
|
||||
{
|
||||
"<a href=\"[[!unsubscribe]]\">Link</a>",
|
||||
`Link ( [[!unsubscribe]] )`,
|
||||
},
|
||||
{
|
||||
"<p>This is <a href=\"http://www.google.com\" >link1</a> and <a href=\"http://www.google.com\" >link2 </a> is next.</p>",
|
||||
`This is link1 ( http://www.google.com ) and link2 ( http://www.google.com ) is next.`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageAltTags(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
`<img />`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
`<img src="http://example.ru/hello.jpg" />`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
`<img alt="Example"/>`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
`<img src="http://example.ru/hello.jpg" alt="Example"/>`,
|
||||
``,
|
||||
},
|
||||
// Images do matter if they are in a link
|
||||
{
|
||||
`<a href="http://example.com/"><img src="http://example.ru/hello.jpg" alt="Example"/></a>`,
|
||||
`Example ( http://example.com/ )`,
|
||||
},
|
||||
{
|
||||
`<a href="http://example.com/"><img src="http://example.ru/hello.jpg" alt="Example"></a>`,
|
||||
`Example ( http://example.com/ )`,
|
||||
},
|
||||
{
|
||||
`<a href='http://example.com/'><img src='http://example.ru/hello.jpg' alt='Example'/></a>`,
|
||||
`Example ( http://example.com/ )`,
|
||||
},
|
||||
{
|
||||
`<a href='http://example.com/'><img src='http://example.ru/hello.jpg' alt='Example'></a>`,
|
||||
`Example ( http://example.com/ )`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeadings(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"<h1>Test</h1>",
|
||||
"****\nTest\n****",
|
||||
},
|
||||
{
|
||||
"\t<h1>\nTest</h1> ",
|
||||
"****\nTest\n****",
|
||||
},
|
||||
{
|
||||
"\t<h1>\nTest line 1<br>Test 2</h1> ",
|
||||
"***********\nTest line 1\nTest 2\n***********",
|
||||
},
|
||||
{
|
||||
"<h1>Test</h1> <h1>Test</h1>",
|
||||
"****\nTest\n****\n\n****\nTest\n****",
|
||||
},
|
||||
{
|
||||
"<h2>Test</h2>",
|
||||
"----\nTest\n----",
|
||||
},
|
||||
{
|
||||
"<h1><a href='http://example.com/'>Test</a></h1>",
|
||||
"****************************\nTest ( http://example.com/ )\n****************************",
|
||||
},
|
||||
{
|
||||
"<h3> <span class='a'>Test </span></h3>",
|
||||
"Test\n----",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBold(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"<b>Test</b>",
|
||||
"*Test*",
|
||||
},
|
||||
{
|
||||
"\t<b>Test</b> ",
|
||||
"*Test*",
|
||||
},
|
||||
{
|
||||
"\t<b>Test line 1<br>Test 2</b> ",
|
||||
"*Test line 1\nTest 2*",
|
||||
},
|
||||
{
|
||||
"<b>Test</b> <b>Test</b>",
|
||||
"*Test* *Test*",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDiv(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"<div>Test</div>",
|
||||
"Test",
|
||||
},
|
||||
{
|
||||
"\t<div>Test</div> ",
|
||||
"Test",
|
||||
},
|
||||
{
|
||||
"<div>Test line 1<div>Test 2</div></div>",
|
||||
"Test line 1\nTest 2",
|
||||
},
|
||||
{
|
||||
"Test 1<div>Test 2</div> <div>Test 3</div>Test 4",
|
||||
"Test 1\nTest 2\nTest 3\nTest 4",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBlockquotes(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"<div>level 0<blockquote>level 1<br><blockquote>level 2</blockquote>level 1</blockquote><div>level 0</div></div>",
|
||||
"level 0\n> \n> level 1\n> \n>> level 2\n> \n> level 1\n\nlevel 0",
|
||||
},
|
||||
{
|
||||
"<blockquote>Test</blockquote>Test",
|
||||
"> \n> Test\n\nTest",
|
||||
},
|
||||
{
|
||||
"\t<blockquote> \nTest<br></blockquote> ",
|
||||
"> \n> Test\n>",
|
||||
},
|
||||
{
|
||||
"\t<blockquote> \nTest line 1<br>Test 2</blockquote> ",
|
||||
"> \n> Test line 1\n> Test 2",
|
||||
},
|
||||
{
|
||||
"<blockquote>Test</blockquote> <blockquote>Test</blockquote> Other Test",
|
||||
"> \n> Test\n\n> \n> Test\n\nOther Test",
|
||||
},
|
||||
{
|
||||
"<blockquote>Lorem ipsum Commodo id consectetur pariatur ea occaecat minim aliqua ad sit consequat quis ex commodo Duis incididunt eu mollit consectetur fugiat voluptate dolore in pariatur in commodo occaecat Ut occaecat velit esse labore aute quis commodo non sit dolore officia Excepteur cillum amet cupidatat culpa velit labore ullamco dolore mollit elit in aliqua dolor irure do</blockquote>",
|
||||
"> \n> Lorem ipsum Commodo id consectetur pariatur ea occaecat minim aliqua ad\n> sit consequat quis ex commodo Duis incididunt eu mollit consectetur fugiat\n> voluptate dolore in pariatur in commodo occaecat Ut occaecat velit esse\n> labore aute quis commodo non sit dolore officia Excepteur cillum amet\n> cupidatat culpa velit labore ullamco dolore mollit elit in aliqua dolor\n> irure do",
|
||||
},
|
||||
{
|
||||
"<blockquote>Lorem<b>ipsum</b><b>Commodo</b><b>id</b><b>consectetur</b><b>pariatur</b><b>ea</b><b>occaecat</b><b>minim</b><b>aliqua</b><b>ad</b><b>sit</b><b>consequat</b><b>quis</b><b>ex</b><b>commodo</b><b>Duis</b><b>incididunt</b><b>eu</b><b>mollit</b><b>consectetur</b><b>fugiat</b><b>voluptate</b><b>dolore</b><b>in</b><b>pariatur</b><b>in</b><b>commodo</b><b>occaecat</b><b>Ut</b><b>occaecat</b><b>velit</b><b>esse</b><b>labore</b><b>aute</b><b>quis</b><b>commodo</b><b>non</b><b>sit</b><b>dolore</b><b>officia</b><b>Excepteur</b><b>cillum</b><b>amet</b><b>cupidatat</b><b>culpa</b><b>velit</b><b>labore</b><b>ullamco</b><b>dolore</b><b>mollit</b><b>elit</b><b>in</b><b>aliqua</b><b>dolor</b><b>irure</b><b>do</b></blockquote>",
|
||||
"> \n> Lorem *ipsum* *Commodo* *id* *consectetur* *pariatur* *ea* *occaecat* *minim*\n> *aliqua* *ad* *sit* *consequat* *quis* *ex* *commodo* *Duis* *incididunt* *eu*\n> *mollit* *consectetur* *fugiat* *voluptate* *dolore* *in* *pariatur* *in* *commodo*\n> *occaecat* *Ut* *occaecat* *velit* *esse* *labore* *aute* *quis* *commodo*\n> *non* *sit* *dolore* *officia* *Excepteur* *cillum* *amet* *cupidatat* *culpa*\n> *velit* *labore* *ullamco* *dolore* *mollit* *elit* *in* *aliqua* *dolor* *irure*\n> *do*",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIgnoreStylesScriptsHead(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"<style>Test</style>",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"<style type=\"text/css\">body { color: #fff; }</style>",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"<link rel=\"stylesheet\" href=\"main.css\">",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"<script>Test</script>",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"<script src=\"main.js\"></script>",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"<script type=\"text/javascript\" src=\"main.js\"></script>",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"<script type=\"text/javascript\">Test</script>",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"<script type=\"text/ng-template\" id=\"template.html\"><a href=\"http://google.com\">Google</a></script>",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"<script type=\"bla-bla-bla\" id=\"template.html\">Test</script>",
|
||||
"",
|
||||
},
|
||||
{
|
||||
`<html><head><title>Title</title></head><body></body></html>`,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertString(t, testCase.input, testCase.output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestText(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expr string
|
||||
}{
|
||||
{
|
||||
`<li>
|
||||
<a href="/new" data-ga-click="Header, create new repository, icon:repo"><span class="octicon octicon-repo"></span> New repository</a>
|
||||
</li>`,
|
||||
`\* New repository \( /new \)`,
|
||||
},
|
||||
{
|
||||
`hi
|
||||
|
||||
<br>
|
||||
|
||||
hello <a href="https://google.com">google</a>
|
||||
<br><br>
|
||||
test<p>List:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="foo">Foo</a></li>
|
||||
<li><a href="http://www.microshwhat.com/bar/soapy">Barsoap</a></li>
|
||||
<li>Baz</li>
|
||||
</ul>
|
||||
`,
|
||||
`hi
|
||||
hello google \( https://google.com \)
|
||||
|
||||
test
|
||||
|
||||
List:
|
||||
|
||||
\* Foo \( foo \)
|
||||
\* Barsoap \( http://www.microshwhat.com/bar/soapy \)
|
||||
\* Baz`,
|
||||
},
|
||||
// Malformed input html.
|
||||
{
|
||||
`hi
|
||||
|
||||
hello <a href="https://google.com">google</a>
|
||||
|
||||
test<p>List:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="foo">Foo</a>
|
||||
<li><a href="/
|
||||
bar/baz">Bar</a>
|
||||
<li>Baz</li>
|
||||
</ul>
|
||||
`,
|
||||
`hi hello google \( https://google.com \) test
|
||||
|
||||
List:
|
||||
|
||||
\* Foo \( foo \)
|
||||
\* Bar \( /\n[ \t]+bar/baz \)
|
||||
\* Baz`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
assertRegexp(t, testCase.input, testCase.expr)
|
||||
}
|
||||
}
|
||||
|
||||
type StringMatcher interface {
|
||||
MatchString(string) bool
|
||||
String() string
|
||||
}
|
||||
|
||||
type RegexpStringMatcher string
|
||||
|
||||
func (m RegexpStringMatcher) MatchString(str string) bool {
|
||||
return regexp.MustCompile(string(m)).MatchString(str)
|
||||
}
|
||||
func (m RegexpStringMatcher) String() string {
|
||||
return string(m)
|
||||
}
|
||||
|
||||
type ExactStringMatcher string
|
||||
|
||||
func (m ExactStringMatcher) MatchString(str string) bool {
|
||||
return string(m) == str
|
||||
}
|
||||
func (m ExactStringMatcher) String() string {
|
||||
return string(m)
|
||||
}
|
||||
|
||||
func assertRegexp(t *testing.T, input string, outputRE string) {
|
||||
assertPlaintext(t, input, RegexpStringMatcher(outputRE))
|
||||
}
|
||||
|
||||
func assertString(t *testing.T, input string, output string) {
|
||||
assertPlaintext(t, input, ExactStringMatcher(output))
|
||||
}
|
||||
|
||||
func assertPlaintext(t *testing.T, input string, matcher StringMatcher) {
|
||||
text, err := FromString(input)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !matcher.MatchString(text) {
|
||||
t.Errorf("Input did not match expression\n"+
|
||||
"Input:\n>>>>\n%s\n<<<<\n\n"+
|
||||
"Output:\n>>>>\n%s\n<<<<\n\n"+
|
||||
"Expected output:\n>>>>\n%s\n<<<<\n\n",
|
||||
input, text, matcher.String())
|
||||
} else {
|
||||
t.Logf("input:\n\n%s\n\n\n\noutput:\n\n%s\n", input, text)
|
||||
}
|
||||
}
|
||||
|
||||
func Example() {
|
||||
inputHtml := `
|
||||
<html>
|
||||
<head>
|
||||
<title>My Mega Service</title>
|
||||
<link rel=\"stylesheet\" href=\"main.css\">
|
||||
<style type=\"text/css\">body { color: #fff; }</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="logo">
|
||||
<a href="http://mymegaservice.com/"><img src="/logo-image.jpg" alt="Mega Service"/></a>
|
||||
</div>
|
||||
|
||||
<h1>Welcome to your new account on my service!</h1>
|
||||
|
||||
<p>
|
||||
Here is some more information:
|
||||
|
||||
<ul>
|
||||
<li>Link 1: <a href="https://example.com">Example.com</a></li>
|
||||
<li>Link 2: <a href="https://example2.com">Example2.com</a></li>
|
||||
<li>Something else</li>
|
||||
</ul>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
text, err := FromString(inputHtml)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(text)
|
||||
|
||||
// Output:
|
||||
// Mega Service ( http://mymegaservice.com/ )
|
||||
//
|
||||
// ******************************************
|
||||
// Welcome to your new account on my service!
|
||||
// ******************************************
|
||||
//
|
||||
// Here is some more information:
|
||||
//
|
||||
// * Link 1: Example.com ( https://example.com )
|
||||
// * Link 2: Example2.com ( https://example2.com )
|
||||
// * Something else
|
||||
}
|
||||
22
vendor/github.com/cpanato/html2text/testdata/utf8.html
generated
vendored
22
vendor/github.com/cpanato/html2text/testdata/utf8.html
generated
vendored
@@ -1,22 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>学习之道:美国公认学习第一书title</title>
|
||||
<link href="stylesheet.css" rel="stylesheet" type="text/css" />
|
||||
<link href="page_styles.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body class="calibre">
|
||||
<p id="filepos9452" class="calibre_"><span class="calibre6"><span class="bold">写在前面的话</span></span>
|
||||
</p>
|
||||
<p class="calibre_12">在台湾的那次世界冠军赛上,我几近疯狂,直至两年后的今天,我仍沉浸在这次的经历中。这是我生平第一次如此深入地审视我自己,甚至是第一次尝试审视自己。这个过程令人很是兴奋,同时也有点感觉怪异。我重新认识了自我,看到了自己的另外一面,自己从未发觉的另外一面。为了生存,为了取胜,我成了一名角斗士,彻头彻尾,简单纯粹。我并没有意识到这一角色早已在我的心中生根发芽,呼之欲出。也许,他的出现已是不可避免。</p>
|
||||
<p class="calibre_7">而我这全新的一面,与我一直熟识的那个乔希,那个曾经害怕黑暗的孩子,那个象棋手,那个狂热于雨水、反复诵读杰克·克鲁亚克作品的年轻人之间,又有什么样的联系呢?这些都是我正在努力弄清楚的问题。</p>
|
||||
<p class="calibre_7">自台湾赛事之后,我急切非常,一心想要回到训练中去,摆脱自己已经达到巅峰的想法。在过去的两年中,我已经重新开始。这是一个新的起点。前方的路还很长,有待进一步的探索。</p>
|
||||
<p class="calibre_7">这本书的创作耗费了相当多的时间和精力。在成长的过程中,我在我的小房间里从未想过等待我的会是这样的战斗。在创作中,我的思想逐渐成熟;爱恋从分崩离析,到失而复得,世界冠军头衔从失之交臂,到囊中取物。如果说在我人生的第一个二十九年中,我学到了什么,那就是,我们永远无法预测结局,无论是重要的比赛、冒险,还是轰轰烈烈的爱情。我们唯一可以肯定的只有,出乎意料。不管我们做了多么万全的准备,在生活的真实场景中,我们总是会处于陌生的境地。我们也许会无法冷静,失去理智,感觉似乎整个世界都在针对我们。在这个时候,我们所要做的是要付出加倍的努力,要表现得比预想得更好。我认为,关键在于准备好随机应变,准备好在所能想象的高压下发挥出创造力。</p>
|
||||
<p class="calibre_7">读者朋友们,我非常希望你们在读过这本书后,可以得到启发,甚至会得到触动,从而能够根据各自的天赋与特长,去实现自己的梦想。这就是我写作此书的目的。我在字里行间所传达的理念曾经使我受益匪浅,我很希望它们可以为大家提供一个基本的框架和方向。如果我的方法言之有理,那么就请接受它,琢磨它,并加之自己的见解。忘记我的那些数字。真正的掌握需要通过自己发现一些最能够引起共鸣的信息,并将其彻底地融合进来,直至成为一体,这样我们才能随心所欲地驾驭它。</p>
|
||||
<div class="mbp_pagebreak" id="calibre_pb_4"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
24
vendor/github.com/cpanato/html2text/testdata/utf8_with_bom.xhtml
generated
vendored
24
vendor/github.com/cpanato/html2text/testdata/utf8_with_bom.xhtml
generated
vendored
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
|
||||
<title>1892年波兰文版序言title</title>
|
||||
<link rel="stylesheet" href="css/stylesheet.css" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page30" />
|
||||
<h2 id="CHP2-6">1892年波兰文版序言<a id="wzyy_18_30" href="#wz_18_30"><sup>[18]</sup></a></h2>
|
||||
<p>出版共产主义宣言的一种新的波兰文本已成为必要,这一事实,引起了许多感想。</p>
|
||||
<p>首先值得注意的是,近来宣言在一定程度上已成为欧洲大陆大工业发展的一种尺度。一个国家的大工业越发展,该国工人中想认清自己作为工人阶级在有产阶级面前所处地位的要求就越增加,他们中间的社会主义运动也越扩大,因而对宣言的需求也越增长。这样,根据宣言用某国文字销行的份数,不仅能够相当确切地断定该国工人运动的状况,而且还能够相当确切地断定该国大工业发展的程度。</p>
|
||||
<p>因此,波兰文的新版本标志着波兰工业的决定性进步。从十年前发表的上一个版本以来确实有了这种进步,对此丝毫不容置疑。俄国的波兰,会议的波兰<a id="wzyy_19_30" href="#wz_19_30"><sup>[19]</sup></a>,成了俄罗斯帝国巨大的工业区。俄国大工业是零星分散的,一部分在芬兰湾沿岸,一部分在中央区(莫斯科和弗拉基米尔),第三部分在黑海和亚速海沿岸,还有另一些散布在别处;而波兰工业则紧缩于相对狭小的地区,享受到由这种积聚引起的长处与短处。这种长处是竞争着的俄罗斯工厂主所承认的,他们要求实行保护关税以对付波兰,尽管他们渴望使波兰人俄罗斯化。这种短处,对波兰工厂主与俄罗斯政府来说,表现在社会主义思想在波兰工人中间的迅速传播和对宣言需求的增长。</p>
|
||||
<p>但是,波兰工业的迅速发展——它超过了俄国工业——本身<a id="page31" />是波兰人民的坚强生命力的一个新证明,是波兰人民临近的民族复兴的一个新保证。而一个独立强盛的波兰的复兴,不只是一件同波兰人有关、而且是同我们大家有关的事情。只有当每个民族在自己内部完全自主时,欧洲各民族间真诚的国际合作才是可能的。1848年革命在无产阶级旗帜下,使无产阶级的战士最终只作了资产阶级的工作,这次革命通过自己遗嘱的执行者路易·波拿巴和俾斯麦也实现了意大利、德国和匈牙利的独立。然而波兰,它从1792年以来为革命做的比所有这三个国家总共做的还要多,而当它1863年失败于强大十倍的俄军的时候,人们却把它抛弃不顾了。贵族既未能保持住、也未能重新争得波兰的独立;今天波兰的独立对资产阶级至少是无所谓的。然而波兰的独立对于欧洲各民族和谐的合作是必需的。这种独立只有年轻的波兰无产阶级才能争得,而且在它的手中会很好地保持住。因为欧洲所有其余的工人都象波兰工人自己一样也需要波兰的独立。</p>
|
||||
<p>弗·恩格斯</p>
|
||||
<p>1892年2月10日于伦敦</p>
|
||||
<div id="page74" />
|
||||
<div><a id="wz_18_30" href="#wzyy_18_30">[18]</a> 恩格斯用德文为《宣言》新的波兰文本写了这篇序言。1892年由波兰社会主义者在伦敦办的《黎明》杂志社出版。序言寄出后,恩格斯写信给门德尔森(1892年2月11日),信中说,他很愿意学会波兰文,并且深入研究波兰工人运动的发展,以便能够为《宣言》的下一版写一篇更详细的序言。——第20页</div>
|
||||
<div><a id="wz_19_30" href="#wzyy_19_30">[19]</a> 指维也纳会议的波兰,即根据1814—1815年维也纳会议的决定,以波兰王国的正式名义割给俄国的那部分波兰土地。——第20页</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
27
vendor/github.com/davecgh/go-spew/.travis.yml
generated
vendored
27
vendor/github.com/davecgh/go-spew/.travis.yml
generated
vendored
@@ -1,14 +1,27 @@
|
||||
language: go
|
||||
go_import_path: github.com/davecgh/go-spew
|
||||
go:
|
||||
- 1.5.4
|
||||
- 1.6.3
|
||||
- 1.7
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- tip
|
||||
sudo: false
|
||||
install:
|
||||
- go get -v golang.org/x/tools/cmd/cover
|
||||
- go get -v github.com/alecthomas/gometalinter
|
||||
- gometalinter --install
|
||||
script:
|
||||
- go test -v -tags=safe ./spew
|
||||
- go test -v -tags=testcgo ./spew -covermode=count -coverprofile=profile.cov
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
- export GORACE="halt_on_error=1"
|
||||
- test -z "$(gometalinter --disable-all
|
||||
--enable=gofmt
|
||||
--enable=golint
|
||||
--enable=vet
|
||||
--enable=gosimple
|
||||
--enable=unconvert
|
||||
--deadline=4m ./spew | tee /dev/stderr)"
|
||||
- go test -v -race -tags safe ./spew
|
||||
- go test -v -race -tags testcgo ./spew -covermode=atomic -coverprofile=profile.cov
|
||||
after_success:
|
||||
- go get -v github.com/mattn/goveralls
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
- goveralls -coverprofile=profile.cov -service=travis-ci
|
||||
|
||||
4
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
4
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
@@ -1,8 +1,8 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2012-2013 Dave Collins <dave@davec.name>
|
||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
|
||||
21
vendor/github.com/davecgh/go-spew/README.md
generated
vendored
21
vendor/github.com/davecgh/go-spew/README.md
generated
vendored
@@ -1,10 +1,9 @@
|
||||
go-spew
|
||||
=======
|
||||
|
||||
[]
|
||||
(https://travis-ci.org/davecgh/go-spew) [![Coverage Status]
|
||||
(https://coveralls.io/repos/davecgh/go-spew/badge.png?branch=master)]
|
||||
(https://coveralls.io/r/davecgh/go-spew?branch=master)
|
||||
[](https://travis-ci.org/davecgh/go-spew)
|
||||
[](http://copyfree.org)
|
||||
[](https://coveralls.io/r/davecgh/go-spew?branch=master)
|
||||
|
||||
Go-spew implements a deep pretty printer for Go data structures to aid in
|
||||
debugging. A comprehensive suite of tests with 100% test coverage is provided
|
||||
@@ -19,8 +18,7 @@ post about it
|
||||
|
||||
## Documentation
|
||||
|
||||
[]
|
||||
(http://godoc.org/github.com/davecgh/go-spew/spew)
|
||||
[](http://godoc.org/github.com/davecgh/go-spew/spew)
|
||||
|
||||
Full `go doc` style documentation for the project can be viewed online without
|
||||
installing this package by using the excellent GoDoc site here:
|
||||
@@ -160,6 +158,15 @@ options. See the ConfigState documentation for more details.
|
||||
App Engine or with the "safe" build tag specified.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
|
||||
* DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of capacities
|
||||
for arrays, slices, maps and channels. This is useful when diffing data
|
||||
structures in tests.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
@@ -191,4 +198,4 @@ using the unsafe package.
|
||||
|
||||
## License
|
||||
|
||||
Go-spew is licensed under the liberal ISC License.
|
||||
Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License.
|
||||
|
||||
8
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
8
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -41,9 +41,9 @@ var (
|
||||
// after commit 82f48826c6c7 which changed the format again to mirror
|
||||
// the original format. Code in the init function updates these offsets
|
||||
// as necessary.
|
||||
offsetPtr = uintptr(ptrSize)
|
||||
offsetPtr = ptrSize
|
||||
offsetScalar = uintptr(0)
|
||||
offsetFlag = uintptr(ptrSize * 2)
|
||||
offsetFlag = ptrSize * 2
|
||||
|
||||
// flagKindWidth and flagKindShift indicate various bits that the
|
||||
// reflect package uses internally to track kind information.
|
||||
@@ -58,7 +58,7 @@ var (
|
||||
// changed their positions. Code in the init function updates these
|
||||
// flags as necessary.
|
||||
flagKindWidth = uintptr(5)
|
||||
flagKindShift = uintptr(flagKindWidth - 1)
|
||||
flagKindShift = flagKindWidth - 1
|
||||
flagRO = uintptr(1 << 0)
|
||||
flagIndir = uintptr(1 << 1)
|
||||
)
|
||||
|
||||
2
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
2
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
4
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
4
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
||||
w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
|
||||
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
|
||||
// prefix to Writer w.
|
||||
func printHexPtr(w io.Writer, p uintptr) {
|
||||
// Null pointer.
|
||||
|
||||
2
vendor/github.com/davecgh/go-spew/spew/common_test.go
generated
vendored
2
vendor/github.com/davecgh/go-spew/spew/common_test.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
11
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
11
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -67,6 +67,15 @@ type ConfigState struct {
|
||||
// Google App Engine or with the "safe" build tag specified.
|
||||
DisablePointerMethods bool
|
||||
|
||||
// DisablePointerAddresses specifies whether to disable the printing of
|
||||
// pointer addresses. This is useful when diffing data structures in tests.
|
||||
DisablePointerAddresses bool
|
||||
|
||||
// DisableCapacities specifies whether to disable the printing of capacities
|
||||
// for arrays, slices, maps and channels. This is useful when diffing
|
||||
// data structures in tests.
|
||||
DisableCapacities bool
|
||||
|
||||
// ContinueOnMethod specifies whether or not recursion should continue once
|
||||
// a custom error or Stringer interface is invoked. The default, false,
|
||||
// means it will print the results of invoking the custom error or Stringer
|
||||
|
||||
11
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
11
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -91,6 +91,15 @@ The following configuration options are available:
|
||||
which only accept pointer receivers from non-pointer variables.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
|
||||
* DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of
|
||||
capacities for arrays, slices, maps and channels. This is useful when
|
||||
diffing data structures in tests.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
||||
18
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
18
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -35,16 +35,16 @@ var (
|
||||
|
||||
// cCharRE is a regular expression that matches a cgo char.
|
||||
// It is used to detect character arrays to hexdump them.
|
||||
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
|
||||
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
|
||||
|
||||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||
// char. It is used to detect unsigned character arrays to hexdump
|
||||
// them.
|
||||
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
|
||||
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
|
||||
|
||||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||
// It is used to detect uint8_t arrays to hexdump them.
|
||||
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
|
||||
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
|
||||
)
|
||||
|
||||
// dumpState contains information about the state of a dump operation.
|
||||
@@ -129,7 +129,7 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||
d.w.Write(closeParenBytes)
|
||||
|
||||
// Display pointer information.
|
||||
if len(pointerChain) > 0 {
|
||||
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
@@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||
// Display dereferenced value.
|
||||
d.w.Write(openParenBytes)
|
||||
switch {
|
||||
case nilFound == true:
|
||||
case nilFound:
|
||||
d.w.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
case cycleFound:
|
||||
d.w.Write(circularBytes)
|
||||
|
||||
default:
|
||||
@@ -282,13 +282,13 @@ func (d *dumpState) dump(v reflect.Value) {
|
||||
case reflect.Map, reflect.String:
|
||||
valueLen = v.Len()
|
||||
}
|
||||
if valueLen != 0 || valueCap != 0 {
|
||||
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
if valueLen != 0 {
|
||||
d.w.Write(lenEqualsBytes)
|
||||
printInt(d.w, int64(valueLen), 10)
|
||||
}
|
||||
if valueCap != 0 {
|
||||
if !d.cs.DisableCapacities && valueCap != 0 {
|
||||
if valueLen != 0 {
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
|
||||
4
vendor/github.com/davecgh/go-spew/spew/dump_test.go
generated
vendored
4
vendor/github.com/davecgh/go-spew/spew/dump_test.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -70,7 +70,7 @@ import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// dumpTest is used to describe a test to be perfomed against the Dump method.
|
||||
// dumpTest is used to describe a test to be performed against the Dump method.
|
||||
type dumpTest struct {
|
||||
in interface{}
|
||||
wants []string
|
||||
|
||||
8
vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
generated
vendored
8
vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -82,18 +82,20 @@ func addCgoDumpTests() {
|
||||
v5Len := fmt.Sprintf("%d", v5l)
|
||||
v5Cap := fmt.Sprintf("%d", v5c)
|
||||
v5t := "[6]testdata._Ctype_uint8_t"
|
||||
v5t2 := "[6]testdata._Ctype_uchar"
|
||||
v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 35 00 " +
|
||||
" |test5.|\n}"
|
||||
addDumpTest(v5, "("+v5t+") "+v5s+"\n")
|
||||
addDumpTest(v5, "("+v5t+") "+v5s+"\n", "("+v5t2+") "+v5s+"\n")
|
||||
|
||||
// C typedefed unsigned char array.
|
||||
v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
|
||||
v6Len := fmt.Sprintf("%d", v6l)
|
||||
v6Cap := fmt.Sprintf("%d", v6c)
|
||||
v6t := "[6]testdata._Ctype_custom_uchar_t"
|
||||
v6t2 := "[6]testdata._Ctype_uchar"
|
||||
v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 36 00 " +
|
||||
" |test6.|\n}"
|
||||
addDumpTest(v6, "("+v6t+") "+v6s+"\n")
|
||||
addDumpTest(v6, "("+v6t+") "+v6s+"\n", "("+v6t2+") "+v6s+"\n")
|
||||
}
|
||||
|
||||
2
vendor/github.com/davecgh/go-spew/spew/example_test.go
generated
vendored
2
vendor/github.com/davecgh/go-spew/spew/example_test.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
6
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
6
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
|
||||
|
||||
// Display dereferenced value.
|
||||
switch {
|
||||
case nilFound == true:
|
||||
case nilFound:
|
||||
f.fs.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
case cycleFound:
|
||||
f.fs.Write(circularShortBytes)
|
||||
|
||||
default:
|
||||
|
||||
8
vendor/github.com/davecgh/go-spew/spew/format_test.go
generated
vendored
8
vendor/github.com/davecgh/go-spew/spew/format_test.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -75,7 +75,7 @@ import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// formatterTest is used to describe a test to be perfomed against NewFormatter.
|
||||
// formatterTest is used to describe a test to be performed against NewFormatter.
|
||||
type formatterTest struct {
|
||||
format string
|
||||
in interface{}
|
||||
@@ -1536,14 +1536,14 @@ func TestPrintSortedKeys(t *testing.T) {
|
||||
t.Errorf("Sorted keys mismatch 3:\n %v %v", s, expected)
|
||||
}
|
||||
|
||||
s = cfg.Sprint(map[testStruct]int{testStruct{1}: 1, testStruct{3}: 3, testStruct{2}: 2})
|
||||
s = cfg.Sprint(map[testStruct]int{{1}: 1, {3}: 3, {2}: 2})
|
||||
expected = "map[ts.1:1 ts.2:2 ts.3:3]"
|
||||
if s != expected {
|
||||
t.Errorf("Sorted keys mismatch 4:\n %v %v", s, expected)
|
||||
}
|
||||
|
||||
if !spew.UnsafeDisabled {
|
||||
s = cfg.Sprint(map[testStructP]int{testStructP{1}: 1, testStructP{3}: 3, testStructP{2}: 2})
|
||||
s = cfg.Sprint(map[testStructP]int{{1}: 1, {3}: 3, {2}: 2})
|
||||
expected = "map[ts.1:1 ts.2:2 ts.3:3]"
|
||||
if s != expected {
|
||||
t.Errorf("Sorted keys mismatch 5:\n %v %v", s, expected)
|
||||
|
||||
7
vendor/github.com/davecgh/go-spew/spew/internal_test.go
generated
vendored
7
vendor/github.com/davecgh/go-spew/spew/internal_test.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -36,10 +36,7 @@ type dummyFmtState struct {
|
||||
}
|
||||
|
||||
func (dfs *dummyFmtState) Flag(f int) bool {
|
||||
if f == int('+') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return f == int('+')
|
||||
}
|
||||
|
||||
func (dfs *dummyFmtState) Precision() (int, bool) {
|
||||
|
||||
2
vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
generated
vendored
2
vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2013-2015 Dave Collins <dave@davec.name>
|
||||
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
2
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
2
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
13
vendor/github.com/davecgh/go-spew/spew/spew_test.go
generated
vendored
13
vendor/github.com/davecgh/go-spew/spew/spew_test.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -130,12 +130,19 @@ func initSpewTests() {
|
||||
scsNoPmethods := &spew.ConfigState{Indent: " ", DisablePointerMethods: true}
|
||||
scsMaxDepth := &spew.ConfigState{Indent: " ", MaxDepth: 1}
|
||||
scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true}
|
||||
scsNoPtrAddr := &spew.ConfigState{DisablePointerAddresses: true}
|
||||
scsNoCap := &spew.ConfigState{DisableCapacities: true}
|
||||
|
||||
// Variables for tests on types which implement Stringer interface with and
|
||||
// without a pointer receiver.
|
||||
ts := stringer("test")
|
||||
tps := pstringer("test")
|
||||
|
||||
type ptrTester struct {
|
||||
s *struct{}
|
||||
}
|
||||
tptr := &ptrTester{s: &struct{}{}}
|
||||
|
||||
// depthTester is used to test max depth handling for structs, array, slices
|
||||
// and maps.
|
||||
type depthTester struct {
|
||||
@@ -192,6 +199,10 @@ func initSpewTests() {
|
||||
{scsContinue, fCSFprint, "", te, "(error: 10) 10"},
|
||||
{scsContinue, fCSFdump, "", te, "(spew_test.customError) " +
|
||||
"(error: 10) 10\n"},
|
||||
{scsNoPtrAddr, fCSFprint, "", tptr, "<*>{<*>{}}"},
|
||||
{scsNoPtrAddr, fCSSdump, "", tptr, "(*spew_test.ptrTester)({\ns: (*struct {})({\n})\n})\n"},
|
||||
{scsNoCap, fCSSdump, "", make([]string, 0, 10), "([]string) {\n}\n"},
|
||||
{scsNoCap, fCSSdump, "", make([]string, 1, 10), "([]string) (len=1) {\n(string) \"\"\n}\n"},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
43
vendor/github.com/disintegration/imaging/helpers.go
generated
vendored
43
vendor/github.com/disintegration/imaging/helpers.go
generated
vendored
@@ -76,8 +76,32 @@ func Open(filename string) (image.Image, error) {
|
||||
return img, err
|
||||
}
|
||||
|
||||
type encodeConfig struct {
|
||||
jpegQuality int
|
||||
}
|
||||
|
||||
var defaultEncodeConfig = encodeConfig{
|
||||
jpegQuality: 95,
|
||||
}
|
||||
|
||||
// EncodeOption sets an optional parameter for the Encode and Save functions.
|
||||
type EncodeOption func(*encodeConfig)
|
||||
|
||||
// JPEGQuality returns an EncodeOption that sets the output JPEG quality.
|
||||
// Quality ranges from 1 to 100 inclusive, higher is better. Default is 95.
|
||||
func JPEGQuality(quality int) EncodeOption {
|
||||
return func(c *encodeConfig) {
|
||||
c.jpegQuality = quality
|
||||
}
|
||||
}
|
||||
|
||||
// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
|
||||
func Encode(w io.Writer, img image.Image, format Format) error {
|
||||
func Encode(w io.Writer, img image.Image, format Format, opts ...EncodeOption) error {
|
||||
cfg := defaultEncodeConfig
|
||||
for _, option := range opts {
|
||||
option(&cfg)
|
||||
}
|
||||
|
||||
var err error
|
||||
switch format {
|
||||
case JPEG:
|
||||
@@ -92,9 +116,9 @@ func Encode(w io.Writer, img image.Image, format Format) error {
|
||||
}
|
||||
}
|
||||
if rgba != nil {
|
||||
err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: 95})
|
||||
err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: cfg.jpegQuality})
|
||||
} else {
|
||||
err = jpeg.Encode(w, img, &jpeg.Options{Quality: 95})
|
||||
err = jpeg.Encode(w, img, &jpeg.Options{Quality: cfg.jpegQuality})
|
||||
}
|
||||
|
||||
case PNG:
|
||||
@@ -113,7 +137,16 @@ func Encode(w io.Writer, img image.Image, format Format) error {
|
||||
|
||||
// Save saves the image to file with the specified filename.
|
||||
// The format is determined from the filename extension: "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
|
||||
func Save(img image.Image, filename string) (err error) {
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// // Save the image as PNG.
|
||||
// err := imaging.Save(img, "out.png")
|
||||
//
|
||||
// // Save the image as JPEG with optional quality parameter set to 80.
|
||||
// err := imaging.Save(img, "out.jpg", imaging.JPEGQuality(80))
|
||||
//
|
||||
func Save(img image.Image, filename string, opts ...EncodeOption) (err error) {
|
||||
formats := map[string]Format{
|
||||
".jpg": JPEG,
|
||||
".jpeg": JPEG,
|
||||
@@ -136,7 +169,7 @@ func Save(img image.Image, filename string) (err error) {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return Encode(file, img, f)
|
||||
return Encode(file, img, f, opts...)
|
||||
}
|
||||
|
||||
// New creates a new image with the specified width and height, and fills it with the specified color.
|
||||
|
||||
4
vendor/github.com/fsnotify/fsnotify/.travis.yml
generated
vendored
4
vendor/github.com/fsnotify/fsnotify/.travis.yml
generated
vendored
@@ -2,12 +2,14 @@ sudo: false
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.6.3
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
fast_finish: true
|
||||
|
||||
before_script:
|
||||
- go get -u github.com/golang/lint/golint
|
||||
|
||||
6
vendor/github.com/fsnotify/fsnotify/AUTHORS
generated
vendored
6
vendor/github.com/fsnotify/fsnotify/AUTHORS
generated
vendored
@@ -8,8 +8,10 @@
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Aaron L <aaron@bettercoder.net>
|
||||
Adrien Bustany <adrien@bustany.org>
|
||||
Amit Krishnan <amit.krishnan@oracle.com>
|
||||
Anmol Sethi <me@anmol.io>
|
||||
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
|
||||
Bruno Bigras <bigras.bruno@gmail.com>
|
||||
Caleb Spare <cespare@gmail.com>
|
||||
@@ -26,6 +28,7 @@ Kelvin Fo <vmirage@gmail.com>
|
||||
Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>
|
||||
Matt Layher <mdlayher@gmail.com>
|
||||
Nathan Youngman <git@nathany.com>
|
||||
Nickolai Zeldovich <nickolai@csail.mit.edu>
|
||||
Patrick <patrick@dropbox.com>
|
||||
Paul Hammond <paul@paulhammond.org>
|
||||
Pawel Knap <pawelknap88@gmail.com>
|
||||
@@ -33,12 +36,15 @@ Pieter Droogendijk <pieter@binky.org.uk>
|
||||
Pursuit92 <JoshChase@techpursuit.net>
|
||||
Riku Voipio <riku.voipio@linaro.org>
|
||||
Rob Figueiredo <robfig@gmail.com>
|
||||
Rodrigo Chiossi <rodrigochiossi@gmail.com>
|
||||
Slawek Ligus <root@ooz.ie>
|
||||
Soge Zhang <zhssoge@gmail.com>
|
||||
Tiffany Jernigan <tiffany.jernigan@intel.com>
|
||||
Tilak Sharma <tilaks@google.com>
|
||||
Tom Payne <twpayne@gmail.com>
|
||||
Travis Cline <travis.cline@gmail.com>
|
||||
Tudor Golubenco <tudor.g@gmail.com>
|
||||
Vahe Khachikyan <vahe@live.ca>
|
||||
Yukang <moorekang@gmail.com>
|
||||
bronze1man <bronze1man@gmail.com>
|
||||
debrando <denis.brandolini@gmail.com>
|
||||
|
||||
16
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
16
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
@@ -1,5 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
## v1.4.7 / 2018-01-09
|
||||
|
||||
* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine)
|
||||
* Tests: Fix missing verb on format string (thanks @rchiossi)
|
||||
* Linux: Fix deadlock in Remove (thanks @aarondl)
|
||||
* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne)
|
||||
* Docs: Moved FAQ into the README (thanks @vahe)
|
||||
* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich)
|
||||
* Docs: replace references to OS X with macOS
|
||||
|
||||
## v1.4.2 / 2016-10-10
|
||||
|
||||
* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack)
|
||||
@@ -79,7 +89,7 @@ kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsn
|
||||
|
||||
## v1.0.2 / 2014-08-17
|
||||
|
||||
* [Fix] Missing create events on OS X. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
||||
* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
||||
* [Fix] Make ./path and path equivalent. (thanks @zhsso)
|
||||
|
||||
## v1.0.0 / 2014-08-15
|
||||
@@ -142,7 +152,7 @@ kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsn
|
||||
|
||||
## v0.9.2 / 2014-08-17
|
||||
|
||||
* [Backport] Fix missing create events on OS X. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
||||
* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
||||
|
||||
## v0.9.1 / 2014-06-12
|
||||
|
||||
@@ -161,7 +171,7 @@ kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsn
|
||||
## v0.8.11 / 2013-11-02
|
||||
|
||||
* [Doc] Add Changelog [#72][] (thanks @nathany)
|
||||
* [Doc] Spotlight and double modify events on OS X [#62][] (reported by @paulhammond)
|
||||
* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond)
|
||||
|
||||
## v0.8.10 / 2013-10-19
|
||||
|
||||
|
||||
6
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
6
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
@@ -17,7 +17,7 @@ Please indicate that you have signed the CLA in your pull request.
|
||||
### How fsnotify is Developed
|
||||
|
||||
* Development is done on feature branches.
|
||||
* Tests are run on BSD, Linux, OS X and Windows.
|
||||
* Tests are run on BSD, Linux, macOS and Windows.
|
||||
* Pull requests are reviewed and [applied to master][am] using [hub][].
|
||||
* Maintainers may modify or squash commits rather than asking contributors to.
|
||||
* To issue a new release, the maintainers will:
|
||||
@@ -44,7 +44,7 @@ This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/
|
||||
|
||||
### Testing
|
||||
|
||||
fsnotify uses build tags to compile different code on Linux, BSD, OS X, and Windows.
|
||||
fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows.
|
||||
|
||||
Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on.
|
||||
|
||||
@@ -58,7 +58,7 @@ To aid in cross-platform testing there is a Vagrantfile for Linux and BSD.
|
||||
|
||||
Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory.
|
||||
|
||||
Right now there is no equivalent solution for Windows and OS X, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads).
|
||||
Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads).
|
||||
|
||||
### Maintainers
|
||||
|
||||
|
||||
37
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
37
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
@@ -8,14 +8,14 @@ fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather
|
||||
go get -u golang.org/x/sys/...
|
||||
```
|
||||
|
||||
Cross platform: Windows, Linux, BSD and OS X.
|
||||
Cross platform: Windows, Linux, BSD and macOS.
|
||||
|
||||
|Adapter |OS |Status |
|
||||
|----------|----------|----------|
|
||||
|inotify |Linux 2.6.27 or later, Android\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)|
|
||||
|kqueue |BSD, OS X, iOS\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)|
|
||||
|kqueue |BSD, macOS, iOS\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)|
|
||||
|ReadDirectoryChangesW|Windows|Supported [](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)|
|
||||
|FSEvents |OS X |[Planned](https://github.com/fsnotify/fsnotify/issues/11)|
|
||||
|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)|
|
||||
|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)|
|
||||
|fanotify |Linux 2.6.37+ | |
|
||||
|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)|
|
||||
@@ -23,7 +23,7 @@ Cross platform: Windows, Linux, BSD and OS X.
|
||||
|
||||
\* Android and iOS are untested.
|
||||
|
||||
Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) for usage. Consult the [Wiki](https://github.com/fsnotify/fsnotify/wiki) for the FAQ and further information.
|
||||
Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information.
|
||||
|
||||
## API stability
|
||||
|
||||
@@ -41,6 +41,35 @@ Please refer to [CONTRIBUTING][] before opening an issue or pull request.
|
||||
|
||||
See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go).
|
||||
|
||||
## FAQ
|
||||
|
||||
**When a file is moved to another directory is it still being watched?**
|
||||
|
||||
No (it shouldn't be, unless you are watching where it was moved to).
|
||||
|
||||
**When I watch a directory, are all subdirectories watched as well?**
|
||||
|
||||
No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]).
|
||||
|
||||
**Do I have to watch the Error and Event channels in a separate goroutine?**
|
||||
|
||||
As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7])
|
||||
|
||||
**Why am I receiving multiple events for the same file on OS X?**
|
||||
|
||||
Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]).
|
||||
|
||||
**How many files can be watched at once?**
|
||||
|
||||
There are OS-specific limits as to how many watches can be created:
|
||||
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
|
||||
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
|
||||
|
||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
|
||||
[#18]: https://github.com/fsnotify/fsnotify/issues/18
|
||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||
[#7]: https://github.com/howeyc/fsnotify/issues/7
|
||||
|
||||
[contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md
|
||||
|
||||
## Related Projects
|
||||
|
||||
4
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
4
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
@@ -9,6 +9,7 @@ package fsnotify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
@@ -60,3 +61,6 @@ func (op Op) String() string {
|
||||
func (e Event) String() string {
|
||||
return fmt.Sprintf("%q: %s", e.Name, e.Op.String())
|
||||
}
|
||||
|
||||
// Common errors that can be reported by a watcher
|
||||
var ErrEventOverflow = errors.New("fsnotify queue overflow")
|
||||
|
||||
32
vendor/github.com/fsnotify/fsnotify/fsnotify_test.go
generated
vendored
32
vendor/github.com/fsnotify/fsnotify/fsnotify_test.go
generated
vendored
@@ -6,7 +6,11 @@
|
||||
|
||||
package fsnotify
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestEventStringWithValue(t *testing.T) {
|
||||
for opMask, expectedString := range map[Op]string{
|
||||
@@ -38,3 +42,29 @@ func TestEventOpStringWithNoValue(t *testing.T) {
|
||||
t.Fatalf("Expected %s, got: %v", expectedOpString, event.Op.String())
|
||||
}
|
||||
}
|
||||
|
||||
// TestWatcherClose tests that the goroutine started by creating the watcher can be
|
||||
// signalled to return at any time, even if there is no goroutine listening on the events
|
||||
// or errors channels.
|
||||
func TestWatcherClose(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
name := tempMkFile(t, "")
|
||||
w := newWatcher(t)
|
||||
err := w.Add(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.Remove(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Allow the watcher to receive the event.
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
66
vendor/github.com/fsnotify/fsnotify/inotify.go
generated
vendored
66
vendor/github.com/fsnotify/fsnotify/inotify.go
generated
vendored
@@ -24,7 +24,6 @@ type Watcher struct {
|
||||
Events chan Event
|
||||
Errors chan error
|
||||
mu sync.Mutex // Map access
|
||||
cv *sync.Cond // sync removing on rm_watch with IN_IGNORE
|
||||
fd int
|
||||
poller *fdPoller
|
||||
watches map[string]*watch // Map of inotify watches (key: path)
|
||||
@@ -56,7 +55,6 @@ func NewWatcher() (*Watcher, error) {
|
||||
done: make(chan struct{}),
|
||||
doneResp: make(chan struct{}),
|
||||
}
|
||||
w.cv = sync.NewCond(&w.mu)
|
||||
|
||||
go w.readEvents()
|
||||
return w, nil
|
||||
@@ -103,21 +101,23 @@ func (w *Watcher) Add(name string) error {
|
||||
var flags uint32 = agnosticEvents
|
||||
|
||||
w.mu.Lock()
|
||||
watchEntry, found := w.watches[name]
|
||||
w.mu.Unlock()
|
||||
if found {
|
||||
watchEntry.flags |= flags
|
||||
flags |= unix.IN_MASK_ADD
|
||||
defer w.mu.Unlock()
|
||||
watchEntry := w.watches[name]
|
||||
if watchEntry != nil {
|
||||
flags |= watchEntry.flags | unix.IN_MASK_ADD
|
||||
}
|
||||
wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
|
||||
if wd == -1 {
|
||||
return errno
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
w.watches[name] = &watch{wd: uint32(wd), flags: flags}
|
||||
w.paths[wd] = name
|
||||
w.mu.Unlock()
|
||||
if watchEntry == nil {
|
||||
w.watches[name] = &watch{wd: uint32(wd), flags: flags}
|
||||
w.paths[wd] = name
|
||||
} else {
|
||||
watchEntry.wd = uint32(wd)
|
||||
watchEntry.flags = flags
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -135,6 +135,13 @@ func (w *Watcher) Remove(name string) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
|
||||
}
|
||||
|
||||
// We successfully removed the watch if InotifyRmWatch doesn't return an
|
||||
// error, we need to clean up our internal state to ensure it matches
|
||||
// inotify's kernel state.
|
||||
delete(w.paths, int(watch.wd))
|
||||
delete(w.watches, name)
|
||||
|
||||
// inotify_rm_watch will return EINVAL if the file has been deleted;
|
||||
// the inotify will already have been removed.
|
||||
// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
|
||||
@@ -152,13 +159,6 @@ func (w *Watcher) Remove(name string) error {
|
||||
return errno
|
||||
}
|
||||
|
||||
// wait until ignoreLinux() deleting maps
|
||||
exists := true
|
||||
for exists {
|
||||
w.cv.Wait()
|
||||
_, exists = w.watches[name]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -245,13 +245,31 @@ func (w *Watcher) readEvents() {
|
||||
|
||||
mask := uint32(raw.Mask)
|
||||
nameLen := uint32(raw.Len)
|
||||
|
||||
if mask&unix.IN_Q_OVERFLOW != 0 {
|
||||
select {
|
||||
case w.Errors <- ErrEventOverflow:
|
||||
case <-w.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If the event happened to the watched directory or the watched file, the kernel
|
||||
// doesn't append the filename to the event, but we would like to always fill the
|
||||
// the "Name" field with a valid filename. We retrieve the path of the watch from
|
||||
// the "paths" map.
|
||||
w.mu.Lock()
|
||||
name := w.paths[int(raw.Wd)]
|
||||
name, ok := w.paths[int(raw.Wd)]
|
||||
// IN_DELETE_SELF occurs when the file/directory being watched is removed.
|
||||
// This is a sign to clean up the maps, otherwise we are no longer in sync
|
||||
// with the inotify kernel state which has already deleted the watch
|
||||
// automatically.
|
||||
if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
|
||||
delete(w.paths, int(raw.Wd))
|
||||
delete(w.watches, name)
|
||||
}
|
||||
w.mu.Unlock()
|
||||
|
||||
if nameLen > 0 {
|
||||
// Point "bytes" at the first byte of the filename
|
||||
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))
|
||||
@@ -262,7 +280,7 @@ func (w *Watcher) readEvents() {
|
||||
event := newEvent(name, mask)
|
||||
|
||||
// Send the events that are not ignored on the events channel
|
||||
if !event.ignoreLinux(w, raw.Wd, mask) {
|
||||
if !event.ignoreLinux(mask) {
|
||||
select {
|
||||
case w.Events <- event:
|
||||
case <-w.done:
|
||||
@@ -279,15 +297,9 @@ func (w *Watcher) readEvents() {
|
||||
// Certain types of events can be "ignored" and not sent over the Events
|
||||
// channel. Such as events marked ignore by the kernel, or MODIFY events
|
||||
// against files that do not exist.
|
||||
func (e *Event) ignoreLinux(w *Watcher, wd int32, mask uint32) bool {
|
||||
func (e *Event) ignoreLinux(mask uint32) bool {
|
||||
// Ignore anything the inotify API says to ignore
|
||||
if mask&unix.IN_IGNORED == unix.IN_IGNORED {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
name := w.paths[int(wd)]
|
||||
delete(w.paths, int(wd))
|
||||
delete(w.watches, name)
|
||||
w.cv.Broadcast()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
109
vendor/github.com/fsnotify/fsnotify/inotify_test.go
generated
vendored
109
vendor/github.com/fsnotify/fsnotify/inotify_test.go
generated
vendored
@@ -293,25 +293,23 @@ func TestInotifyRemoveTwice(t *testing.T) {
|
||||
t.Fatalf("Failed to add testFile: %v", err)
|
||||
}
|
||||
|
||||
err = os.Remove(testFile)
|
||||
err = w.Remove(testFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to remove testFile: %v", err)
|
||||
t.Fatalf("wanted successful remove but got: %v", err)
|
||||
}
|
||||
|
||||
err = w.Remove(testFile)
|
||||
if err == nil {
|
||||
t.Fatalf("no error on removing invalid file")
|
||||
}
|
||||
s1 := fmt.Sprintf("%s", err)
|
||||
|
||||
err = w.Remove(testFile)
|
||||
if err == nil {
|
||||
t.Fatalf("no error on removing invalid file")
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if len(w.watches) != 0 {
|
||||
t.Fatalf("Expected watches len is 0, but got: %d, %v", len(w.watches), w.watches)
|
||||
}
|
||||
s2 := fmt.Sprintf("%s", err)
|
||||
|
||||
if s1 != s2 {
|
||||
t.Fatalf("receive different error - %s / %s", s1, s2)
|
||||
if len(w.paths) != 0 {
|
||||
t.Fatalf("Expected paths len is 0, but got: %d, %v", len(w.paths), w.paths)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,3 +356,94 @@ func TestInotifyInnerMapLength(t *testing.T) {
|
||||
t.Fatalf("Expected paths len is 0, but got: %d, %v", len(w.paths), w.paths)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInotifyOverflow(t *testing.T) {
|
||||
// We need to generate many more events than the
|
||||
// fs.inotify.max_queued_events sysctl setting.
|
||||
// We use multiple goroutines (one per directory)
|
||||
// to speed up file creation.
|
||||
numDirs := 128
|
||||
numFiles := 1024
|
||||
|
||||
testDir := tempMkdir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
w, err := NewWatcher()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create watcher: %v", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
for dn := 0; dn < numDirs; dn++ {
|
||||
testSubdir := fmt.Sprintf("%s/%d", testDir, dn)
|
||||
|
||||
err := os.Mkdir(testSubdir, 0777)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot create subdir: %v", err)
|
||||
}
|
||||
|
||||
err = w.Add(testSubdir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add subdir: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
errChan := make(chan error, numDirs*numFiles)
|
||||
|
||||
for dn := 0; dn < numDirs; dn++ {
|
||||
testSubdir := fmt.Sprintf("%s/%d", testDir, dn)
|
||||
|
||||
go func() {
|
||||
for fn := 0; fn < numFiles; fn++ {
|
||||
testFile := fmt.Sprintf("%s/%d", testSubdir, fn)
|
||||
|
||||
handle, err := os.Create(testFile)
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("Create failed: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = handle.Close()
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("Close failed: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
creates := 0
|
||||
overflows := 0
|
||||
|
||||
after := time.After(10 * time.Second)
|
||||
for overflows == 0 && creates < numDirs*numFiles {
|
||||
select {
|
||||
case <-after:
|
||||
t.Fatalf("Not done")
|
||||
case err := <-errChan:
|
||||
t.Fatalf("Got an error from file creator goroutine: %v", err)
|
||||
case err := <-w.Errors:
|
||||
if err == ErrEventOverflow {
|
||||
overflows++
|
||||
} else {
|
||||
t.Fatalf("Got an error from watcher: %v", err)
|
||||
}
|
||||
case evt := <-w.Events:
|
||||
if !strings.HasPrefix(evt.Name, testDir) {
|
||||
t.Fatalf("Got an event for an unknown file: %s", evt.Name)
|
||||
}
|
||||
if evt.Op == Create {
|
||||
creates++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if creates == numDirs*numFiles {
|
||||
t.Fatalf("Could not trigger overflow")
|
||||
}
|
||||
|
||||
if overflows == 0 {
|
||||
t.Fatalf("No overflow and not enough creates (expected %d, got %d)",
|
||||
numDirs*numFiles, creates)
|
||||
}
|
||||
}
|
||||
|
||||
4
vendor/github.com/fsnotify/fsnotify/integration_darwin_test.go
generated
vendored
4
vendor/github.com/fsnotify/fsnotify/integration_darwin_test.go
generated
vendored
@@ -13,9 +13,9 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// testExchangedataForWatcher tests the watcher with the exchangedata operation on OS X.
|
||||
// testExchangedataForWatcher tests the watcher with the exchangedata operation on macOS.
|
||||
//
|
||||
// This is widely used for atomic saves on OS X, e.g. TextMate and in Apple's NSDocument.
|
||||
// This is widely used for atomic saves on macOS, e.g. TextMate and in Apple's NSDocument.
|
||||
//
|
||||
// See https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/exchangedata.2.html
|
||||
// Also see: https://github.com/textmate/textmate/blob/cd016be29489eba5f3c09b7b70b06da134dda550/Frameworks/io/src/swap_file_data.cc#L20
|
||||
|
||||
62
vendor/github.com/fsnotify/fsnotify/kqueue.go
generated
vendored
62
vendor/github.com/fsnotify/fsnotify/kqueue.go
generated
vendored
@@ -22,7 +22,7 @@ import (
|
||||
type Watcher struct {
|
||||
Events chan Event
|
||||
Errors chan error
|
||||
done chan bool // Channel for sending a "quit message" to the reader goroutine
|
||||
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
|
||||
|
||||
kq int // File descriptor (as returned by the kqueue() syscall).
|
||||
|
||||
@@ -56,7 +56,7 @@ func NewWatcher() (*Watcher, error) {
|
||||
externalWatches: make(map[string]bool),
|
||||
Events: make(chan Event),
|
||||
Errors: make(chan error),
|
||||
done: make(chan bool),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
go w.readEvents()
|
||||
@@ -71,10 +71,8 @@ func (w *Watcher) Close() error {
|
||||
return nil
|
||||
}
|
||||
w.isClosed = true
|
||||
w.mu.Unlock()
|
||||
|
||||
// copy paths to remove while locked
|
||||
w.mu.Lock()
|
||||
var pathsToRemove = make([]string, 0, len(w.watches))
|
||||
for name := range w.watches {
|
||||
pathsToRemove = append(pathsToRemove, name)
|
||||
@@ -82,15 +80,12 @@ func (w *Watcher) Close() error {
|
||||
w.mu.Unlock()
|
||||
// unlock before calling Remove, which also locks
|
||||
|
||||
var err error
|
||||
for _, name := range pathsToRemove {
|
||||
if e := w.Remove(name); e != nil && err == nil {
|
||||
err = e
|
||||
}
|
||||
w.Remove(name)
|
||||
}
|
||||
|
||||
// Send "quit" message to the reader goroutine:
|
||||
w.done <- true
|
||||
// send a "quit" message to the reader goroutine
|
||||
close(w.done)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -266,17 +261,12 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
|
||||
func (w *Watcher) readEvents() {
|
||||
eventBuffer := make([]unix.Kevent_t, 10)
|
||||
|
||||
loop:
|
||||
for {
|
||||
// See if there is a message on the "done" channel
|
||||
select {
|
||||
case <-w.done:
|
||||
err := unix.Close(w.kq)
|
||||
if err != nil {
|
||||
w.Errors <- err
|
||||
}
|
||||
close(w.Events)
|
||||
close(w.Errors)
|
||||
return
|
||||
break loop
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -284,7 +274,11 @@ func (w *Watcher) readEvents() {
|
||||
kevents, err := read(w.kq, eventBuffer, &keventWaitTime)
|
||||
// EINTR is okay, the syscall was interrupted before timeout expired.
|
||||
if err != nil && err != unix.EINTR {
|
||||
w.Errors <- err
|
||||
select {
|
||||
case w.Errors <- err:
|
||||
case <-w.done:
|
||||
break loop
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -319,8 +313,12 @@ func (w *Watcher) readEvents() {
|
||||
if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) {
|
||||
w.sendDirectoryChangeEvents(event.Name)
|
||||
} else {
|
||||
// Send the event on the Events channel
|
||||
w.Events <- event
|
||||
// Send the event on the Events channel.
|
||||
select {
|
||||
case w.Events <- event:
|
||||
case <-w.done:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
if event.Op&Remove == Remove {
|
||||
@@ -352,6 +350,18 @@ func (w *Watcher) readEvents() {
|
||||
kevents = kevents[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup
|
||||
err := unix.Close(w.kq)
|
||||
if err != nil {
|
||||
// only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors.
|
||||
select {
|
||||
case w.Errors <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
close(w.Events)
|
||||
close(w.Errors)
|
||||
}
|
||||
|
||||
// newEvent returns an platform-independent Event based on kqueue Fflags.
|
||||
@@ -407,7 +417,11 @@ func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
|
||||
// Get all files
|
||||
files, err := ioutil.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
w.Errors <- err
|
||||
select {
|
||||
case w.Errors <- err:
|
||||
case <-w.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Search for new files
|
||||
@@ -428,7 +442,11 @@ func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInf
|
||||
w.mu.Unlock()
|
||||
if !doesExist {
|
||||
// Send create event
|
||||
w.Events <- newCreateEvent(filePath)
|
||||
select {
|
||||
case w.Events <- newCreateEvent(filePath):
|
||||
case <-w.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// like watchDirectoryFiles (but without doing another ReadDir)
|
||||
|
||||
6
vendor/github.com/go-ldap/ldap/.travis.yml
generated
vendored
6
vendor/github.com/go-ldap/ldap/.travis.yml
generated
vendored
@@ -1,8 +1,8 @@
|
||||
language: go
|
||||
env:
|
||||
global:
|
||||
- VET_VERSIONS="1.6 1.7 tip"
|
||||
- LINT_VERSIONS="1.6 1.7 tip"
|
||||
- VET_VERSIONS="1.6 1.7 1.8 1.9 tip"
|
||||
- LINT_VERSIONS="1.6 1.7 1.8 1.9 tip"
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
@@ -10,6 +10,8 @@ go:
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- 1.8
|
||||
- 1.9
|
||||
- tip
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
12
vendor/github.com/go-ldap/ldap/Makefile
generated
vendored
12
vendor/github.com/go-ldap/ldap/Makefile
generated
vendored
@@ -1,5 +1,15 @@
|
||||
.PHONY: default install build test quicktest fmt vet lint
|
||||
|
||||
GO_VERSION := $(shell go version | cut -d' ' -f3 | cut -d. -f2)
|
||||
|
||||
# Only use the `-race` flag on newer versions of Go
|
||||
IS_OLD_GO := $(shell test $(GO_VERSION) -le 2 && echo true)
|
||||
ifeq ($(IS_OLD_GO),true)
|
||||
RACE_FLAG :=
|
||||
else
|
||||
RACE_FLAG := -race -cpu 1,2,4
|
||||
endif
|
||||
|
||||
default: fmt vet lint build quicktest
|
||||
|
||||
install:
|
||||
@@ -9,7 +19,7 @@ build:
|
||||
go build -v ./...
|
||||
|
||||
test:
|
||||
go test -v -cover ./...
|
||||
go test -v $(RACE_FLAG) -cover ./...
|
||||
|
||||
quicktest:
|
||||
go test ./...
|
||||
|
||||
13
vendor/github.com/go-ldap/ldap/atomic_value.go
generated
vendored
Normal file
13
vendor/github.com/go-ldap/ldap/atomic_value.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// +build go1.4
|
||||
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// For compilers that support it, we just use the underlying sync/atomic.Value
|
||||
// type.
|
||||
type atomicValue struct {
|
||||
atomic.Value
|
||||
}
|
||||
28
vendor/github.com/go-ldap/ldap/atomic_value_go13.go
generated
vendored
Normal file
28
vendor/github.com/go-ldap/ldap/atomic_value_go13.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// +build !go1.4
|
||||
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// This is a helper type that emulates the use of the "sync/atomic.Value"
|
||||
// struct that's available in Go 1.4 and up.
|
||||
type atomicValue struct {
|
||||
value interface{}
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func (av *atomicValue) Store(val interface{}) {
|
||||
av.lock.Lock()
|
||||
av.value = val
|
||||
av.lock.Unlock()
|
||||
}
|
||||
|
||||
func (av *atomicValue) Load() interface{} {
|
||||
av.lock.RLock()
|
||||
ret := av.value
|
||||
av.lock.RUnlock()
|
||||
|
||||
return ret
|
||||
}
|
||||
73
vendor/github.com/go-ldap/ldap/conn.go
generated
vendored
73
vendor/github.com/go-ldap/ldap/conn.go
generated
vendored
@@ -11,6 +11,7 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"gopkg.in/asn1-ber.v1"
|
||||
@@ -82,20 +83,18 @@ const (
|
||||
type Conn struct {
|
||||
conn net.Conn
|
||||
isTLS bool
|
||||
isClosing bool
|
||||
closeErr error
|
||||
closing uint32
|
||||
closeErr atomicValue
|
||||
isStartingTLS bool
|
||||
Debug debugging
|
||||
chanConfirm chan bool
|
||||
chanConfirm chan struct{}
|
||||
messageContexts map[int64]*messageContext
|
||||
chanMessage chan *messagePacket
|
||||
chanMessageID chan int64
|
||||
wgSender sync.WaitGroup
|
||||
wgClose sync.WaitGroup
|
||||
once sync.Once
|
||||
outstandingRequests uint
|
||||
messageMutex sync.Mutex
|
||||
requestTimeout time.Duration
|
||||
requestTimeout int64
|
||||
}
|
||||
|
||||
var _ Client = &Conn{}
|
||||
@@ -142,7 +141,7 @@ func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
|
||||
func NewConn(conn net.Conn, isTLS bool) *Conn {
|
||||
return &Conn{
|
||||
conn: conn,
|
||||
chanConfirm: make(chan bool),
|
||||
chanConfirm: make(chan struct{}),
|
||||
chanMessageID: make(chan int64),
|
||||
chanMessage: make(chan *messagePacket, 10),
|
||||
messageContexts: map[int64]*messageContext{},
|
||||
@@ -158,12 +157,22 @@ func (l *Conn) Start() {
|
||||
l.wgClose.Add(1)
|
||||
}
|
||||
|
||||
// isClosing returns whether or not we're currently closing.
|
||||
func (l *Conn) isClosing() bool {
|
||||
return atomic.LoadUint32(&l.closing) == 1
|
||||
}
|
||||
|
||||
// setClosing sets the closing value to true
|
||||
func (l *Conn) setClosing() bool {
|
||||
return atomic.CompareAndSwapUint32(&l.closing, 0, 1)
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
func (l *Conn) Close() {
|
||||
l.once.Do(func() {
|
||||
l.isClosing = true
|
||||
l.wgSender.Wait()
|
||||
l.messageMutex.Lock()
|
||||
defer l.messageMutex.Unlock()
|
||||
|
||||
if l.setClosing() {
|
||||
l.Debug.Printf("Sending quit message and waiting for confirmation")
|
||||
l.chanMessage <- &messagePacket{Op: MessageQuit}
|
||||
<-l.chanConfirm
|
||||
@@ -171,27 +180,25 @@ func (l *Conn) Close() {
|
||||
|
||||
l.Debug.Printf("Closing network connection")
|
||||
if err := l.conn.Close(); err != nil {
|
||||
log.Print(err)
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
l.wgClose.Done()
|
||||
})
|
||||
}
|
||||
l.wgClose.Wait()
|
||||
}
|
||||
|
||||
// SetTimeout sets the time after a request is sent that a MessageTimeout triggers
|
||||
func (l *Conn) SetTimeout(timeout time.Duration) {
|
||||
if timeout > 0 {
|
||||
l.requestTimeout = timeout
|
||||
atomic.StoreInt64(&l.requestTimeout, int64(timeout))
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the next available messageID
|
||||
func (l *Conn) nextMessageID() int64 {
|
||||
if l.chanMessageID != nil {
|
||||
if messageID, ok := <-l.chanMessageID; ok {
|
||||
return messageID
|
||||
}
|
||||
if messageID, ok := <-l.chanMessageID; ok {
|
||||
return messageID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -258,7 +265,7 @@ func (l *Conn) sendMessage(packet *ber.Packet) (*messageContext, error) {
|
||||
}
|
||||
|
||||
func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (*messageContext, error) {
|
||||
if l.isClosing {
|
||||
if l.isClosing() {
|
||||
return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
|
||||
}
|
||||
l.messageMutex.Lock()
|
||||
@@ -297,7 +304,7 @@ func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags)
|
||||
func (l *Conn) finishMessage(msgCtx *messageContext) {
|
||||
close(msgCtx.done)
|
||||
|
||||
if l.isClosing {
|
||||
if l.isClosing() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -316,12 +323,12 @@ func (l *Conn) finishMessage(msgCtx *messageContext) {
|
||||
}
|
||||
|
||||
func (l *Conn) sendProcessMessage(message *messagePacket) bool {
|
||||
if l.isClosing {
|
||||
l.messageMutex.Lock()
|
||||
defer l.messageMutex.Unlock()
|
||||
if l.isClosing() {
|
||||
return false
|
||||
}
|
||||
l.wgSender.Add(1)
|
||||
l.chanMessage <- message
|
||||
l.wgSender.Done()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -333,15 +340,14 @@ func (l *Conn) processMessages() {
|
||||
for messageID, msgCtx := range l.messageContexts {
|
||||
// If we are closing due to an error, inform anyone who
|
||||
// is waiting about the error.
|
||||
if l.isClosing && l.closeErr != nil {
|
||||
msgCtx.sendResponse(&PacketResponse{Error: l.closeErr})
|
||||
if l.isClosing() && l.closeErr.Load() != nil {
|
||||
msgCtx.sendResponse(&PacketResponse{Error: l.closeErr.Load().(error)})
|
||||
}
|
||||
l.Debug.Printf("Closing channel for MessageID %d", messageID)
|
||||
close(msgCtx.responses)
|
||||
delete(l.messageContexts, messageID)
|
||||
}
|
||||
close(l.chanMessageID)
|
||||
l.chanConfirm <- true
|
||||
close(l.chanConfirm)
|
||||
}()
|
||||
|
||||
@@ -350,11 +356,7 @@ func (l *Conn) processMessages() {
|
||||
select {
|
||||
case l.chanMessageID <- messageID:
|
||||
messageID++
|
||||
case message, ok := <-l.chanMessage:
|
||||
if !ok {
|
||||
l.Debug.Printf("Shutting down - message channel is closed")
|
||||
return
|
||||
}
|
||||
case message := <-l.chanMessage:
|
||||
switch message.Op {
|
||||
case MessageQuit:
|
||||
l.Debug.Printf("Shutting down - quit message received")
|
||||
@@ -377,14 +379,15 @@ func (l *Conn) processMessages() {
|
||||
l.messageContexts[message.MessageID] = message.Context
|
||||
|
||||
// Add timeout if defined
|
||||
if l.requestTimeout > 0 {
|
||||
requestTimeout := time.Duration(atomic.LoadInt64(&l.requestTimeout))
|
||||
if requestTimeout > 0 {
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Printf("ldap: recovered panic in RequestTimeout: %v", err)
|
||||
}
|
||||
}()
|
||||
time.Sleep(l.requestTimeout)
|
||||
time.Sleep(requestTimeout)
|
||||
timeoutMessage := &messagePacket{
|
||||
Op: MessageTimeout,
|
||||
MessageID: message.MessageID,
|
||||
@@ -397,7 +400,7 @@ func (l *Conn) processMessages() {
|
||||
if msgCtx, ok := l.messageContexts[message.MessageID]; ok {
|
||||
msgCtx.sendResponse(&PacketResponse{message.Packet, nil})
|
||||
} else {
|
||||
log.Printf("Received unexpected message %d, %v", message.MessageID, l.isClosing)
|
||||
log.Printf("Received unexpected message %d, %v", message.MessageID, l.isClosing())
|
||||
ber.PrintPacket(message.Packet)
|
||||
}
|
||||
case MessageTimeout:
|
||||
@@ -439,8 +442,8 @@ func (l *Conn) reader() {
|
||||
packet, err := ber.ReadPacket(l.conn)
|
||||
if err != nil {
|
||||
// A read error is expected here if we are closing the connection...
|
||||
if !l.isClosing {
|
||||
l.closeErr = fmt.Errorf("unable to read LDAP response packet: %s", err)
|
||||
if !l.isClosing() {
|
||||
l.closeErr.Store(fmt.Errorf("unable to read LDAP response packet: %s", err))
|
||||
l.Debug.Printf("reader error: %s", err.Error())
|
||||
}
|
||||
return
|
||||
|
||||
10
vendor/github.com/go-ldap/ldap/conn_test.go
generated
vendored
10
vendor/github.com/go-ldap/ldap/conn_test.go
generated
vendored
@@ -60,7 +60,7 @@ func TestUnresponsiveConnection(t *testing.T) {
|
||||
|
||||
// TestFinishMessage tests that we do not enter deadlock when a goroutine makes
|
||||
// a request but does not handle all responses from the server.
|
||||
func TestConn(t *testing.T) {
|
||||
func TestFinishMessage(t *testing.T) {
|
||||
ptc := newPacketTranslatorConn()
|
||||
defer ptc.Close()
|
||||
|
||||
@@ -174,16 +174,12 @@ func testSendUnhandledResponsesAndFinish(t *testing.T, ptc *packetTranslatorConn
|
||||
}
|
||||
|
||||
func runWithTimeout(t *testing.T, timeout time.Duration, f func()) {
|
||||
runtime.Gosched()
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
f()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
runtime.Gosched()
|
||||
|
||||
select {
|
||||
case <-done: // Success!
|
||||
case <-time.After(timeout):
|
||||
@@ -192,7 +188,7 @@ func runWithTimeout(t *testing.T, timeout time.Duration, f func()) {
|
||||
}
|
||||
}
|
||||
|
||||
// packetTranslatorConn is a helful type which can be used with various tests
|
||||
// packetTranslatorConn is a helpful type which can be used with various tests
|
||||
// in this package. It implements the net.Conn interface to be used as an
|
||||
// underlying connection for a *ldap.Conn. Most methods are no-ops but the
|
||||
// Read() and Write() methods are able to translate ber-encoded packets for
|
||||
@@ -245,7 +241,7 @@ func (c *packetTranslatorConn) Read(b []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
// SendResponse writes the given response packet to the response buffer for
|
||||
// this conection, signalling any goroutine waiting to read a response.
|
||||
// this connection, signalling any goroutine waiting to read a response.
|
||||
func (c *packetTranslatorConn) SendResponse(packet *ber.Packet) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
2
vendor/github.com/go-ldap/ldap/debug.go
generated
vendored
2
vendor/github.com/go-ldap/ldap/debug.go
generated
vendored
@@ -6,7 +6,7 @@ import (
|
||||
"gopkg.in/asn1-ber.v1"
|
||||
)
|
||||
|
||||
// debbuging type
|
||||
// debugging type
|
||||
// - has a Printf method to write the debug output
|
||||
type debugging bool
|
||||
|
||||
|
||||
7
vendor/github.com/go-ldap/ldap/dn.go
generated
vendored
7
vendor/github.com/go-ldap/ldap/dn.go
generated
vendored
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// File contains DN parsing functionallity
|
||||
// File contains DN parsing functionality
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4514
|
||||
//
|
||||
@@ -52,7 +52,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
ber "gopkg.in/asn1-ber.v1"
|
||||
"gopkg.in/asn1-ber.v1"
|
||||
)
|
||||
|
||||
// AttributeTypeAndValue represents an attributeTypeAndValue from https://tools.ietf.org/html/rfc4514
|
||||
@@ -143,6 +143,9 @@ func ParseDN(str string) (*DN, error) {
|
||||
}
|
||||
} else if char == ',' || char == '+' {
|
||||
// We're done with this RDN or value, push it
|
||||
if len(attribute.Type) == 0 {
|
||||
return nil, errors.New("incomplete type, value pair")
|
||||
}
|
||||
attribute.Value = stringFromBuffer()
|
||||
rdn.Attributes = append(rdn.Attributes, attribute)
|
||||
attribute = new(AttributeTypeAndValue)
|
||||
|
||||
12
vendor/github.com/go-ldap/ldap/dn_test.go
generated
vendored
12
vendor/github.com/go-ldap/ldap/dn_test.go
generated
vendored
@@ -75,11 +75,13 @@ func TestSuccessfulDNParsing(t *testing.T) {
|
||||
|
||||
func TestErrorDNParsing(t *testing.T) {
|
||||
testcases := map[string]string{
|
||||
"*": "DN ended with incomplete type, value pair",
|
||||
"cn=Jim\\0Test": "Failed to decode escaped character: encoding/hex: invalid byte: U+0054 'T'",
|
||||
"cn=Jim\\0": "Got corrupted escaped character",
|
||||
"DC=example,=net": "DN ended with incomplete type, value pair",
|
||||
"1=#0402486": "Failed to decode BER encoding: encoding/hex: odd length hex string",
|
||||
"*": "DN ended with incomplete type, value pair",
|
||||
"cn=Jim\\0Test": "Failed to decode escaped character: encoding/hex: invalid byte: U+0054 'T'",
|
||||
"cn=Jim\\0": "Got corrupted escaped character",
|
||||
"DC=example,=net": "DN ended with incomplete type, value pair",
|
||||
"1=#0402486": "Failed to decode BER encoding: encoding/hex: odd length hex string",
|
||||
"test,DC=example,DC=com": "incomplete type, value pair",
|
||||
"=test,DC=example,DC=com": "incomplete type, value pair",
|
||||
}
|
||||
|
||||
for test, answer := range testcases {
|
||||
|
||||
7
vendor/github.com/go-ldap/ldap/error.go
generated
vendored
7
vendor/github.com/go-ldap/ldap/error.go
generated
vendored
@@ -97,6 +97,13 @@ var LDAPResultCodeMap = map[uint8]string{
|
||||
LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited",
|
||||
LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs",
|
||||
LDAPResultOther: "Other",
|
||||
|
||||
ErrorNetwork: "Network Error",
|
||||
ErrorFilterCompile: "Filter Compile Error",
|
||||
ErrorFilterDecompile: "Filter Decompile Error",
|
||||
ErrorDebugging: "Debugging Error",
|
||||
ErrorUnexpectedMessage: "Unexpected Message",
|
||||
ErrorUnexpectedResponse: "Unexpected Response",
|
||||
}
|
||||
|
||||
func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) {
|
||||
|
||||
4
vendor/github.com/go-ldap/ldap/error_test.go
generated
vendored
4
vendor/github.com/go-ldap/ldap/error_test.go
generated
vendored
@@ -49,7 +49,7 @@ func TestConnReadErr(t *testing.T) {
|
||||
// Send the signal after a short amount of time.
|
||||
time.AfterFunc(10*time.Millisecond, func() { conn.signals <- expectedError })
|
||||
|
||||
// This should block until the underlyiny conn gets the error signal
|
||||
// This should block until the underlying conn gets the error signal
|
||||
// which should bubble up through the reader() goroutine, close the
|
||||
// connection, and
|
||||
_, err := ldapConn.Search(searchReq)
|
||||
@@ -58,7 +58,7 @@ func TestConnReadErr(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// signalErrConn is a helful type used with TestConnReadErr. It implements the
|
||||
// signalErrConn is a helpful type used with TestConnReadErr. It implements the
|
||||
// net.Conn interface to be used as a connection for the test. Most methods are
|
||||
// no-ops but the Read() method blocks until it receives a signal which it
|
||||
// returns as an error.
|
||||
|
||||
12
vendor/github.com/go-ldap/ldap/example_test.go
generated
vendored
12
vendor/github.com/go-ldap/ldap/example_test.go
generated
vendored
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
// ExampleConn_Bind demonstrates how to bind a connection to an ldap user
|
||||
// allowing access to restricted attrabutes that user has access to
|
||||
// allowing access to restricted attributes that user has access to
|
||||
func ExampleConn_Bind() {
|
||||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
|
||||
if err != nil {
|
||||
@@ -63,10 +63,10 @@ func ExampleConn_StartTLS() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Opertations via l are now encrypted
|
||||
// Operations via l are now encrypted
|
||||
}
|
||||
|
||||
// ExampleConn_Compare demonstrates how to comapre an attribute with a value
|
||||
// ExampleConn_Compare demonstrates how to compare an attribute with a value
|
||||
func ExampleConn_Compare() {
|
||||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
|
||||
if err != nil {
|
||||
@@ -193,7 +193,7 @@ func Example_userAuthentication() {
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
"dc=example,dc=com",
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf("(&(objectClass=organizationalPerson)&(uid=%s))", username),
|
||||
fmt.Sprintf("(&(objectClass=organizationalPerson)(uid=%s))", username),
|
||||
[]string{"dn"},
|
||||
nil,
|
||||
)
|
||||
@@ -215,7 +215,7 @@ func Example_userAuthentication() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Rebind as the read only user for any futher queries
|
||||
// Rebind as the read only user for any further queries
|
||||
err = l.Bind(bindusername, bindpassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -240,7 +240,7 @@ func Example_beherappolicy() {
|
||||
if ppolicyControl != nil {
|
||||
ppolicy = ppolicyControl.(*ldap.ControlBeheraPasswordPolicy)
|
||||
} else {
|
||||
log.Printf("ppolicyControl response not avaliable.\n")
|
||||
log.Printf("ppolicyControl response not available.\n")
|
||||
}
|
||||
if err != nil {
|
||||
errStr := "ERROR: Cannot bind: " + err.Error()
|
||||
|
||||
5
vendor/github.com/go-ldap/ldap/filter.go
generated
vendored
5
vendor/github.com/go-ldap/ldap/filter.go
generated
vendored
@@ -82,7 +82,10 @@ func CompileFilter(filter string) (*ber.Packet, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pos != len(filter) {
|
||||
switch {
|
||||
case pos > len(filter):
|
||||
return nil, NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
|
||||
case pos < len(filter):
|
||||
return nil, NewError(ErrorFilterCompile, errors.New("ldap: finished compiling filter with extra at end: "+fmt.Sprint(filter[pos:])))
|
||||
}
|
||||
return packet, nil
|
||||
|
||||
6
vendor/github.com/go-ldap/ldap/filter_test.go
generated
vendored
6
vendor/github.com/go-ldap/ldap/filter_test.go
generated
vendored
@@ -131,6 +131,12 @@ var testFilters = []compileTest{
|
||||
expectedType: 0,
|
||||
expectedErr: "unexpected end of filter",
|
||||
},
|
||||
compileTest{
|
||||
filterStr: `((cn=)`,
|
||||
expectedFilter: ``,
|
||||
expectedType: 0,
|
||||
expectedErr: "unexpected end of filter",
|
||||
},
|
||||
compileTest{
|
||||
filterStr: `(&(objectclass=inetorgperson)(cn=中文))`,
|
||||
expectedFilter: `(&(objectclass=inetorgperson)(cn=\e4\b8\ad\e6\96\87))`,
|
||||
|
||||
2
vendor/github.com/go-ldap/ldap/ldap.go
generated
vendored
2
vendor/github.com/go-ldap/ldap/ldap.go
generated
vendored
@@ -9,7 +9,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
ber "gopkg.in/asn1-ber.v1"
|
||||
"gopkg.in/asn1-ber.v1"
|
||||
)
|
||||
|
||||
// LDAP Application Codes
|
||||
|
||||
8
vendor/github.com/go-ldap/ldap/passwdmodify.go
generated
vendored
8
vendor/github.com/go-ldap/ldap/passwdmodify.go
generated
vendored
@@ -135,10 +135,10 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa
|
||||
extendedResponse := packet.Children[1]
|
||||
for _, child := range extendedResponse.Children {
|
||||
if child.Tag == 11 {
|
||||
passwordModifyReponseValue := ber.DecodePacket(child.Data.Bytes())
|
||||
if len(passwordModifyReponseValue.Children) == 1 {
|
||||
if passwordModifyReponseValue.Children[0].Tag == 0 {
|
||||
result.GeneratedPassword = ber.DecodeString(passwordModifyReponseValue.Children[0].Data.Bytes())
|
||||
passwordModifyResponseValue := ber.DecodePacket(child.Data.Bytes())
|
||||
if len(passwordModifyResponseValue.Children) == 1 {
|
||||
if passwordModifyResponseValue.Children[0].Tag == 0 {
|
||||
result.GeneratedPassword = ber.DecodeString(passwordModifyResponseValue.Children[0].Data.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
vendor/github.com/go-ldap/ldap/search_test.go
generated
vendored
6
vendor/github.com/go-ldap/ldap/search_test.go
generated
vendored
@@ -15,7 +15,7 @@ func TestNewEntry(t *testing.T) {
|
||||
"delta": {"value"},
|
||||
"epsilon": {"value"},
|
||||
}
|
||||
exectedEntry := NewEntry(dn, attributes)
|
||||
executedEntry := NewEntry(dn, attributes)
|
||||
|
||||
iteration := 0
|
||||
for {
|
||||
@@ -23,8 +23,8 @@ func TestNewEntry(t *testing.T) {
|
||||
break
|
||||
}
|
||||
testEntry := NewEntry(dn, attributes)
|
||||
if !reflect.DeepEqual(exectedEntry, testEntry) {
|
||||
t.Fatalf("consequent calls to NewEntry did not yield the same result:\n\texpected:\n\t%s\n\tgot:\n\t%s\n", exectedEntry, testEntry)
|
||||
if !reflect.DeepEqual(executedEntry, testEntry) {
|
||||
t.Fatalf("subsequent calls to NewEntry did not yield the same result:\n\texpected:\n\t%s\n\tgot:\n\t%s\n", executedEntry, testEntry)
|
||||
}
|
||||
iteration = iteration + 1
|
||||
}
|
||||
|
||||
2
vendor/github.com/go-redis/redis/.travis.yml
generated
vendored
2
vendor/github.com/go-redis/redis/.travis.yml
generated
vendored
@@ -5,7 +5,6 @@ services:
|
||||
- redis-server
|
||||
|
||||
go:
|
||||
- 1.4.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
@@ -13,7 +12,6 @@ go:
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: 1.4.x
|
||||
- go: tip
|
||||
|
||||
install:
|
||||
|
||||
5
vendor/github.com/go-redis/redis/README.md
generated
vendored
5
vendor/github.com/go-redis/redis/README.md
generated
vendored
@@ -2,6 +2,7 @@
|
||||
|
||||
[](https://travis-ci.org/go-redis/redis)
|
||||
[](https://godoc.org/github.com/go-redis/redis)
|
||||
[](https://airbrake.io)
|
||||
|
||||
Supports:
|
||||
|
||||
@@ -66,14 +67,14 @@ func ExampleClient() {
|
||||
|
||||
val2, err := client.Get("key2").Result()
|
||||
if err == redis.Nil {
|
||||
fmt.Println("key2 does not exists")
|
||||
fmt.Println("key2 does not exist")
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Println("key2", val2)
|
||||
}
|
||||
// Output: key value
|
||||
// key2 does not exists
|
||||
// key2 does not exist
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
76
vendor/github.com/go-redis/redis/cluster.go
generated
vendored
76
vendor/github.com/go-redis/redis/cluster.go
generated
vendored
@@ -226,7 +226,7 @@ func (c *clusterNodes) NextGeneration() uint32 {
|
||||
}
|
||||
|
||||
// GC removes unused nodes.
|
||||
func (c *clusterNodes) GC(generation uint32) error {
|
||||
func (c *clusterNodes) GC(generation uint32) {
|
||||
var collected []*clusterNode
|
||||
c.mu.Lock()
|
||||
for i := 0; i < len(c.addrs); {
|
||||
@@ -243,14 +243,11 @@ func (c *clusterNodes) GC(generation uint32) error {
|
||||
}
|
||||
c.mu.Unlock()
|
||||
|
||||
var firstErr error
|
||||
for _, node := range collected {
|
||||
if err := node.Client.Close(); err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
time.AfterFunc(time.Minute, func() {
|
||||
for _, node := range collected {
|
||||
_ = node.Client.Close()
|
||||
}
|
||||
}
|
||||
|
||||
return firstErr
|
||||
})
|
||||
}
|
||||
|
||||
func (c *clusterNodes) All() ([]*clusterNode, error) {
|
||||
@@ -533,16 +530,22 @@ func (c *ClusterClient) cmdInfo(name string) *CommandInfo {
|
||||
return info
|
||||
}
|
||||
|
||||
func cmdSlot(cmd Cmder, pos int) int {
|
||||
if pos == 0 {
|
||||
return hashtag.RandomSlot()
|
||||
}
|
||||
firstKey := cmd.stringArg(pos)
|
||||
return hashtag.Slot(firstKey)
|
||||
}
|
||||
|
||||
func (c *ClusterClient) cmdSlot(cmd Cmder) int {
|
||||
cmdInfo := c.cmdInfo(cmd.Name())
|
||||
firstKey := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo))
|
||||
return hashtag.Slot(firstKey)
|
||||
return cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo))
|
||||
}
|
||||
|
||||
func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *clusterNode, error) {
|
||||
cmdInfo := c.cmdInfo(cmd.Name())
|
||||
firstKey := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo))
|
||||
slot := hashtag.Slot(firstKey)
|
||||
slot := cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo))
|
||||
|
||||
if cmdInfo != nil && cmdInfo.ReadOnly && c.opt.ReadOnly {
|
||||
if c.opt.RouteByLatency {
|
||||
@@ -590,6 +593,10 @@ func (c *ClusterClient) Watch(fn func(*Tx) error, keys ...string) error {
|
||||
break
|
||||
}
|
||||
|
||||
if internal.IsRetryableError(err, true) {
|
||||
continue
|
||||
}
|
||||
|
||||
moved, ask, addr := internal.IsMovedError(err)
|
||||
if moved || ask {
|
||||
c.lazyReloadState()
|
||||
@@ -600,6 +607,13 @@ func (c *ClusterClient) Watch(fn func(*Tx) error, keys ...string) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if err == pool.ErrClosed {
|
||||
node, err = state.slotMasterNode(slot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -635,10 +649,10 @@ func (c *ClusterClient) Process(cmd Cmder) error {
|
||||
|
||||
if ask {
|
||||
pipe := node.Client.Pipeline()
|
||||
pipe.Process(NewCmd("ASKING"))
|
||||
pipe.Process(cmd)
|
||||
_ = pipe.Process(NewCmd("ASKING"))
|
||||
_ = pipe.Process(cmd)
|
||||
_, err = pipe.Exec()
|
||||
pipe.Close()
|
||||
_ = pipe.Close()
|
||||
ask = false
|
||||
} else {
|
||||
err = node.Client.Process(cmd)
|
||||
@@ -679,6 +693,14 @@ func (c *ClusterClient) Process(cmd Cmder) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if err == pool.ErrClosed {
|
||||
_, node, err = c.cmdSlotAndNode(state, cmd)
|
||||
if err != nil {
|
||||
cmd.setErr(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -915,7 +937,11 @@ func (c *ClusterClient) pipelineExec(cmds []Cmder) error {
|
||||
for node, cmds := range cmdsMap {
|
||||
cn, _, err := node.Client.getConn()
|
||||
if err != nil {
|
||||
setCmdsErr(cmds, err)
|
||||
if err == pool.ErrClosed {
|
||||
c.remapCmds(cmds, failedCmds)
|
||||
} else {
|
||||
setCmdsErr(cmds, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -955,6 +981,18 @@ func (c *ClusterClient) mapCmdsByNode(cmds []Cmder) (map[*clusterNode][]Cmder, e
|
||||
return cmdsMap, nil
|
||||
}
|
||||
|
||||
func (c *ClusterClient) remapCmds(cmds []Cmder, failedCmds map[*clusterNode][]Cmder) {
|
||||
remappedCmds, err := c.mapCmdsByNode(cmds)
|
||||
if err != nil {
|
||||
setCmdsErr(cmds, err)
|
||||
return
|
||||
}
|
||||
|
||||
for node, cmds := range remappedCmds {
|
||||
failedCmds[node] = cmds
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClusterClient) pipelineProcessCmds(
|
||||
node *clusterNode, cn *pool.Conn, cmds []Cmder, failedCmds map[*clusterNode][]Cmder,
|
||||
) error {
|
||||
@@ -1061,7 +1099,11 @@ func (c *ClusterClient) txPipelineExec(cmds []Cmder) error {
|
||||
for node, cmds := range cmdsMap {
|
||||
cn, _, err := node.Client.getConn()
|
||||
if err != nil {
|
||||
setCmdsErr(cmds, err)
|
||||
if err == pool.ErrClosed {
|
||||
c.remapCmds(cmds, failedCmds)
|
||||
} else {
|
||||
setCmdsErr(cmds, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
26
vendor/github.com/go-redis/redis/cluster_test.go
generated
vendored
26
vendor/github.com/go-redis/redis/cluster_test.go
generated
vendored
@@ -536,6 +536,32 @@ var _ = Describe("ClusterClient", func() {
|
||||
Expect(nodesList).Should(HaveLen(1))
|
||||
})
|
||||
|
||||
It("should RANDOMKEY", func() {
|
||||
const nkeys = 100
|
||||
|
||||
for i := 0; i < nkeys; i++ {
|
||||
err := client.Set(fmt.Sprintf("key%d", i), "value", 0).Err()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
|
||||
var keys []string
|
||||
addKey := func(key string) {
|
||||
for _, k := range keys {
|
||||
if k == key {
|
||||
return
|
||||
}
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
for i := 0; i < nkeys*10; i++ {
|
||||
key := client.RandomKey().Val()
|
||||
addKey(key)
|
||||
}
|
||||
|
||||
Expect(len(keys)).To(BeNumerically("~", nkeys, nkeys/10))
|
||||
})
|
||||
|
||||
assertClusterClient()
|
||||
})
|
||||
|
||||
|
||||
42
vendor/github.com/go-redis/redis/command.go
generated
vendored
42
vendor/github.com/go-redis/redis/command.go
generated
vendored
@@ -82,13 +82,13 @@ func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
|
||||
if cmd.stringArg(2) != "0" {
|
||||
return 3
|
||||
} else {
|
||||
return -1
|
||||
return 0
|
||||
}
|
||||
case "publish":
|
||||
return 1
|
||||
}
|
||||
if info == nil {
|
||||
return -1
|
||||
return 0
|
||||
}
|
||||
return int(info.FirstKeyPos)
|
||||
}
|
||||
@@ -675,6 +675,44 @@ func (cmd *StringIntMapCmd) readReply(cn *pool.Conn) error {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type StringStructMapCmd struct {
|
||||
baseCmd
|
||||
|
||||
val map[string]struct{}
|
||||
}
|
||||
|
||||
var _ Cmder = (*StringStructMapCmd)(nil)
|
||||
|
||||
func NewStringStructMapCmd(args ...interface{}) *StringStructMapCmd {
|
||||
return &StringStructMapCmd{
|
||||
baseCmd: baseCmd{_args: args},
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *StringStructMapCmd) Val() map[string]struct{} {
|
||||
return cmd.val
|
||||
}
|
||||
|
||||
func (cmd *StringStructMapCmd) Result() (map[string]struct{}, error) {
|
||||
return cmd.val, cmd.err
|
||||
}
|
||||
|
||||
func (cmd *StringStructMapCmd) String() string {
|
||||
return cmdString(cmd, cmd.val)
|
||||
}
|
||||
|
||||
func (cmd *StringStructMapCmd) readReply(cn *pool.Conn) error {
|
||||
var v interface{}
|
||||
v, cmd.err = cn.Rd.ReadArrayReply(stringStructMapParser)
|
||||
if cmd.err != nil {
|
||||
return cmd.err
|
||||
}
|
||||
cmd.val = v.(map[string]struct{})
|
||||
return nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type ZSliceCmd struct {
|
||||
baseCmd
|
||||
|
||||
|
||||
10
vendor/github.com/go-redis/redis/commands.go
generated
vendored
10
vendor/github.com/go-redis/redis/commands.go
generated
vendored
@@ -143,6 +143,7 @@ type Cmdable interface {
|
||||
SInterStore(destination string, keys ...string) *IntCmd
|
||||
SIsMember(key string, member interface{}) *BoolCmd
|
||||
SMembers(key string) *StringSliceCmd
|
||||
SMembersMap(key string) *StringStructMapCmd
|
||||
SMove(source, destination string, member interface{}) *BoolCmd
|
||||
SPop(key string) *StringCmd
|
||||
SPopN(key string, count int64) *StringSliceCmd
|
||||
@@ -676,6 +677,7 @@ func (c *cmdable) DecrBy(key string, decrement int64) *IntCmd {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Redis `GET key` command. It returns redis.Nil error when key does not exist.
|
||||
func (c *cmdable) Get(key string) *StringCmd {
|
||||
cmd := NewStringCmd("get", key)
|
||||
c.process(cmd)
|
||||
@@ -1163,12 +1165,20 @@ func (c *cmdable) SIsMember(key string, member interface{}) *BoolCmd {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Redis `SMEMBERS key` command output as a slice
|
||||
func (c *cmdable) SMembers(key string) *StringSliceCmd {
|
||||
cmd := NewStringSliceCmd("smembers", key)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Redis `SMEMBERS key` command output as a map
|
||||
func (c *cmdable) SMembersMap(key string) *StringStructMapCmd {
|
||||
cmd := NewStringStructMapCmd("smembers", key)
|
||||
c.process(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *cmdable) SMove(source, destination string, member interface{}) *BoolCmd {
|
||||
cmd := NewBoolCmd("smove", source, destination, member)
|
||||
c.process(cmd)
|
||||
|
||||
11
vendor/github.com/go-redis/redis/commands_test.go
generated
vendored
11
vendor/github.com/go-redis/redis/commands_test.go
generated
vendored
@@ -1848,6 +1848,17 @@ var _ = Describe("Commands", func() {
|
||||
Expect(sMembers.Val()).To(ConsistOf([]string{"Hello", "World"}))
|
||||
})
|
||||
|
||||
It("should SMembersMap", func() {
|
||||
sAdd := client.SAdd("set", "Hello")
|
||||
Expect(sAdd.Err()).NotTo(HaveOccurred())
|
||||
sAdd = client.SAdd("set", "World")
|
||||
Expect(sAdd.Err()).NotTo(HaveOccurred())
|
||||
|
||||
sMembersMap := client.SMembersMap("set")
|
||||
Expect(sMembersMap.Err()).NotTo(HaveOccurred())
|
||||
Expect(sMembersMap.Val()).To(Equal(map[string]struct{}{"Hello": struct{}{}, "World": struct{}{}}))
|
||||
})
|
||||
|
||||
It("should SMove", func() {
|
||||
sAdd := client.SAdd("set1", "one")
|
||||
Expect(sAdd.Err()).NotTo(HaveOccurred())
|
||||
|
||||
4
vendor/github.com/go-redis/redis/example_test.go
generated
vendored
4
vendor/github.com/go-redis/redis/example_test.go
generated
vendored
@@ -96,14 +96,14 @@ func ExampleClient() {
|
||||
|
||||
val2, err := client.Get("key2").Result()
|
||||
if err == redis.Nil {
|
||||
fmt.Println("key2 does not exists")
|
||||
fmt.Println("key2 does not exist")
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Println("key2", val2)
|
||||
}
|
||||
// Output: key value
|
||||
// key2 does not exists
|
||||
// key2 does not exist
|
||||
}
|
||||
|
||||
func ExampleClient_Set() {
|
||||
|
||||
8
vendor/github.com/go-redis/redis/internal/hashtag/hashtag.go
generated
vendored
8
vendor/github.com/go-redis/redis/internal/hashtag/hashtag.go
generated
vendored
@@ -55,13 +55,17 @@ func Key(key string) string {
|
||||
return key
|
||||
}
|
||||
|
||||
func RandomSlot() int {
|
||||
return rand.Intn(SlotNumber)
|
||||
}
|
||||
|
||||
// hashSlot returns a consistent slot number between 0 and 16383
|
||||
// for any given string key.
|
||||
func Slot(key string) int {
|
||||
key = Key(key)
|
||||
if key == "" {
|
||||
return rand.Intn(SlotNumber)
|
||||
return RandomSlot()
|
||||
}
|
||||
key = Key(key)
|
||||
return int(crc16sum(key)) % SlotNumber
|
||||
}
|
||||
|
||||
|
||||
5
vendor/github.com/go-redis/redis/internal/proto/scan.go
generated
vendored
5
vendor/github.com/go-redis/redis/internal/proto/scan.go
generated
vendored
@@ -123,8 +123,9 @@ func ScanSlice(data []string, slice interface{}) error {
|
||||
next := internal.MakeSliceNextElemFunc(v)
|
||||
for i, s := range data {
|
||||
elem := next()
|
||||
if err := Scan(internal.StringToBytes(s), elem.Addr().Interface()); err != nil {
|
||||
return fmt.Errorf("redis: ScanSlice(index=%d value=%q) failed: %s", i, s, err)
|
||||
if err := Scan([]byte(s), elem.Addr().Interface()); err != nil {
|
||||
err = fmt.Errorf("redis: ScanSlice index=%d value=%q failed: %s", i, s, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
4
vendor/github.com/go-redis/redis/internal/safe.go
generated
vendored
4
vendor/github.com/go-redis/redis/internal/safe.go
generated
vendored
@@ -5,7 +5,3 @@ package internal
|
||||
func BytesToString(b []byte) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func StringToBytes(s string) []byte {
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
19
vendor/github.com/go-redis/redis/internal/unsafe.go
generated
vendored
19
vendor/github.com/go-redis/redis/internal/unsafe.go
generated
vendored
@@ -3,25 +3,10 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// BytesToString converts byte slice to string.
|
||||
func BytesToString(b []byte) string {
|
||||
bytesHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
strHeader := reflect.StringHeader{
|
||||
Data: bytesHeader.Data,
|
||||
Len: bytesHeader.Len,
|
||||
}
|
||||
return *(*string)(unsafe.Pointer(&strHeader))
|
||||
}
|
||||
|
||||
func StringToBytes(s string) []byte {
|
||||
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
bh := reflect.SliceHeader{
|
||||
Data: sh.Data,
|
||||
Len: sh.Len,
|
||||
Cap: sh.Len,
|
||||
}
|
||||
return *(*[]byte)(unsafe.Pointer(&bh))
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
2
vendor/github.com/go-redis/redis/options_test.go
generated
vendored
2
vendor/github.com/go-redis/redis/options_test.go
generated
vendored
@@ -71,7 +71,7 @@ func TestParseURL(t *testing.T) {
|
||||
t.Run(c.u, func(t *testing.T) {
|
||||
o, err := ParseURL(c.u)
|
||||
if c.err == nil && err != nil {
|
||||
t.Fatalf("unexpected error: '%q'", err)
|
||||
t.Fatalf("unexpected error: %q", err)
|
||||
return
|
||||
}
|
||||
if c.err != nil && err != nil {
|
||||
|
||||
14
vendor/github.com/go-redis/redis/parser.go
generated
vendored
14
vendor/github.com/go-redis/redis/parser.go
generated
vendored
@@ -97,6 +97,20 @@ func stringIntMapParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func stringStructMapParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
m := make(map[string]struct{}, n)
|
||||
for i := int64(0); i < n; i++ {
|
||||
key, err := rd.ReadStringReply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m[key] = struct{}{}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Implements proto.MultiBulkParse
|
||||
func zSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
|
||||
zz := make([]Z, n/2)
|
||||
|
||||
2
vendor/github.com/go-redis/redis/redis.go
generated
vendored
2
vendor/github.com/go-redis/redis/redis.go
generated
vendored
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/go-redis/redis/internal/proto"
|
||||
)
|
||||
|
||||
// Redis nil reply, .e.g. when key does not exist.
|
||||
// Redis nil reply returned when key does not exist.
|
||||
const Nil = internal.Nil
|
||||
|
||||
func init() {
|
||||
|
||||
9
vendor/github.com/go-redis/redis/ring.go
generated
vendored
9
vendor/github.com/go-redis/redis/ring.go
generated
vendored
@@ -298,6 +298,9 @@ func (c *Ring) cmdInfo(name string) *CommandInfo {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if c.cmdsInfo == nil {
|
||||
return nil
|
||||
}
|
||||
info := c.cmdsInfo[name]
|
||||
if info == nil {
|
||||
internal.Logf("info for cmd=%s not found", name)
|
||||
@@ -343,7 +346,11 @@ func (c *Ring) shardByName(name string) (*ringShard, error) {
|
||||
|
||||
func (c *Ring) cmdShard(cmd Cmder) (*ringShard, error) {
|
||||
cmdInfo := c.cmdInfo(cmd.Name())
|
||||
firstKey := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo))
|
||||
pos := cmdFirstKeyPos(cmd, cmdInfo)
|
||||
if pos == 0 {
|
||||
return c.randomShard()
|
||||
}
|
||||
firstKey := cmd.stringArg(pos)
|
||||
return c.shardByKey(firstKey)
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/go-redis/redis/universal.go
generated
vendored
2
vendor/github.com/go-redis/redis/universal.go
generated
vendored
@@ -114,6 +114,8 @@ func (o *UniversalOptions) simple() *Options {
|
||||
type UniversalClient interface {
|
||||
Cmdable
|
||||
Process(cmd Cmder) error
|
||||
Subscribe(channels ...string) *PubSub
|
||||
PSubscribe(channels ...string) *PubSub
|
||||
Close() error
|
||||
}
|
||||
|
||||
|
||||
1
vendor/github.com/go-sql-driver/mysql/.gitignore
generated
vendored
1
vendor/github.com/go-sql-driver/mysql/.gitignore
generated
vendored
@@ -6,3 +6,4 @@
|
||||
Icon?
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
.idea
|
||||
|
||||
93
vendor/github.com/go-sql-driver/mysql/.travis.yml
generated
vendored
93
vendor/github.com/go-sql-driver/mysql/.travis.yml
generated
vendored
@@ -1,13 +1,92 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- tip
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- master
|
||||
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
before_script:
|
||||
- echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB" | sudo tee -a /etc/mysql/my.cnf
|
||||
- sudo service mysql restart
|
||||
- .travis/wait_mysql.sh
|
||||
- mysql -e 'create database gotest;'
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: DB=MYSQL57
|
||||
sudo: required
|
||||
dist: trusty
|
||||
go: 1.9.x
|
||||
services:
|
||||
- docker
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
- docker pull mysql:5.7
|
||||
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
|
||||
mysql:5.7 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB
|
||||
- sleep 30
|
||||
- cp .travis/docker.cnf ~/.my.cnf
|
||||
- mysql --print-defaults
|
||||
- .travis/wait_mysql.sh
|
||||
before_script:
|
||||
- export MYSQL_TEST_USER=gotest
|
||||
- export MYSQL_TEST_PASS=secret
|
||||
- export MYSQL_TEST_ADDR=127.0.0.1:3307
|
||||
- export MYSQL_TEST_CONCURRENT=1
|
||||
|
||||
- env: DB=MARIA55
|
||||
sudo: required
|
||||
dist: trusty
|
||||
go: 1.9.x
|
||||
services:
|
||||
- docker
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
- docker pull mariadb:5.5
|
||||
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
|
||||
mariadb:5.5 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB
|
||||
- sleep 30
|
||||
- cp .travis/docker.cnf ~/.my.cnf
|
||||
- mysql --print-defaults
|
||||
- .travis/wait_mysql.sh
|
||||
before_script:
|
||||
- export MYSQL_TEST_USER=gotest
|
||||
- export MYSQL_TEST_PASS=secret
|
||||
- export MYSQL_TEST_ADDR=127.0.0.1:3307
|
||||
- export MYSQL_TEST_CONCURRENT=1
|
||||
|
||||
- env: DB=MARIA10_1
|
||||
sudo: required
|
||||
dist: trusty
|
||||
go: 1.9.x
|
||||
services:
|
||||
- docker
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
- docker pull mariadb:10.1
|
||||
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
|
||||
mariadb:10.1 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB
|
||||
- sleep 30
|
||||
- cp .travis/docker.cnf ~/.my.cnf
|
||||
- mysql --print-defaults
|
||||
- .travis/wait_mysql.sh
|
||||
before_script:
|
||||
- export MYSQL_TEST_USER=gotest
|
||||
- export MYSQL_TEST_PASS=secret
|
||||
- export MYSQL_TEST_ADDR=127.0.0.1:3307
|
||||
- export MYSQL_TEST_CONCURRENT=1
|
||||
|
||||
script:
|
||||
- go test -v -covermode=count -coverprofile=coverage.out
|
||||
- go vet ./...
|
||||
- test -z "$(gofmt -d -s . | tee /dev/stderr)"
|
||||
after_script:
|
||||
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci
|
||||
|
||||
5
vendor/github.com/go-sql-driver/mysql/.travis/docker.cnf
generated
vendored
Normal file
5
vendor/github.com/go-sql-driver/mysql/.travis/docker.cnf
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[client]
|
||||
user = gotest
|
||||
password = secret
|
||||
host = 127.0.0.1
|
||||
port = 3307
|
||||
8
vendor/github.com/go-sql-driver/mysql/.travis/wait_mysql.sh
generated
vendored
Executable file
8
vendor/github.com/go-sql-driver/mysql/.travis/wait_mysql.sh
generated
vendored
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
while :
|
||||
do
|
||||
sleep 3
|
||||
if mysql -e 'select version()'; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
27
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
27
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
@@ -12,35 +12,58 @@
|
||||
# Individual Persons
|
||||
|
||||
Aaron Hopkins <go-sql-driver at die.net>
|
||||
Achille Roussel <achille.roussel at gmail.com>
|
||||
Arne Hormann <arnehormann at gmail.com>
|
||||
Asta Xie <xiemengjun at gmail.com>
|
||||
Bulat Gaifullin <gaifullinbf at gmail.com>
|
||||
Carlos Nieto <jose.carlos at menteslibres.net>
|
||||
Chris Moos <chris at tech9computers.com>
|
||||
Daniel Montoya <dsmontoyam at gmail.com>
|
||||
Daniel Nichter <nil at codenode.com>
|
||||
Daniël van Eeden <git at myname.nl>
|
||||
Dave Protasowski <dprotaso at gmail.com>
|
||||
DisposaBoy <disposaboy at dby.me>
|
||||
Egor Smolyakov <egorsmkv at gmail.com>
|
||||
Evan Shaw <evan at vendhq.com>
|
||||
Frederick Mayle <frederickmayle at gmail.com>
|
||||
Gustavo Kristic <gkristic at gmail.com>
|
||||
Hanno Braun <mail at hannobraun.com>
|
||||
Henri Yandell <flamefew at gmail.com>
|
||||
Hirotaka Yamamoto <ymmt2005 at gmail.com>
|
||||
ICHINOSE Shogo <shogo82148 at gmail.com>
|
||||
INADA Naoki <songofacandy at gmail.com>
|
||||
Jacek Szwec <szwec.jacek at gmail.com>
|
||||
James Harr <james.harr at gmail.com>
|
||||
Jeff Hodges <jeff at somethingsimilar.com>
|
||||
Jeffrey Charles <jeffreycharles at gmail.com>
|
||||
Jian Zhen <zhenjl at gmail.com>
|
||||
Joshua Prunier <joshua.prunier at gmail.com>
|
||||
Julien Lefevre <julien.lefevr at gmail.com>
|
||||
Julien Schmidt <go-sql-driver at julienschmidt.com>
|
||||
Justin Li <jli at j-li.net>
|
||||
Justin Nuß <nuss.justin at gmail.com>
|
||||
Kamil Dziedzic <kamil at klecza.pl>
|
||||
Kevin Malachowski <kevin at chowski.com>
|
||||
Kieron Woodhouse <kieron.woodhouse at infosum.com>
|
||||
Lennart Rudolph <lrudolph at hmc.edu>
|
||||
Leonardo YongUk Kim <dalinaum at gmail.com>
|
||||
Linh Tran Tuan <linhduonggnu at gmail.com>
|
||||
Lion Yang <lion at aosc.xyz>
|
||||
Luca Looz <luca.looz92 at gmail.com>
|
||||
Lucas Liu <extrafliu at gmail.com>
|
||||
Luke Scott <luke at webconnex.com>
|
||||
Maciej Zimnoch <maciej.zimnoch@codilime.com>
|
||||
Michael Woolnough <michael.woolnough at gmail.com>
|
||||
Nicola Peduzzi <thenikso at gmail.com>
|
||||
Olivier Mengué <dolmen at cpan.org>
|
||||
oscarzhao <oscarzhaosl at gmail.com>
|
||||
Paul Bonser <misterpib at gmail.com>
|
||||
Peter Schultz <peter.schultz at classmarkets.com>
|
||||
Rebecca Chin <rchin at pivotal.io>
|
||||
Reed Allman <rdallman10 at gmail.com>
|
||||
Runrioter Wung <runrioter at gmail.com>
|
||||
Robert Russell <robert at rrbrussell.com>
|
||||
Shuode Li <elemount at qq.com>
|
||||
Soroush Pour <me at soroushjp.com>
|
||||
Stan Putrya <root.vagner at gmail.com>
|
||||
Stanley Gunawan <gunawan.stanley at gmail.com>
|
||||
@@ -52,5 +75,9 @@ Zhenye Xie <xiezhenye at gmail.com>
|
||||
# Organizations
|
||||
|
||||
Barracuda Networks, Inc.
|
||||
Counting Ltd.
|
||||
Google Inc.
|
||||
InfoSum Ltd.
|
||||
Keybase Inc.
|
||||
Pivotal Inc.
|
||||
Stripe Inc.
|
||||
|
||||
113
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
113
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
@@ -1,6 +1,6 @@
|
||||
# Go-MySQL-Driver
|
||||
|
||||
A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) package
|
||||
A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) package
|
||||
|
||||

|
||||
|
||||
@@ -15,6 +15,9 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa
|
||||
* [Address](#address)
|
||||
* [Parameters](#parameters)
|
||||
* [Examples](#examples)
|
||||
* [Connection pool and timeouts](#connection-pool-and-timeouts)
|
||||
* [context.Context Support](#contextcontext-support)
|
||||
* [ColumnType Support](#columntype-support)
|
||||
* [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support)
|
||||
* [time.Time support](#timetime-support)
|
||||
* [Unicode support](#unicode-support)
|
||||
@@ -26,31 +29,31 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa
|
||||
## Features
|
||||
* Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance")
|
||||
* Native Go implementation. No C-bindings, just pure Go
|
||||
* Connections over TCP/IPv4, TCP/IPv6, Unix domain sockets or [custom protocols](http://godoc.org/github.com/go-sql-driver/mysql#DialFunc)
|
||||
* Connections over TCP/IPv4, TCP/IPv6, Unix domain sockets or [custom protocols](https://godoc.org/github.com/go-sql-driver/mysql#DialFunc)
|
||||
* Automatic handling of broken connections
|
||||
* Automatic Connection Pooling *(by database/sql package)*
|
||||
* Supports queries larger than 16MB
|
||||
* Full [`sql.RawBytes`](http://golang.org/pkg/database/sql/#RawBytes) support.
|
||||
* Full [`sql.RawBytes`](https://golang.org/pkg/database/sql/#RawBytes) support.
|
||||
* Intelligent `LONG DATA` handling in prepared statements
|
||||
* Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support
|
||||
* Optional `time.Time` parsing
|
||||
* Optional placeholder interpolation
|
||||
|
||||
## Requirements
|
||||
* Go 1.2 or higher
|
||||
* Go 1.7 or higher. We aim to support the 3 latest versions of Go.
|
||||
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## Installation
|
||||
Simple install the package to your [$GOPATH](http://code.google.com/p/go-wiki/wiki/GOPATH "GOPATH") with the [go tool](http://golang.org/cmd/go/ "go command") from shell:
|
||||
Simple install the package to your [$GOPATH](https://github.com/golang/go/wiki/GOPATH "GOPATH") with the [go tool](https://golang.org/cmd/go/ "go command") from shell:
|
||||
```bash
|
||||
$ go get github.com/go-sql-driver/mysql
|
||||
$ go get -u github.com/go-sql-driver/mysql
|
||||
```
|
||||
Make sure [Git is installed](http://git-scm.com/downloads) on your machine and in your system's `PATH`.
|
||||
Make sure [Git is installed](https://git-scm.com/downloads) on your machine and in your system's `PATH`.
|
||||
|
||||
## Usage
|
||||
_Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](http://golang.org/pkg/database/sql) API then.
|
||||
_Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](https://golang.org/pkg/database/sql/) API then.
|
||||
|
||||
Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`:
|
||||
```go
|
||||
@@ -95,13 +98,14 @@ Alternatively, [Config.FormatDSN](https://godoc.org/github.com/go-sql-driver/mys
|
||||
Passwords can consist of any character. Escaping is **not** necessary.
|
||||
|
||||
#### Protocol
|
||||
See [net.Dial](http://golang.org/pkg/net/#Dial) for more information which networks are available.
|
||||
See [net.Dial](https://golang.org/pkg/net/#Dial) for more information which networks are available.
|
||||
In general you should use an Unix domain socket if available and TCP otherwise for best performance.
|
||||
|
||||
#### Address
|
||||
For TCP and UDP networks, addresses have the form `host:port`.
|
||||
For TCP and UDP networks, addresses have the form `host[:port]`.
|
||||
If `port` is omitted, the default port will be used.
|
||||
If `host` is a literal IPv6 address, it must be enclosed in square brackets.
|
||||
The functions [net.JoinHostPort](http://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](http://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form.
|
||||
The functions [net.JoinHostPort](https://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](https://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form.
|
||||
|
||||
For Unix domain sockets the address is the absolute path to the MySQL-Server-socket, e.g. `/var/run/mysqld/mysqld.sock` or `/tmp/mysql.sock`.
|
||||
|
||||
@@ -136,9 +140,9 @@ Default: false
|
||||
```
|
||||
Type: bool
|
||||
Valid Values: true, false
|
||||
Default: false
|
||||
Default: true
|
||||
```
|
||||
`allowNativePasswords=true` allows the usage of the mysql native password method.
|
||||
`allowNativePasswords=false` disallows the usage of MySQL native password method.
|
||||
|
||||
##### `allowOldPasswords`
|
||||
|
||||
@@ -220,19 +224,19 @@ Valid Values: <escaped name>
|
||||
Default: UTC
|
||||
```
|
||||
|
||||
Sets the location for time.Time values (when using `parseTime=true`). *"Local"* sets the system's location. See [time.LoadLocation](http://golang.org/pkg/time/#LoadLocation) for details.
|
||||
Sets the location for time.Time values (when using `parseTime=true`). *"Local"* sets the system's location. See [time.LoadLocation](https://golang.org/pkg/time/#LoadLocation) for details.
|
||||
|
||||
Note that this sets the location for time.Time values but does not change MySQL's [time_zone setting](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html). For that see the [time_zone system variable](#system-variables), which can also be set as a DSN parameter.
|
||||
|
||||
Please keep in mind, that param values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`.
|
||||
Please keep in mind, that param values must be [url.QueryEscape](https://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`.
|
||||
|
||||
##### `maxAllowedPacket`
|
||||
```
|
||||
Type: decimal number
|
||||
Default: 0
|
||||
Default: 4194304
|
||||
```
|
||||
|
||||
Max packet size allowed in bytes. Use `maxAllowedPacket=0` to automatically fetch the `max_allowed_packet` variable from server.
|
||||
Max packet size allowed in bytes. The default value is 4 MiB and should be adjusted to match the server settings. `maxAllowedPacket=0` can be used to automatically fetch the `max_allowed_packet` variable from server *on every connection*.
|
||||
|
||||
##### `multiStatements`
|
||||
|
||||
@@ -260,13 +264,13 @@ Default: false
|
||||
##### `readTimeout`
|
||||
|
||||
```
|
||||
Type: decimal number
|
||||
Type: duration
|
||||
Default: 0
|
||||
```
|
||||
|
||||
I/O read timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
||||
I/O read timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
||||
|
||||
##### `strict`
|
||||
##### `rejectReadOnly`
|
||||
|
||||
```
|
||||
Type: bool
|
||||
@@ -274,20 +278,37 @@ Valid Values: true, false
|
||||
Default: false
|
||||
```
|
||||
|
||||
`strict=true` enables a driver-side strict mode in which MySQL warnings are treated as errors. This mode should not be used in production as it may lead to data corruption in certain situations.
|
||||
|
||||
A server-side strict mode, which is safe for production use, can be set via the [`sql_mode`](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html) system variable.
|
||||
`rejectReadOnly=true` causes the driver to reject read-only connections. This
|
||||
is for a possible race condition during an automatic failover, where the mysql
|
||||
client gets connected to a read-only replica after the failover.
|
||||
|
||||
Note that this should be a fairly rare case, as an automatic failover normally
|
||||
happens when the primary is down, and the race condition shouldn't happen
|
||||
unless it comes back up online as soon as the failover is kicked off. On the
|
||||
other hand, when this happens, a MySQL application can get stuck on a
|
||||
read-only connection until restarted. It is however fairly easy to reproduce,
|
||||
for example, using a manual failover on AWS Aurora's MySQL-compatible cluster.
|
||||
|
||||
If you are not relying on read-only transactions to reject writes that aren't
|
||||
supposed to happen, setting this on some MySQL providers (such as AWS Aurora)
|
||||
is safer for failovers.
|
||||
|
||||
Note that ERROR 1290 can be returned for a `read-only` server and this option will
|
||||
cause a retry for that error. However the same error number is used for some
|
||||
other cases. You should ensure your application will never cause an ERROR 1290
|
||||
except for `read-only` mode when enabling this option.
|
||||
|
||||
By default MySQL also treats notes as warnings. Use [`sql_notes=false`](http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_notes) to ignore notes.
|
||||
|
||||
##### `timeout`
|
||||
|
||||
```
|
||||
Type: decimal number
|
||||
Type: duration
|
||||
Default: OS default
|
||||
```
|
||||
|
||||
*Driver* side connection timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout).
|
||||
Timeout for establishing connections, aka dial timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
||||
|
||||
|
||||
##### `tls`
|
||||
|
||||
@@ -297,16 +318,17 @@ Valid Values: true, false, skip-verify, <name>
|
||||
Default: false
|
||||
```
|
||||
|
||||
`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](http://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
|
||||
`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
|
||||
|
||||
|
||||
##### `writeTimeout`
|
||||
|
||||
```
|
||||
Type: decimal number
|
||||
Type: duration
|
||||
Default: 0
|
||||
```
|
||||
|
||||
I/O write timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
||||
I/O write timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
||||
|
||||
|
||||
##### System Variables
|
||||
@@ -317,9 +339,9 @@ Any other parameters are interpreted as system variables:
|
||||
* `<string_var>=%27<value>%27`: `SET <string_var>='<value>'`
|
||||
|
||||
Rules:
|
||||
* The values for string variables must be quoted with '
|
||||
* The values for string variables must be quoted with `'`.
|
||||
* The values must also be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!
|
||||
(which implies values of string variables must be wrapped with `%27`)
|
||||
(which implies values of string variables must be wrapped with `%27`).
|
||||
|
||||
Examples:
|
||||
* `autocommit=1`: `SET autocommit=1`
|
||||
@@ -380,6 +402,18 @@ No Database preselected:
|
||||
user:password@/
|
||||
```
|
||||
|
||||
|
||||
### Connection pool and timeouts
|
||||
The connection pool is managed by Go's database/sql package. For details on how to configure the size of the pool and how long connections stay in the pool see `*DB.SetMaxOpenConns`, `*DB.SetMaxIdleConns`, and `*DB.SetConnMaxLifetime` in the [database/sql documentation](https://golang.org/pkg/database/sql/). The read, write, and dial timeouts for each individual connection are configured with the DSN parameters [`readTimeout`](#readtimeout), [`writeTimeout`](#writetimeout), and [`timeout`](#timeout), respectively.
|
||||
|
||||
## `ColumnType` Support
|
||||
This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported.
|
||||
|
||||
## `context.Context` Support
|
||||
Go 1.8 added `database/sql` support for `context.Context`. This driver supports query timeouts and cancellation via contexts.
|
||||
See [context support in the database/sql package](https://golang.org/doc/go1.8#database_sql) for more details.
|
||||
|
||||
|
||||
### `LOAD DATA LOCAL INFILE` support
|
||||
For this feature you need direct access to the package. Therefore you must change the import path (no `_`):
|
||||
```go
|
||||
@@ -390,17 +424,17 @@ Files must be whitelisted by registering them with `mysql.RegisterLocalFile(file
|
||||
|
||||
To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
|
||||
|
||||
See the [godoc of Go-MySQL-Driver](http://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details.
|
||||
See the [godoc of Go-MySQL-Driver](https://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details.
|
||||
|
||||
|
||||
### `time.Time` support
|
||||
The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your programm.
|
||||
The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your program.
|
||||
|
||||
However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical opposite in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](http://golang.org/pkg/time/#Location) with the `loc` DSN parameter.
|
||||
However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical opposite in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](https://golang.org/pkg/time/#Location) with the `loc` DSN parameter.
|
||||
|
||||
**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
|
||||
|
||||
Alternatively you can use the [`NullTime`](http://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`.
|
||||
Alternatively you can use the [`NullTime`](https://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`.
|
||||
|
||||
|
||||
### Unicode support
|
||||
@@ -412,7 +446,6 @@ Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAM
|
||||
|
||||
See http://dev.mysql.com/doc/refman/5.7/en/charset-unicode.html for more details on MySQL's Unicode support.
|
||||
|
||||
|
||||
## Testing / Development
|
||||
To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details.
|
||||
|
||||
@@ -431,13 +464,13 @@ Mozilla summarizes the license scope as follows:
|
||||
|
||||
|
||||
That means:
|
||||
* You can **use** the **unchanged** source code both in private and commercially
|
||||
* When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0)
|
||||
* You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged**
|
||||
* You can **use** the **unchanged** source code both in private and commercially.
|
||||
* When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0).
|
||||
* You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged**.
|
||||
|
||||
Please read the [MPL 2.0 FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html) if you have further questions regarding the license.
|
||||
Please read the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/) if you have further questions regarding the license.
|
||||
|
||||
You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE)
|
||||
You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE).
|
||||
|
||||

|
||||
|
||||
|
||||
2
vendor/github.com/go-sql-driver/mysql/appengine.go
generated
vendored
2
vendor/github.com/go-sql-driver/mysql/appengine.go
generated
vendored
@@ -11,7 +11,7 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"appengine/cloudsql"
|
||||
"google.golang.org/appengine/cloudsql"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
93
vendor/github.com/go-sql-driver/mysql/benchmark_go18_test.go
generated
vendored
Normal file
93
vendor/github.com/go-sql-driver/mysql/benchmark_go18_test.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func benchmarkQueryContext(b *testing.B, db *sql.DB, p int) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
db.SetMaxIdleConns(p * runtime.GOMAXPROCS(0))
|
||||
|
||||
tb := (*TB)(b)
|
||||
stmt := tb.checkStmt(db.PrepareContext(ctx, "SELECT val FROM foo WHERE id=?"))
|
||||
defer stmt.Close()
|
||||
|
||||
b.SetParallelism(p)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
var got string
|
||||
for pb.Next() {
|
||||
tb.check(stmt.QueryRow(1).Scan(&got))
|
||||
if got != "one" {
|
||||
b.Fatalf("query = %q; want one", got)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkQueryContext(b *testing.B) {
|
||||
db := initDB(b,
|
||||
"DROP TABLE IF EXISTS foo",
|
||||
"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
|
||||
`INSERT INTO foo VALUES (1, "one")`,
|
||||
`INSERT INTO foo VALUES (2, "two")`,
|
||||
)
|
||||
defer db.Close()
|
||||
for _, p := range []int{1, 2, 3, 4} {
|
||||
b.Run(fmt.Sprintf("%d", p), func(b *testing.B) {
|
||||
benchmarkQueryContext(b, db, p)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkExecContext(b *testing.B, db *sql.DB, p int) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
db.SetMaxIdleConns(p * runtime.GOMAXPROCS(0))
|
||||
|
||||
tb := (*TB)(b)
|
||||
stmt := tb.checkStmt(db.PrepareContext(ctx, "DO 1"))
|
||||
defer stmt.Close()
|
||||
|
||||
b.SetParallelism(p)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
if _, err := stmt.ExecContext(ctx); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkExecContext(b *testing.B) {
|
||||
db := initDB(b,
|
||||
"DROP TABLE IF EXISTS foo",
|
||||
"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
|
||||
`INSERT INTO foo VALUES (1, "one")`,
|
||||
`INSERT INTO foo VALUES (2, "two")`,
|
||||
)
|
||||
defer db.Close()
|
||||
for _, p := range []int{1, 2, 3, 4} {
|
||||
b.Run(fmt.Sprintf("%d", p), func(b *testing.B) {
|
||||
benchmarkQueryContext(b, db, p)
|
||||
})
|
||||
}
|
||||
}
|
||||
6
vendor/github.com/go-sql-driver/mysql/benchmark_test.go
generated
vendored
6
vendor/github.com/go-sql-driver/mysql/benchmark_test.go
generated
vendored
@@ -48,11 +48,7 @@ func initDB(b *testing.B, queries ...string) *sql.DB {
|
||||
db := tb.checkDB(sql.Open("mysql", dsn))
|
||||
for _, query := range queries {
|
||||
if _, err := db.Exec(query); err != nil {
|
||||
if w, ok := err.(MySQLWarnings); ok {
|
||||
b.Logf("warning on %q: %v", query, w)
|
||||
} else {
|
||||
b.Fatalf("error on %q: %v", query, err)
|
||||
}
|
||||
b.Fatalf("error on %q: %v", query, err)
|
||||
}
|
||||
}
|
||||
return db
|
||||
|
||||
1
vendor/github.com/go-sql-driver/mysql/collations.go
generated
vendored
1
vendor/github.com/go-sql-driver/mysql/collations.go
generated
vendored
@@ -9,6 +9,7 @@
|
||||
package mysql
|
||||
|
||||
const defaultCollation = "utf8_general_ci"
|
||||
const binaryCollation = "binary"
|
||||
|
||||
// A list of available collations mapped to the internal ID.
|
||||
// To update this map use the following MySQL query:
|
||||
|
||||
152
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
152
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
@@ -10,12 +10,23 @@ package mysql
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// a copy of context.Context for Go 1.7 and earlier
|
||||
type mysqlContext interface {
|
||||
Done() <-chan struct{}
|
||||
Err() error
|
||||
|
||||
// defined in context.Context, but not used in this driver:
|
||||
// Deadline() (deadline time.Time, ok bool)
|
||||
// Value(key interface{}) interface{}
|
||||
}
|
||||
|
||||
type mysqlConn struct {
|
||||
buf buffer
|
||||
netConn net.Conn
|
||||
@@ -29,7 +40,14 @@ type mysqlConn struct {
|
||||
status statusFlag
|
||||
sequence uint8
|
||||
parseTime bool
|
||||
strict bool
|
||||
|
||||
// for context support (Go 1.8+)
|
||||
watching bool
|
||||
watcher chan<- mysqlContext
|
||||
closech chan struct{}
|
||||
finished chan<- struct{}
|
||||
canceled atomicError // set non-nil if conn is canceled
|
||||
closed atomicBool // set when conn is closed, before closech is closed
|
||||
}
|
||||
|
||||
// Handles parameters set in DSN after the connection is established
|
||||
@@ -62,22 +80,41 @@ func (mc *mysqlConn) handleParams() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) markBadConn(err error) error {
|
||||
if mc == nil {
|
||||
return err
|
||||
}
|
||||
if err != errBadConnNoWrite {
|
||||
return err
|
||||
}
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Begin() (driver.Tx, error) {
|
||||
if mc.netConn == nil {
|
||||
return mc.begin(false)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
err := mc.exec("START TRANSACTION")
|
||||
var q string
|
||||
if readOnly {
|
||||
q = "START TRANSACTION READ ONLY"
|
||||
} else {
|
||||
q = "START TRANSACTION"
|
||||
}
|
||||
err := mc.exec(q)
|
||||
if err == nil {
|
||||
return &mysqlTx{mc}, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, mc.markBadConn(err)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Close() (err error) {
|
||||
// Makes Close idempotent
|
||||
if mc.netConn != nil {
|
||||
if !mc.closed.IsSet() {
|
||||
err = mc.writeCommandPacket(comQuit)
|
||||
}
|
||||
|
||||
@@ -91,26 +128,39 @@ func (mc *mysqlConn) Close() (err error) {
|
||||
// is called before auth or on auth failure because MySQL will have already
|
||||
// closed the network connection.
|
||||
func (mc *mysqlConn) cleanup() {
|
||||
// Makes cleanup idempotent
|
||||
if mc.netConn != nil {
|
||||
if err := mc.netConn.Close(); err != nil {
|
||||
errLog.Print(err)
|
||||
}
|
||||
mc.netConn = nil
|
||||
if !mc.closed.TrySet(true) {
|
||||
return
|
||||
}
|
||||
mc.cfg = nil
|
||||
mc.buf.nc = nil
|
||||
|
||||
// Makes cleanup idempotent
|
||||
close(mc.closech)
|
||||
if mc.netConn == nil {
|
||||
return
|
||||
}
|
||||
if err := mc.netConn.Close(); err != nil {
|
||||
errLog.Print(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) error() error {
|
||||
if mc.closed.IsSet() {
|
||||
if err := mc.canceled.Value(); err != nil {
|
||||
return err
|
||||
}
|
||||
return ErrInvalidConn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
|
||||
if mc.netConn == nil {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comStmtPrepare, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, mc.markBadConn(err)
|
||||
}
|
||||
|
||||
stmt := &mysqlStmt{
|
||||
@@ -144,7 +194,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
||||
if buf == nil {
|
||||
// can not take the buffer. Something must be wrong with the connection
|
||||
errLog.Print(ErrBusyBuffer)
|
||||
return "", driver.ErrBadConn
|
||||
return "", ErrInvalidConn
|
||||
}
|
||||
buf = buf[:0]
|
||||
argPos := 0
|
||||
@@ -257,7 +307,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
||||
if mc.netConn == nil {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
@@ -271,7 +321,6 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
|
||||
return nil, err
|
||||
}
|
||||
query = prepared
|
||||
args = nil
|
||||
}
|
||||
mc.affectedRows = 0
|
||||
mc.insertId = 0
|
||||
@@ -283,32 +332,43 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
|
||||
insertId: int64(mc.insertId),
|
||||
}, err
|
||||
}
|
||||
return nil, err
|
||||
return nil, mc.markBadConn(err)
|
||||
}
|
||||
|
||||
// Internal function to execute commands
|
||||
func (mc *mysqlConn) exec(query string) error {
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comQuery, query)
|
||||
if err != nil {
|
||||
return err
|
||||
if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
|
||||
return mc.markBadConn(err)
|
||||
}
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
if err == nil && resLen > 0 {
|
||||
if err = mc.readUntilEOF(); err != nil {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resLen > 0 {
|
||||
// columns
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = mc.readUntilEOF()
|
||||
// rows
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
return mc.discardResults()
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
||||
if mc.netConn == nil {
|
||||
return mc.query(query, args)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
@@ -322,7 +382,6 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
|
||||
return nil, err
|
||||
}
|
||||
query = prepared
|
||||
args = nil
|
||||
}
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comQuery, query)
|
||||
@@ -335,15 +394,22 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
|
||||
rows.mc = mc
|
||||
|
||||
if resLen == 0 {
|
||||
// no columns, no more data
|
||||
return emptyRows{}, nil
|
||||
rows.rs.done = true
|
||||
|
||||
switch err := rows.NextResultSet(); err {
|
||||
case nil, io.EOF:
|
||||
return rows, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Columns
|
||||
rows.columns, err = mc.readColumns(resLen)
|
||||
rows.rs.columns, err = mc.readColumns(resLen)
|
||||
return rows, err
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
return nil, mc.markBadConn(err)
|
||||
}
|
||||
|
||||
// Gets the value of the given MySQL System Variable
|
||||
@@ -359,7 +425,7 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
||||
if err == nil {
|
||||
rows := new(textRows)
|
||||
rows.mc = mc
|
||||
rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
|
||||
rows.rs.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
|
||||
|
||||
if resLen > 0 {
|
||||
// Columns
|
||||
@@ -375,3 +441,21 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// finish is called when the query has canceled.
|
||||
func (mc *mysqlConn) cancel(err error) {
|
||||
mc.canceled.Set(err)
|
||||
mc.cleanup()
|
||||
}
|
||||
|
||||
// finish is called when the query has succeeded.
|
||||
func (mc *mysqlConn) finish() {
|
||||
if !mc.watching || mc.finished == nil {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case mc.finished <- struct{}{}:
|
||||
mc.watching = false
|
||||
case <-mc.closech:
|
||||
}
|
||||
}
|
||||
|
||||
202
vendor/github.com/go-sql-driver/mysql/connection_go18.go
generated
vendored
Normal file
202
vendor/github.com/go-sql-driver/mysql/connection_go18.go
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
// Ping implements driver.Pinger interface
|
||||
func (mc *mysqlConn) Ping(ctx context.Context) error {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
|
||||
if err := mc.watchCancel(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
defer mc.finish()
|
||||
|
||||
if err := mc.writeCommandPacket(comPing); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := mc.readResultOK(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeginTx implements driver.ConnBeginTx interface
|
||||
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
|
||||
if err := mc.watchCancel(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer mc.finish()
|
||||
|
||||
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
|
||||
level, err := mapIsolationLevel(opts.Isolation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return mc.begin(opts.ReadOnly)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := mc.watchCancel(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := mc.query(query, dargs)
|
||||
if err != nil {
|
||||
mc.finish()
|
||||
return nil, err
|
||||
}
|
||||
rows.finish = mc.finish
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := mc.watchCancel(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer mc.finish()
|
||||
|
||||
return mc.Exec(query, dargs)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
|
||||
if err := mc.watchCancel(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stmt, err := mc.Prepare(query)
|
||||
mc.finish()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
stmt.Close()
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := stmt.mc.watchCancel(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := stmt.query(dargs)
|
||||
if err != nil {
|
||||
stmt.mc.finish()
|
||||
return nil, err
|
||||
}
|
||||
rows.finish = stmt.mc.finish
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
||||
dargs, err := namedValueToValue(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := stmt.mc.watchCancel(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stmt.mc.finish()
|
||||
|
||||
return stmt.Exec(dargs)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) watchCancel(ctx context.Context) error {
|
||||
if mc.watching {
|
||||
// Reach here if canceled,
|
||||
// so the connection is already invalid
|
||||
mc.cleanup()
|
||||
return nil
|
||||
}
|
||||
if ctx.Done() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
mc.watching = true
|
||||
select {
|
||||
default:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
if mc.watcher == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
mc.watcher <- ctx
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) startWatcher() {
|
||||
watcher := make(chan mysqlContext, 1)
|
||||
mc.watcher = watcher
|
||||
finished := make(chan struct{})
|
||||
mc.finished = finished
|
||||
go func() {
|
||||
for {
|
||||
var ctx mysqlContext
|
||||
select {
|
||||
case ctx = <-watcher:
|
||||
case <-mc.closech:
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
mc.cancel(ctx.Err())
|
||||
case <-finished:
|
||||
case <-mc.closech:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) {
|
||||
nv.Value, err = converter{}.ConvertValue(nv.Value)
|
||||
return
|
||||
}
|
||||
30
vendor/github.com/go-sql-driver/mysql/connection_go18_test.go
generated
vendored
Normal file
30
vendor/github.com/go-sql-driver/mysql/connection_go18_test.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckNamedValue(t *testing.T) {
|
||||
value := driver.NamedValue{Value: ^uint64(0)}
|
||||
x := &mysqlConn{}
|
||||
err := x.CheckNamedValue(&value)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("uint64 high-bit not convertible", err)
|
||||
}
|
||||
|
||||
if value.Value != "18446744073709551615" {
|
||||
t.Fatalf("uint64 high-bit not converted, got %#v %T", value.Value, value.Value)
|
||||
}
|
||||
}
|
||||
9
vendor/github.com/go-sql-driver/mysql/const.go
generated
vendored
9
vendor/github.com/go-sql-driver/mysql/const.go
generated
vendored
@@ -9,7 +9,8 @@
|
||||
package mysql
|
||||
|
||||
const (
|
||||
minProtocolVersion byte = 10
|
||||
defaultMaxAllowedPacket = 4 << 20 // 4 MiB
|
||||
minProtocolVersion = 10
|
||||
maxPacketSize = 1<<24 - 1
|
||||
timeFormat = "2006-01-02 15:04:05.999999"
|
||||
)
|
||||
@@ -87,8 +88,10 @@ const (
|
||||
)
|
||||
|
||||
// https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType
|
||||
type fieldType byte
|
||||
|
||||
const (
|
||||
fieldTypeDecimal byte = iota
|
||||
fieldTypeDecimal fieldType = iota
|
||||
fieldTypeTiny
|
||||
fieldTypeShort
|
||||
fieldTypeLong
|
||||
@@ -107,7 +110,7 @@ const (
|
||||
fieldTypeBit
|
||||
)
|
||||
const (
|
||||
fieldTypeJSON byte = iota + 0xf5
|
||||
fieldTypeJSON fieldType = iota + 0xf5
|
||||
fieldTypeNewDecimal
|
||||
fieldTypeEnum
|
||||
fieldTypeSet
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user