Этот коммит содержится в:
Christopher Speller
2017-02-02 09:32:00 -05:00
коммит произвёл Harrison Healey
родитель ca3211bc04
Коммит 701d1ab638
326 изменённых файлов: 121334 добавлений и 3928 удалений

Просмотреть файл

@@ -31,7 +31,7 @@ func InitFile() {
BaseRoutes.NeedFile.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("GET") BaseRoutes.NeedFile.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("GET")
BaseRoutes.Public.Handle("/files/{file_id:[A-Za-z0-9]+}/get", ApiAppHandlerTrustRequesterIndependent(getPublicFile)).Methods("GET") BaseRoutes.Public.Handle("/files/{file_id:[A-Za-z0-9]+}/get", ApiAppHandlerTrustRequesterIndependent(getPublicFile)).Methods("GET")
BaseRoutes.Public.Handle("/files/get/{team_id:[A-Za-z0-9]+}/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiAppHandlerTrustRequesterIndependent(getPublicFileOld)).Methods("GET") BaseRoutes.Public.Handle("/files/get/{team_id:[A-Za-z0-9]+}/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:(?:[A-Za-z0-9]+/)?.+(?:\\.[A-Za-z0-9]{3,})?}", ApiAppHandlerTrustRequesterIndependent(getPublicFileOld)).Methods("GET")
} }
func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) { func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {

54
glide.lock сгенерированный
Просмотреть файл

@@ -1,5 +1,5 @@
hash: 67ac70374ac7d1acb02736f628409e406cb55f343a260ccf78c4f324ec2df45a hash: 42839eb256dd5b5607d93cd50372cf55881de6e874a63525862ed3f0ac47682b
updated: 2016-12-25T21:59:49.671116871+05:30 updated: 2017-02-01T10:22:19.702548922-05:00
imports: imports:
- name: github.com/alecthomas/log4go - name: github.com/alecthomas/log4go
version: e5dc62318d9bd58682f1dceb53a4b24e8253682f version: e5dc62318d9bd58682f1dceb53a4b24e8253682f
@@ -10,7 +10,7 @@ imports:
- name: github.com/dgryski/dgoogauth - name: github.com/dgryski/dgoogauth
version: 96977cbd42e27be71f9f731db6634123de7e861a version: 96977cbd42e27be71f9f731db6634123de7e861a
- name: github.com/disintegration/imaging - name: github.com/disintegration/imaging
version: 5b7e22645c93e3f3911b36b7d66bf8799f3eddfd version: 243d2d8673c1225a6afceeb9b3b4423d485dc8df
- name: github.com/dyatlov/go-opengraph - name: github.com/dyatlov/go-opengraph
version: 41a3523719dfbe7e8f853fbd4061867543db5270 version: 41a3523719dfbe7e8f853fbd4061867543db5270
subpackages: subpackages:
@@ -18,16 +18,16 @@ imports:
- name: github.com/go-gorp/gorp - name: github.com/go-gorp/gorp
version: 0c9bc0918534d133cedb439a24adc7cbe66e4a9d version: 0c9bc0918534d133cedb439a24adc7cbe66e4a9d
- name: github.com/go-ldap/ldap - name: github.com/go-ldap/ldap
version: d0a5ced67b4dc310b9158d63a2c6f9c5ec13f105 version: 8168ee085ee43257585e50c6441aadf54ecb2c9f
- name: github.com/go-sql-driver/mysql - name: github.com/go-sql-driver/mysql
version: a732e14c62dde3285440047bba97581bc472ae18 version: a0583e0143b1624142adab07e0e97fe106d99561
- name: github.com/goamz/goamz - name: github.com/goamz/goamz
version: fb002ae75f50beb93874fa6ce2c861e488ce08a0 version: b2c2eaf25cbb87f41087aa796facf82113809ed5
subpackages: subpackages:
- aws - aws
- s3 - s3
- name: github.com/golang/freetype - name: github.com/golang/freetype
version: 38b4c392adc5eed94207994c4848fff99f4ac234 version: d9be45aaf7452cc30c0ceb1b1bf7efe1d17b7c87
subpackages: subpackages:
- raster - raster
- truetype - truetype
@@ -38,11 +38,11 @@ imports:
- name: github.com/gorilla/context - name: github.com/gorilla/context
version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42
- name: github.com/gorilla/handlers - name: github.com/gorilla/handlers
version: e1b2144f2167de0e1042d1d35e5cba5119d4fb5d version: 3a5767ca75ece5f7f1440b1d16975247f8d8b221
- name: github.com/gorilla/mux - name: github.com/gorilla/mux
version: 34dda716af12ba79ed40ce1f66b102ff75dc3411 version: 392c28fe23e1c45ddba891b0320b3b5df220beea
- name: github.com/gorilla/websocket - name: github.com/gorilla/websocket
version: e8f0f8aaa98dfb6586cbdf2978d511e3199a960a version: 3ab3a8b8831546bd18fd182c20687ca853b2bb13
- name: github.com/hashicorp/golang-lru - name: github.com/hashicorp/golang-lru
version: 0a025b7e63adc15a622f29b0b2c4c3848243bbf6 version: 0a025b7e63adc15a622f29b0b2c4c3848243bbf6
subpackages: subpackages:
@@ -52,7 +52,7 @@ imports:
- name: github.com/jehiah/go-strftime - name: github.com/jehiah/go-strftime
version: 834e15c05a45371503440cc195bbd05c9a0968d9 version: 834e15c05a45371503440cc195bbd05c9a0968d9
- name: github.com/lib/pq - name: github.com/lib/pq
version: d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b version: a6657b2386e9b8be76484c08711b02c7cf867ead
subpackages: subpackages:
- oid - oid
- name: github.com/mattermost/rsc - name: github.com/mattermost/rsc
@@ -66,23 +66,25 @@ imports:
subpackages: subpackages:
- pbutil - pbutil
- name: github.com/miekg/dns - name: github.com/miekg/dns
version: 271c58e0c14f552178ea321a545ff9af38930f39 version: 99f84ae56e75126dd77e5de4fae2ea034a468ca1
- name: github.com/minio/minio-go - name: github.com/minio/minio-go
version: c040578255dbb1a1f41cbdc5bd944de4140b4149 version: 52cc94e879db78c2e2c6e160869df943137ec4cd
subpackages: subpackages:
- pkg/policy - pkg/policy
- pkg/s3signer
- pkg/s3utils
- pkg/set - pkg/set
- name: github.com/mssola/user_agent - name: github.com/mssola/user_agent
version: 85b2f5798558a46fc23443c596e781712f4b7792 version: 85b2f5798558a46fc23443c596e781712f4b7792
- name: github.com/nicksnyder/go-i18n - name: github.com/nicksnyder/go-i18n
version: 1c9e01733308fe876a840a54ce7633540b182a75 version: f757c9f9b69c16ff69d38dbf224be28a7b6537bb
subpackages: subpackages:
- i18n - i18n
- i18n/bundle - i18n/bundle
- i18n/language - i18n/language
- i18n/translation - i18n/translation
- name: github.com/NYTimes/gziphandler - name: github.com/NYTimes/gziphandler
version: 172fbbbb329cf7e031dd3ab35766186fc2081eab version: 6710af535839f57c687b62c4c23d649f9545d885
- name: github.com/pborman/uuid - name: github.com/pborman/uuid
version: a97ce2ca70fa5a848076093f05e639a89ca34d06 version: a97ce2ca70fa5a848076093f05e639a89ca34d06
- name: github.com/prometheus/client_golang - name: github.com/prometheus/client_golang
@@ -94,11 +96,11 @@ imports:
subpackages: subpackages:
- go - go
- name: github.com/prometheus/common - name: github.com/prometheus/common
version: 0d5de9d6d8629cb8bee6d4674da4127cd8b615a3 version: dd2f054febf4a6c00f2343686efb775948a8bff4
subpackages: subpackages:
- expfmt - expfmt
- name: github.com/prometheus/procfs - name: github.com/prometheus/procfs
version: abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 version: 1878d9fbb537119d24b21ca07effd591627cd160
- name: github.com/rsc/letsencrypt - name: github.com/rsc/letsencrypt
version: 76104d26167d38b6a0010f42bfc8ec5487742e8b version: 76104d26167d38b6a0010f42bfc8ec5487742e8b
- name: github.com/rwcarlsen/goexif - name: github.com/rwcarlsen/goexif
@@ -111,25 +113,25 @@ imports:
- name: github.com/segmentio/backo-go - name: github.com/segmentio/backo-go
version: 204274ad699c0983a70203a566887f17a717fef4 version: 204274ad699c0983a70203a566887f17a717fef4
- name: github.com/spf13/cobra - name: github.com/spf13/cobra
version: 9495bc009a56819bdb0ddbc1a373e29c140bc674 version: 35136c09d8da66b901337c6e86fd8e88a1a255bd
- name: github.com/spf13/pflag - name: github.com/spf13/pflag
version: 5ccb023bc27df288a957c5e994cd44fd19619465 version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7
- name: github.com/tylerb/graceful - name: github.com/tylerb/graceful
version: 4df1190835320af7076dfcf27b3d071fd3612caf version: 0e9129e9c6d47da90dc0c188b26bd7bb1dab53cd
- name: github.com/xenolf/lego - name: github.com/xenolf/lego
version: 0abbd738a45af51595a214c52b02b18d16974f19 version: f5d538caab6dc0c167d4e32990c79bbf9eff578c
subpackages: subpackages:
- acme - acme
- name: github.com/xtgo/uuid - name: github.com/xtgo/uuid
version: a0b114877d4caeffbd7f87e3757c17fce570fea7 version: a0b114877d4caeffbd7f87e3757c17fce570fea7
- name: golang.org/x/crypto - name: golang.org/x/crypto
version: 9477e0b78b9ac3d0b03822fd95422e2fe07627cd version: dc137beb6cce2043eb6b5f223ab8bf51c32459f4
subpackages: subpackages:
- bcrypt - bcrypt
- blowfish - blowfish
- ocsp - ocsp
- name: golang.org/x/image - name: golang.org/x/image
version: 507b1a44bd72830c136e6ecf7c05f9089796d572 version: 83686c547965220f8b5d75e83ddc67d73420a89f
subpackages: subpackages:
- bmp - bmp
- font - font
@@ -137,14 +139,14 @@ imports:
- tiff - tiff
- tiff/lzw - tiff/lzw
- name: golang.org/x/net - name: golang.org/x/net
version: 4971afdc2f162e82d185353533d3cf16188a9f4e version: f2499483f923065a842d38eb4c7f1927e6fc6e6d
subpackages: subpackages:
- context - context
- html - html
- html/atom - html/atom
- publicsuffix - publicsuffix
- name: golang.org/x/sys - name: golang.org/x/sys
version: 30237cf4eefd639b184d1f2cb77a581ea0be8947 version: 7a6e5648d140666db5d920909e082ca00a87ba2c
subpackages: subpackages:
- unix - unix
- name: golang.org/x/time - name: golang.org/x/time
@@ -166,5 +168,5 @@ imports:
- store - store
- store/memstore - store/memstore
- name: gopkg.in/yaml.v2 - name: gopkg.in/yaml.v2
version: a5b47d31c556af34a302ce5d659e6fea44d90de0 version: 4c78c975fe7c825c6d1466c42be594d1d6f3aba6
testImports: [] testImports: []

Просмотреть файл

@@ -1,56 +1,45 @@
package: github.com/mattermost/platform package: github.com/mattermost/platform
import: import:
- package: github.com/NYTimes/gziphandler - package: github.com/NYTimes/gziphandler
version: 172fbbbb329cf7e031dd3ab35766186fc2081eab
- package: github.com/alecthomas/log4go - package: github.com/alecthomas/log4go
version: e5dc62318d9bd58682f1dceb53a4b24e8253682f
- package: github.com/dgryski/dgoogauth - package: github.com/dgryski/dgoogauth
version: 96977cbd42e27be71f9f731db6634123de7e861a
- package: github.com/disintegration/imaging - package: github.com/disintegration/imaging
version: 5b7e22645c93e3f3911b36b7d66bf8799f3eddfd
- package: github.com/go-gorp/gorp - package: github.com/go-gorp/gorp
version: 0c9bc0918534d133cedb439a24adc7cbe66e4a9d version: 0c9bc0918534d133cedb439a24adc7cbe66e4a9d
- package: github.com/go-ldap/ldap - package: github.com/go-ldap/ldap
version: v2.4.1 version: v2.5.0
- package: github.com/go-sql-driver/mysql - package: github.com/go-sql-driver/mysql
version: v1.2 version: v1.3
- package: github.com/goamz/goamz - package: github.com/goamz/goamz
version: fb002ae75f50beb93874fa6ce2c861e488ce08a0
subpackages: subpackages:
- aws - aws
- s3 - s3
- package: github.com/golang/freetype - package: github.com/golang/freetype
version: 38b4c392adc5eed94207994c4848fff99f4ac234
- package: github.com/gorilla/handlers - package: github.com/gorilla/handlers
version: e1b2144f2167de0e1042d1d35e5cba5119d4fb5d version: v1.2
- package: github.com/gorilla/mux - package: github.com/gorilla/mux
version: 34dda716af12ba79ed40ce1f66b102ff75dc3411 version: v1.3.0
- package: github.com/gorilla/websocket - package: github.com/gorilla/websocket
version: e8f0f8aaa98dfb6586cbdf2978d511e3199a960a version: v1.1.0
- package: github.com/lib/pq - package: github.com/lib/pq
version: d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b
- package: github.com/mattermost/rsc - package: github.com/mattermost/rsc
version: bbaefb05eaa0389ea712340066837c8ce4d287f9
subpackages: subpackages:
- qr - qr
- package: github.com/mssola/user_agent - package: github.com/mssola/user_agent
version: v0.4.1 version: v0.4.1
- package: github.com/nicksnyder/go-i18n - package: github.com/nicksnyder/go-i18n
version: v1.6.0 version: v1.7.0
subpackages: subpackages:
- i18n - i18n
- package: github.com/pborman/uuid - package: github.com/pborman/uuid
version: v1.0 version: v1.0
- package: github.com/rwcarlsen/goexif - package: github.com/rwcarlsen/goexif
version: 709fab3d192d7c62f86043caff1e7e3fb0f42bd8
subpackages: subpackages:
- exif - exif
- package: golang.org/x/crypto - package: golang.org/x/crypto
version: 9477e0b78b9ac3d0b03822fd95422e2fe07627cd
subpackages: subpackages:
- bcrypt - bcrypt
- package: golang.org/x/image - package: golang.org/x/image
version: 507b1a44bd72830c136e6ecf7c05f9089796d572
subpackages: subpackages:
- bmp - bmp
- package: gopkg.in/fsnotify.v1 - package: gopkg.in/fsnotify.v1
@@ -62,9 +51,8 @@ import:
- package: github.com/segmentio/analytics-go - package: github.com/segmentio/analytics-go
version: 2.1.1 version: 2.1.1
- package: github.com/rsc/letsencrypt - package: github.com/rsc/letsencrypt
version: 76104d26167d38b6a0010f42bfc8ec5487742e8b
- package: github.com/minio/minio-go - package: github.com/minio/minio-go
version: 2.0.2 version: v2.0.3
- package: github.com/prometheus/client_golang - package: github.com/prometheus/client_golang
version: v0.8.0 version: v0.8.0
subpackages: subpackages:
@@ -89,6 +77,5 @@ import:
- package: github.com/spf13/cobra - package: github.com/spf13/cobra
- package: github.com/spf13/pflag - package: github.com/spf13/pflag
- package: github.com/dyatlov/go-opengraph - package: github.com/dyatlov/go-opengraph
version: 41a3523719dfbe7e8f853fbd4061867543db5270
subpackages: subpackages:
- opengraph - opengraph

14
vendor/github.com/NYTimes/gziphandler/gzip.go сгенерированный поставляемый
Просмотреть файл

@@ -1,8 +1,10 @@
package gziphandler package gziphandler
import ( import (
"bufio"
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"net"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@@ -131,6 +133,18 @@ func (w *GzipResponseWriter) Flush() {
} }
} }
// Hijack implements http.Hijacker. If the underlying ResponseWriter is a
// Hijacker, its Hijack method is returned. Otherwise an error is returned.
func (w *GzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if hj, ok := w.ResponseWriter.(http.Hijacker); ok {
return hj.Hijack()
}
return nil, nil, fmt.Errorf("http.Hijacker interface is not supported")
}
// verify Hijacker interface implementation
var _ http.Hijacker = &GzipResponseWriter{}
// MustNewGzipLevelHandler behaves just like NewGzipLevelHandler except that in // MustNewGzipLevelHandler behaves just like NewGzipLevelHandler except that in
// an error case it panics rather than returning an error. // an error case it panics rather than returning an error.
func MustNewGzipLevelHandler(level int) func(http.Handler) http.Handler { func MustNewGzipLevelHandler(level int) func(http.Handler) http.Handler {

30
vendor/github.com/disintegration/imaging/effects.go сгенерированный поставляемый
Просмотреть файл

@@ -67,15 +67,16 @@ func blurHorizontal(src *image.NRGBA, kernel []float64) *image.NRGBA {
for ix := start; ix <= end; ix++ { for ix := start; ix <= end; ix++ {
weight := kernel[absint(x-ix)] weight := kernel[absint(x-ix)]
i := y*src.Stride + ix*4 i := y*src.Stride + ix*4
r += float64(src.Pix[i+0]) * weight wa := float64(src.Pix[i+3]) * weight
g += float64(src.Pix[i+1]) * weight r += float64(src.Pix[i+0]) * wa
b += float64(src.Pix[i+2]) * weight g += float64(src.Pix[i+1]) * wa
a += float64(src.Pix[i+3]) * weight b += float64(src.Pix[i+2]) * wa
a += wa
} }
r = math.Min(math.Max(r/weightSum, 0.0), 255.0) r = math.Min(math.Max(r/a, 0.0), 255.0)
g = math.Min(math.Max(g/weightSum, 0.0), 255.0) g = math.Min(math.Max(g/a, 0.0), 255.0)
b = math.Min(math.Max(b/weightSum, 0.0), 255.0) b = math.Min(math.Max(b/a, 0.0), 255.0)
a = math.Min(math.Max(a/weightSum, 0.0), 255.0) a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
j := y*dst.Stride + x*4 j := y*dst.Stride + x*4
@@ -121,15 +122,16 @@ func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA {
for iy := start; iy <= end; iy++ { for iy := start; iy <= end; iy++ {
weight := kernel[absint(y-iy)] weight := kernel[absint(y-iy)]
i := iy*src.Stride + x*4 i := iy*src.Stride + x*4
r += float64(src.Pix[i+0]) * weight wa := float64(src.Pix[i+3]) * weight
g += float64(src.Pix[i+1]) * weight r += float64(src.Pix[i+0]) * wa
b += float64(src.Pix[i+2]) * weight g += float64(src.Pix[i+1]) * wa
a += float64(src.Pix[i+3]) * weight b += float64(src.Pix[i+2]) * wa
a += wa
} }
r = math.Min(math.Max(r/weightSum, 0.0), 255.0) r = math.Min(math.Max(r/a, 0.0), 255.0)
g = math.Min(math.Max(g/weightSum, 0.0), 255.0) g = math.Min(math.Max(g/a, 0.0), 255.0)
b = math.Min(math.Max(b/weightSum, 0.0), 255.0) b = math.Min(math.Max(b/a, 0.0), 255.0)
a = math.Min(math.Max(a/weightSum, 0.0), 255.0) a = math.Min(math.Max(a/weightSum, 0.0), 255.0)
j := y*dst.Stride + x*4 j := y*dst.Stride + x*4

14
vendor/github.com/disintegration/imaging/effects_test.go сгенерированный поставляемый
Просмотреть файл

@@ -50,9 +50,9 @@ func TestBlur(t *testing.T) {
Rect: image.Rect(0, 0, 3, 3), Rect: image.Rect(0, 0, 3, 3),
Stride: 3 * 4, Stride: 3 * 4,
Pix: []uint8{ Pix: []uint8{
0x01, 0x02, 0x04, 0x04, 0x0a, 0x10, 0x18, 0x18, 0x01, 0x02, 0x04, 0x04, 0x66, 0xaa, 0xff, 0x04, 0x66, 0xaa, 0xff, 0x18, 0x66, 0xaa, 0xff, 0x04,
0x09, 0x10, 0x18, 0x18, 0x3f, 0x69, 0x9e, 0x9e, 0x09, 0x10, 0x18, 0x18, 0x66, 0xaa, 0xff, 0x18, 0x66, 0xaa, 0xff, 0x9e, 0x66, 0xaa, 0xff, 0x18,
0x01, 0x02, 0x04, 0x04, 0x0a, 0x10, 0x18, 0x18, 0x01, 0x02, 0x04, 0x04, 0x66, 0xaa, 0xff, 0x04, 0x66, 0xaa, 0xff, 0x18, 0x66, 0xaa, 0xff, 0x04,
}, },
}, },
}, },
@@ -72,9 +72,9 @@ func TestBlur(t *testing.T) {
Rect: image.Rect(0, 0, 3, 3), Rect: image.Rect(0, 0, 3, 3),
Stride: 3 * 4, Stride: 3 * 4,
Pix: []uint8{ Pix: []uint8{
0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, 0x66, 0xaa, 0xff, 0x1c, 0x66, 0xaa, 0xff, 0x1c, 0x66, 0xaa, 0xff, 0x1c,
0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, 0x66, 0xaa, 0xff, 0x1c, 0x66, 0xaa, 0xff, 0x1c, 0x66, 0xaa, 0xff, 0x1c,
0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, 0x0b, 0x13, 0x1c, 0x1c, 0x66, 0xaa, 0xff, 0x1c, 0x66, 0xaa, 0xff, 0x1c, 0x66, 0xaa, 0xff, 0x1c,
}, },
}, },
}, },
@@ -134,7 +134,7 @@ func TestSharpen(t *testing.T) {
Stride: 3 * 4, Stride: 3 * 4,
Pix: []uint8{ Pix: []uint8{
0x66, 0x66, 0x66, 0x66, 0x64, 0x64, 0x64, 0x64, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x64, 0x64, 0x64, 0x64, 0x66, 0x66, 0x66, 0x66,
0x64, 0x64, 0x64, 0x64, 0x7e, 0x7e, 0x7e, 0x7e, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x7d, 0x7d, 0x7d, 0x7e, 0x64, 0x64, 0x64, 0x64,
0x66, 0x66, 0x66, 0x66, 0x64, 0x64, 0x64, 0x64, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x64, 0x64, 0x64, 0x64, 0x66, 0x66, 0x66, 0x66,
}, },
}, },

43
vendor/github.com/disintegration/imaging/histogram.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,43 @@
package imaging
import (
"image"
)
// Histogram returns a normalized histogram of an image.
//
// Resulting histogram is represented as an array of 256 floats, where
// histogram[i] is a probability of a pixel being of a particular luminance i.
func Histogram(img image.Image) [256]float64 {
src := toNRGBA(img)
width := src.Bounds().Max.X
height := src.Bounds().Max.Y
var histogram [256]float64
var total float64
if width == 0 || height == 0 {
return histogram
}
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
i := y*src.Stride + x*4
r := src.Pix[i+0]
g := src.Pix[i+1]
b := src.Pix[i+2]
var y float32 = 0.299*float32(r) + 0.587*float32(g) + 0.114*float32(b)
histogram[int(y+0.5)]++
total++
}
}
for i := 0; i < 256; i++ {
histogram[i] = histogram[i] / total
}
return histogram
}

42
vendor/github.com/disintegration/imaging/histogram_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,42 @@
package imaging
import (
"image"
"image/color"
"testing"
)
func TestHistogram(t *testing.T) {
b := image.Rectangle{image.Point{0, 0}, image.Point{2, 2}}
i1 := image.NewRGBA(b)
i1.Set(0, 0, image.Black)
i1.Set(1, 0, image.White)
i1.Set(1, 1, image.White)
i1.Set(0, 1, color.Gray{123})
h := Histogram(i1)
if h[0] != 0.25 || h[123] != 0.25 || h[255] != 0.5 {
t.Errorf("Incorrect histogram for image i1")
}
i2 := image.NewRGBA(b)
i2.Set(0, 0, color.Gray{51})
i2.Set(0, 1, color.Gray{14})
i2.Set(1, 0, color.Gray{14})
h = Histogram(i2)
if h[14] != 0.5 || h[51] != 0.25 || h[0] != 0.25 {
t.Errorf("Incorrect histogram for image i2")
}
b = image.Rectangle{image.Point{0, 0}, image.Point{0, 0}}
i3 := image.NewRGBA(b)
h = Histogram(i3)
for _, val := range h {
if val != 0 {
t.Errorf("Histogram for an empty image should be a zero histogram.")
return
}
}
}

38
vendor/github.com/disintegration/imaging/resize.go сгенерированный поставляемый
Просмотреть файл

@@ -128,20 +128,21 @@ func resizeHorizontal(src *image.NRGBA, width int, filter ResampleFilter) *image
parallel(dstH, func(partStart, partEnd int) { parallel(dstH, func(partStart, partEnd int) {
for dstY := partStart; dstY < partEnd; dstY++ { for dstY := partStart; dstY < partEnd; dstY++ {
for dstX := 0; dstX < dstW; dstX++ { for dstX := 0; dstX < dstW; dstX++ {
var c [4]int32 var c [4]int64
for _, iw := range weights[dstX].iwpairs { for _, iw := range weights[dstX].iwpairs {
i := dstY*src.Stride + iw.i*4 i := dstY*src.Stride + iw.i*4
c[0] += int32(src.Pix[i+0]) * iw.w a := int64(src.Pix[i+3]) * int64(iw.w)
c[1] += int32(src.Pix[i+1]) * iw.w c[0] += int64(src.Pix[i+0]) * a
c[2] += int32(src.Pix[i+2]) * iw.w c[1] += int64(src.Pix[i+1]) * a
c[3] += int32(src.Pix[i+3]) * iw.w c[2] += int64(src.Pix[i+2]) * a
c[3] += a
} }
j := dstY*dst.Stride + dstX*4 j := dstY*dst.Stride + dstX*4
sum := weights[dstX].wsum sum := weights[dstX].wsum
dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5)) dst.Pix[j+0] = clampint32(int32(float64(c[0])/float64(c[3]) + 0.5))
dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5)) dst.Pix[j+1] = clampint32(int32(float64(c[1])/float64(c[3]) + 0.5))
dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5)) dst.Pix[j+2] = clampint32(int32(float64(c[2])/float64(c[3]) + 0.5))
dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5)) dst.Pix[j+3] = clampint32(int32(float64(c[3])/float64(sum) + 0.5))
} }
} }
}) })
@@ -165,20 +166,21 @@ func resizeVertical(src *image.NRGBA, height int, filter ResampleFilter) *image.
for dstX := partStart; dstX < partEnd; dstX++ { for dstX := partStart; dstX < partEnd; dstX++ {
for dstY := 0; dstY < dstH; dstY++ { for dstY := 0; dstY < dstH; dstY++ {
var c [4]int32 var c [4]int64
for _, iw := range weights[dstY].iwpairs { for _, iw := range weights[dstY].iwpairs {
i := iw.i*src.Stride + dstX*4 i := iw.i*src.Stride + dstX*4
c[0] += int32(src.Pix[i+0]) * iw.w a := int64(src.Pix[i+3]) * int64(iw.w)
c[1] += int32(src.Pix[i+1]) * iw.w c[0] += int64(src.Pix[i+0]) * a
c[2] += int32(src.Pix[i+2]) * iw.w c[1] += int64(src.Pix[i+1]) * a
c[3] += int32(src.Pix[i+3]) * iw.w c[2] += int64(src.Pix[i+2]) * a
c[3] += a
} }
j := dstY*dst.Stride + dstX*4 j := dstY*dst.Stride + dstX*4
sum := weights[dstY].wsum sum := weights[dstY].wsum
dst.Pix[j+0] = clampint32(int32(float32(c[0])/float32(sum) + 0.5)) dst.Pix[j+0] = clampint32(int32(float64(c[0])/float64(c[3]) + 0.5))
dst.Pix[j+1] = clampint32(int32(float32(c[1])/float32(sum) + 0.5)) dst.Pix[j+1] = clampint32(int32(float64(c[1])/float64(c[3]) + 0.5))
dst.Pix[j+2] = clampint32(int32(float32(c[2])/float32(sum) + 0.5)) dst.Pix[j+2] = clampint32(int32(float64(c[2])/float64(c[3]) + 0.5))
dst.Pix[j+3] = clampint32(int32(float32(c[3])/float32(sum) + 0.5)) dst.Pix[j+3] = clampint32(int32(float64(c[3])/float64(sum) + 0.5))
} }
} }

18
vendor/github.com/disintegration/imaging/resize_test.go сгенерированный поставляемый
Просмотреть файл

@@ -28,7 +28,7 @@ func TestResize(t *testing.T) {
&image.NRGBA{ &image.NRGBA{
Rect: image.Rect(0, 0, 1, 1), Rect: image.Rect(0, 0, 1, 1),
Stride: 1 * 4, Stride: 1 * 4,
Pix: []uint8{0x40, 0x40, 0x40, 0xc0}, Pix: []uint8{0x55, 0x55, 0x55, 0xc0},
}, },
}, },
{ {
@@ -108,10 +108,10 @@ func TestResize(t *testing.T) {
Rect: image.Rect(0, 0, 4, 4), Rect: image.Rect(0, 0, 4, 4),
Stride: 4 * 4, Stride: 4 * 4,
Pix: []uint8{ Pix: []uint8{
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0xbf, 0x00, 0x00, 0xbf, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0xc0, 0xff, 0x00, 0x00, 0xff,
0x00, 0x40, 0x00, 0x40, 0x30, 0x30, 0x10, 0x70, 0x8f, 0x10, 0x30, 0xcf, 0xbf, 0x00, 0x40, 0xff, 0x00, 0xff, 0x00, 0x3f, 0x6d, 0x6e, 0x24, 0x6f, 0xb1, 0x13, 0x3a, 0xd0, 0xc0, 0x00, 0x3f, 0xff,
0x00, 0xbf, 0x00, 0xbf, 0x10, 0x8f, 0x30, 0xcf, 0x30, 0x30, 0x8f, 0xef, 0x40, 0x00, 0xbf, 0xff, 0x00, 0xff, 0x00, 0xc0, 0x13, 0xb2, 0x3a, 0xcf, 0x33, 0x32, 0x9a, 0xef, 0x3f, 0x00, 0xc0, 0xff,
0x00, 0xff, 0x00, 0xff, 0x00, 0xbf, 0x40, 0xff, 0x00, 0x40, 0xbf, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xc0, 0x3f, 0xff, 0x00, 0x3f, 0xc0, 0xff, 0x00, 0x00, 0xff, 0xff,
}, },
}, },
}, },
@@ -224,7 +224,7 @@ func TestFit(t *testing.T) {
&image.NRGBA{ &image.NRGBA{
Rect: image.Rect(0, 0, 1, 1), Rect: image.Rect(0, 0, 1, 1),
Stride: 1 * 4, Stride: 1 * 4,
Pix: []uint8{0x40, 0x40, 0x40, 0xc0}, Pix: []uint8{0x55, 0x55, 0x55, 0xc0},
}, },
}, },
{ {
@@ -242,7 +242,7 @@ func TestFit(t *testing.T) {
&image.NRGBA{ &image.NRGBA{
Rect: image.Rect(0, 0, 1, 1), Rect: image.Rect(0, 0, 1, 1),
Stride: 1 * 4, Stride: 1 * 4,
Pix: []uint8{0x40, 0x40, 0x40, 0xc0}, Pix: []uint8{0x55, 0x55, 0x55, 0xc0},
}, },
}, },
{ {
@@ -512,7 +512,7 @@ func TestThumbnail(t *testing.T) {
&image.NRGBA{ &image.NRGBA{
Rect: image.Rect(0, 0, 1, 1), Rect: image.Rect(0, 0, 1, 1),
Stride: 1 * 4, Stride: 1 * 4,
Pix: []uint8{0x40, 0x40, 0x40, 0xc0}, Pix: []uint8{0x55, 0x55, 0x55, 0xc0},
}, },
}, },
{ {
@@ -534,7 +534,7 @@ func TestThumbnail(t *testing.T) {
&image.NRGBA{ &image.NRGBA{
Rect: image.Rect(0, 0, 1, 1), Rect: image.Rect(0, 0, 1, 1),
Stride: 1 * 4, Stride: 1 * 4,
Pix: []uint8{0x40, 0x40, 0x40, 0xc0}, Pix: []uint8{0x55, 0x55, 0x55, 0xc0},
}, },
}, },
{ {

9
vendor/github.com/go-ldap/ldap/.travis.yml сгенерированный поставляемый
Просмотреть файл

@@ -1,15 +1,20 @@
language: go language: go
env: env:
global: global:
- VET_VERSIONS="1.5 1.6 tip" - VET_VERSIONS="1.6 1.7 tip"
- LINT_VERSIONS="1.5 1.6 tip" - LINT_VERSIONS="1.6 1.7 tip"
go: go:
- 1.2 - 1.2
- 1.3 - 1.3
- 1.4 - 1.4
- 1.5 - 1.5
- 1.6 - 1.6
- 1.7
- tip - tip
matrix:
fast_finish: true
allow_failures:
- go: tip
go_import_path: gopkg.in/ldap.v2 go_import_path: gopkg.in/ldap.v2
install: install:
- go get gopkg.in/asn1-ber.v1 - go get gopkg.in/asn1-ber.v1

43
vendor/github.com/go-ldap/ldap/LICENSE сгенерированный поставляемый
Просмотреть файл

@@ -1,27 +1,22 @@
Copyright (c) 2012 The Go Authors. All rights reserved. The MIT License (MIT)
Redistribution and use in source and binary forms, with or without Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)
modification, are permitted provided that the following conditions are Portions copyright (c) 2015-2016 go-ldap Authors
met:
* Redistributions of source code must retain the above copyright Permission is hereby granted, free of charge, to any person obtaining a copy
notice, this list of conditions and the following disclaimer. of this software and associated documentation files (the "Software"), to deal
* Redistributions in binary form must reproduce the above in the Software without restriction, including without limitation the rights
copyright notice, this list of conditions and the following disclaimer to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
in the documentation and/or other materials provided with the copies of the Software, and to permit persons to whom the Software is
distribution. furnished to do so, subject to the following conditions:
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS The above copyright notice and this permission notice shall be included in all
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT copies or substantial portions of the Software.
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE SOFTWARE.
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

12
vendor/github.com/go-ldap/ldap/control.go сгенерированный поставляемый
Просмотреть файл

@@ -334,18 +334,18 @@ func DecodeControl(packet *ber.Packet) Control {
for _, child := range sequence.Children { for _, child := range sequence.Children {
if child.Tag == 0 { if child.Tag == 0 {
//Warning //Warning
child := child.Children[0] warningPacket := child.Children[0]
packet := ber.DecodePacket(child.Data.Bytes()) packet := ber.DecodePacket(warningPacket.Data.Bytes())
val, ok := packet.Value.(int64) val, ok := packet.Value.(int64)
if ok { if ok {
if child.Tag == 0 { if warningPacket.Tag == 0 {
//timeBeforeExpiration //timeBeforeExpiration
c.Expire = val c.Expire = val
child.Value = c.Expire warningPacket.Value = c.Expire
} else if child.Tag == 1 { } else if warningPacket.Tag == 1 {
//graceAuthNsRemaining //graceAuthNsRemaining
c.Grace = val c.Grace = val
child.Value = c.Grace warningPacket.Value = c.Grace
} }
} }
} else if child.Tag == 1 { } else if child.Tag == 1 {

39
vendor/github.com/go-ldap/ldap/control_test.go сгенерированный поставляемый
Просмотреть файл

@@ -56,3 +56,42 @@ func runControlTest(t *testing.T, originalControl Control) {
t.Errorf("%sgot different type decoding from encoded bytes: %T vs %T", header, fromBytes, originalControl) t.Errorf("%sgot different type decoding from encoded bytes: %T vs %T", header, fromBytes, originalControl)
} }
} }
func TestDescribeControlManageDsaIT(t *testing.T) {
runAddControlDescriptions(t, NewControlManageDsaIT(false), "Control Type (Manage DSA IT)")
runAddControlDescriptions(t, NewControlManageDsaIT(true), "Control Type (Manage DSA IT)", "Criticality")
}
func TestDescribeControlPaging(t *testing.T) {
runAddControlDescriptions(t, NewControlPaging(100), "Control Type (Paging)", "Control Value (Paging)")
runAddControlDescriptions(t, NewControlPaging(0), "Control Type (Paging)", "Control Value (Paging)")
}
func TestDescribeControlString(t *testing.T) {
runAddControlDescriptions(t, NewControlString("x", true, "y"), "Control Type ()", "Criticality", "Control Value")
runAddControlDescriptions(t, NewControlString("x", true, ""), "Control Type ()", "Criticality", "Control Value")
runAddControlDescriptions(t, NewControlString("x", false, "y"), "Control Type ()", "Control Value")
runAddControlDescriptions(t, NewControlString("x", false, ""), "Control Type ()", "Control Value")
}
func runAddControlDescriptions(t *testing.T, originalControl Control, childDescriptions ...string) {
header := ""
if callerpc, _, line, ok := runtime.Caller(1); ok {
if caller := runtime.FuncForPC(callerpc); caller != nil {
header = fmt.Sprintf("%s:%d: ", caller.Name(), line)
}
}
encodedControls := encodeControls([]Control{originalControl})
addControlDescriptions(encodedControls)
encodedPacket := encodedControls.Children[0]
if len(encodedPacket.Children) != len(childDescriptions) {
t.Errorf("%sinvalid number of children: %d != %d", header, len(encodedPacket.Children), len(childDescriptions))
}
for i, desc := range childDescriptions {
if encodedPacket.Children[i].Description != desc {
t.Errorf("%sdescription not as expected: %s != %s", header, encodedPacket.Children[i].Description, desc)
}
}
}

96
vendor/github.com/go-ldap/ldap/dn.go сгенерированный поставляемый
Просмотреть файл

@@ -83,9 +83,19 @@ func ParseDN(str string) (*DN, error) {
attribute := new(AttributeTypeAndValue) attribute := new(AttributeTypeAndValue)
escaping := false escaping := false
unescapedTrailingSpaces := 0
stringFromBuffer := func() string {
s := buffer.String()
s = s[0 : len(s)-unescapedTrailingSpaces]
buffer.Reset()
unescapedTrailingSpaces = 0
return s
}
for i := 0; i < len(str); i++ { for i := 0; i < len(str); i++ {
char := str[i] char := str[i]
if escaping { if escaping {
unescapedTrailingSpaces = 0
escaping = false escaping = false
switch char { switch char {
case ' ', '"', '#', '+', ',', ';', '<', '=', '>', '\\': case ' ', '"', '#', '+', ',', ';', '<', '=', '>', '\\':
@@ -107,10 +117,10 @@ func ParseDN(str string) (*DN, error) {
buffer.WriteByte(dst[0]) buffer.WriteByte(dst[0])
i++ i++
} else if char == '\\' { } else if char == '\\' {
unescapedTrailingSpaces = 0
escaping = true escaping = true
} else if char == '=' { } else if char == '=' {
attribute.Type = buffer.String() attribute.Type = stringFromBuffer()
buffer.Reset()
// Special case: If the first character in the value is # the // Special case: If the first character in the value is # the
// following data is BER encoded so we can just fast forward // following data is BER encoded so we can just fast forward
// and decode. // and decode.
@@ -133,7 +143,7 @@ func ParseDN(str string) (*DN, error) {
} }
} else if char == ',' || char == '+' { } else if char == ',' || char == '+' {
// We're done with this RDN or value, push it // We're done with this RDN or value, push it
attribute.Value = buffer.String() attribute.Value = stringFromBuffer()
rdn.Attributes = append(rdn.Attributes, attribute) rdn.Attributes = append(rdn.Attributes, attribute)
attribute = new(AttributeTypeAndValue) attribute = new(AttributeTypeAndValue)
if char == ',' { if char == ',' {
@@ -141,8 +151,17 @@ func ParseDN(str string) (*DN, error) {
rdn = new(RelativeDN) rdn = new(RelativeDN)
rdn.Attributes = make([]*AttributeTypeAndValue, 0) rdn.Attributes = make([]*AttributeTypeAndValue, 0)
} }
buffer.Reset() } else if char == ' ' && buffer.Len() == 0 {
// ignore unescaped leading spaces
continue
} else { } else {
if char == ' ' {
// Track unescaped spaces in case they are trailing and we need to remove them
unescapedTrailingSpaces++
} else {
// Reset if we see a non-space char
unescapedTrailingSpaces = 0
}
buffer.WriteByte(char) buffer.WriteByte(char)
} }
} }
@@ -150,9 +169,76 @@ func ParseDN(str string) (*DN, error) {
if len(attribute.Type) == 0 { if len(attribute.Type) == 0 {
return nil, errors.New("DN ended with incomplete type, value pair") return nil, errors.New("DN ended with incomplete type, value pair")
} }
attribute.Value = buffer.String() attribute.Value = stringFromBuffer()
rdn.Attributes = append(rdn.Attributes, attribute) rdn.Attributes = append(rdn.Attributes, attribute)
dn.RDNs = append(dn.RDNs, rdn) dn.RDNs = append(dn.RDNs, rdn)
} }
return dn, nil return dn, nil
} }
// Equal returns true if the DNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
// Returns true if they have the same number of relative distinguished names
// and corresponding relative distinguished names (by position) are the same.
func (d *DN) Equal(other *DN) bool {
if len(d.RDNs) != len(other.RDNs) {
return false
}
for i := range d.RDNs {
if !d.RDNs[i].Equal(other.RDNs[i]) {
return false
}
}
return true
}
// AncestorOf returns true if the other DN consists of at least one RDN followed by all the RDNs of the current DN.
// "ou=widgets,o=acme.com" is an ancestor of "ou=sprockets,ou=widgets,o=acme.com"
// "ou=widgets,o=acme.com" is not an ancestor of "ou=sprockets,ou=widgets,o=foo.com"
// "ou=widgets,o=acme.com" is not an ancestor of "ou=widgets,o=acme.com"
func (d *DN) AncestorOf(other *DN) bool {
if len(d.RDNs) >= len(other.RDNs) {
return false
}
// Take the last `len(d.RDNs)` RDNs from the other DN to compare against
otherRDNs := other.RDNs[len(other.RDNs)-len(d.RDNs):]
for i := range d.RDNs {
if !d.RDNs[i].Equal(otherRDNs[i]) {
return false
}
}
return true
}
// Equal returns true if the RelativeDNs are equal as defined by rfc4517 4.2.15 (distinguishedNameMatch).
// Relative distinguished names are the same if and only if they have the same number of AttributeTypeAndValues
// and each attribute of the first RDN is the same as the attribute of the second RDN with the same attribute type.
// The order of attributes is not significant.
// Case of attribute types is not significant.
func (r *RelativeDN) Equal(other *RelativeDN) bool {
if len(r.Attributes) != len(other.Attributes) {
return false
}
return r.hasAllAttributes(other.Attributes) && other.hasAllAttributes(r.Attributes)
}
func (r *RelativeDN) hasAllAttributes(attrs []*AttributeTypeAndValue) bool {
for _, attr := range attrs {
found := false
for _, myattr := range r.Attributes {
if myattr.Equal(attr) {
found = true
break
}
}
if !found {
return false
}
}
return true
}
// Equal returns true if the AttributeTypeAndValue is equivalent to the specified AttributeTypeAndValue
// Case of the attribute type is not significant
func (a *AttributeTypeAndValue) Equal(other *AttributeTypeAndValue) bool {
return strings.EqualFold(a.Type, other.Type) && a.Value == other.Value
}

139
vendor/github.com/go-ldap/ldap/dn_test.go сгенерированный поставляемый
Просмотреть файл

@@ -31,6 +31,22 @@ func TestSuccessfulDNParsing(t *testing.T) {
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}}, &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}},
"CN=Lu\\C4\\8Di\\C4\\87": ldap.DN{[]*ldap.RelativeDN{ "CN=Lu\\C4\\8Di\\C4\\87": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"CN", "Lučić"}}}}}, &ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"CN", "Lučić"}}}}},
" CN = Lu\\C4\\8Di\\C4\\87 ": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"CN", "Lučić"}}}}},
` A = 1 , B = 2 `: ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"A", "1"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"B", "2"}}}}},
` A = 1 + B = 2 `: ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{
&ldap.AttributeTypeAndValue{"A", "1"},
&ldap.AttributeTypeAndValue{"B", "2"}}}}},
` \ \ A\ \ = \ \ 1\ \ , \ \ B\ \ = \ \ 2\ \ `: ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{" A ", " 1 "}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{" B ", " 2 "}}}}},
` \ \ A\ \ = \ \ 1\ \ + \ \ B\ \ = \ \ 2\ \ `: ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{
&ldap.AttributeTypeAndValue{" A ", " 1 "},
&ldap.AttributeTypeAndValue{" B ", " 2 "}}}}},
} }
for test, answer := range testcases { for test, answer := range testcases {
@@ -41,6 +57,13 @@ func TestSuccessfulDNParsing(t *testing.T) {
} }
if !reflect.DeepEqual(dn, &answer) { if !reflect.DeepEqual(dn, &answer) {
t.Errorf("Parsed DN %s is not equal to the expected structure", test) t.Errorf("Parsed DN %s is not equal to the expected structure", test)
t.Logf("Expected:")
for _, rdn := range answer.RDNs {
for _, attribs := range rdn.Attributes {
t.Logf("#%v\n", attribs)
}
}
t.Logf("Actual:")
for _, rdn := range dn.RDNs { for _, rdn := range dn.RDNs {
for _, attribs := range rdn.Attributes { for _, attribs := range rdn.Attributes {
t.Logf("#%v\n", attribs) t.Logf("#%v\n", attribs)
@@ -68,3 +91,119 @@ func TestErrorDNParsing(t *testing.T) {
} }
} }
} }
func TestDNEqual(t *testing.T) {
testcases := []struct {
A string
B string
Equal bool
}{
// Exact match
{"", "", true},
{"o=A", "o=A", true},
{"o=A", "o=B", false},
{"o=A,o=B", "o=A,o=B", true},
{"o=A,o=B", "o=A,o=C", false},
{"o=A+o=B", "o=A+o=B", true},
{"o=A+o=B", "o=A+o=C", false},
// Case mismatch in type is ignored
{"o=A", "O=A", true},
{"o=A,o=B", "o=A,O=B", true},
{"o=A+o=B", "o=A+O=B", true},
// Case mismatch in value is significant
{"o=a", "O=A", false},
{"o=a,o=B", "o=A,O=B", false},
{"o=a+o=B", "o=A+O=B", false},
// Multi-valued RDN order mismatch is ignored
{"o=A+o=B", "O=B+o=A", true},
// Number of RDN attributes is significant
{"o=A+o=B", "O=B+o=A+O=B", false},
// Missing values are significant
{"o=A+o=B", "O=B+o=A+O=C", false}, // missing values matter
{"o=A+o=B+o=C", "O=B+o=A", false}, // missing values matter
// Whitespace tests
// Matching
{
"cn=John Doe, ou=People, dc=sun.com",
"cn=John Doe, ou=People, dc=sun.com",
true,
},
// Difference in leading/trailing chars is ignored
{
"cn=John Doe, ou=People, dc=sun.com",
"cn=John Doe,ou=People,dc=sun.com",
true,
},
// Difference in values is significant
{
"cn=John Doe, ou=People, dc=sun.com",
"cn=John Doe, ou=People, dc=sun.com",
false,
},
}
for i, tc := range testcases {
a, err := ldap.ParseDN(tc.A)
if err != nil {
t.Errorf("%d: %v", i, err)
continue
}
b, err := ldap.ParseDN(tc.B)
if err != nil {
t.Errorf("%d: %v", i, err)
continue
}
if expected, actual := tc.Equal, a.Equal(b); expected != actual {
t.Errorf("%d: when comparing '%s' and '%s' expected %v, got %v", i, tc.A, tc.B, expected, actual)
continue
}
if expected, actual := tc.Equal, b.Equal(a); expected != actual {
t.Errorf("%d: when comparing '%s' and '%s' expected %v, got %v", i, tc.A, tc.B, expected, actual)
continue
}
}
}
func TestDNAncestor(t *testing.T) {
testcases := []struct {
A string
B string
Ancestor bool
}{
// Exact match returns false
{"", "", false},
{"o=A", "o=A", false},
{"o=A,o=B", "o=A,o=B", false},
{"o=A+o=B", "o=A+o=B", false},
// Mismatch
{"ou=C,ou=B,o=A", "ou=E,ou=D,ou=B,o=A", false},
// Descendant
{"ou=C,ou=B,o=A", "ou=E,ou=C,ou=B,o=A", true},
}
for i, tc := range testcases {
a, err := ldap.ParseDN(tc.A)
if err != nil {
t.Errorf("%d: %v", i, err)
continue
}
b, err := ldap.ParseDN(tc.B)
if err != nil {
t.Errorf("%d: %v", i, err)
continue
}
if expected, actual := tc.Ancestor, a.AncestorOf(b); expected != actual {
t.Errorf("%d: when comparing '%s' and '%s' expected %v, got %v", i, tc.A, tc.B, expected, actual)
continue
}
}
}

59
vendor/github.com/go-ldap/ldap/ldap.go сгенерированный поставляемый
Просмотреть файл

@@ -153,16 +153,47 @@ func addLDAPDescriptions(packet *ber.Packet) (err error) {
func addControlDescriptions(packet *ber.Packet) { func addControlDescriptions(packet *ber.Packet) {
packet.Description = "Controls" packet.Description = "Controls"
for _, child := range packet.Children { for _, child := range packet.Children {
var value *ber.Packet
controlType := ""
child.Description = "Control" child.Description = "Control"
child.Children[0].Description = "Control Type (" + ControlTypeMap[child.Children[0].Value.(string)] + ")" switch len(child.Children) {
value := child.Children[1] case 0:
if len(child.Children) == 3 { // at least one child is required for control type
child.Children[1].Description = "Criticality" continue
value = child.Children[2]
}
value.Description = "Control Value"
switch child.Children[0].Value.(string) { case 1:
// just type, no criticality or value
controlType = child.Children[0].Value.(string)
child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
case 2:
controlType = child.Children[0].Value.(string)
child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
// Children[1] could be criticality or value (both are optional)
// duck-type on whether this is a boolean
if _, ok := child.Children[1].Value.(bool); ok {
child.Children[1].Description = "Criticality"
} else {
child.Children[1].Description = "Control Value"
value = child.Children[1]
}
case 3:
// criticality and value present
controlType = child.Children[0].Value.(string)
child.Children[0].Description = "Control Type (" + ControlTypeMap[controlType] + ")"
child.Children[1].Description = "Criticality"
child.Children[2].Description = "Control Value"
value = child.Children[2]
default:
// more than 3 children is invalid
continue
}
if value == nil {
continue
}
switch controlType {
case ControlTypePaging: case ControlTypePaging:
value.Description += " (Paging)" value.Description += " (Paging)"
if value.Value != nil { if value.Value != nil {
@@ -188,18 +219,18 @@ func addControlDescriptions(packet *ber.Packet) {
for _, child := range sequence.Children { for _, child := range sequence.Children {
if child.Tag == 0 { if child.Tag == 0 {
//Warning //Warning
child := child.Children[0] warningPacket := child.Children[0]
packet := ber.DecodePacket(child.Data.Bytes()) packet := ber.DecodePacket(warningPacket.Data.Bytes())
val, ok := packet.Value.(int64) val, ok := packet.Value.(int64)
if ok { if ok {
if child.Tag == 0 { if warningPacket.Tag == 0 {
//timeBeforeExpiration //timeBeforeExpiration
value.Description += " (TimeBeforeExpiration)" value.Description += " (TimeBeforeExpiration)"
child.Value = val warningPacket.Value = val
} else if child.Tag == 1 { } else if warningPacket.Tag == 1 {
//graceAuthNsRemaining //graceAuthNsRemaining
value.Description += " (GraceAuthNsRemaining)" value.Description += " (GraceAuthNsRemaining)"
child.Value = val warningPacket.Value = val
} }
} }
} else if child.Tag == 1 { } else if child.Tag == 1 {

21
vendor/github.com/go-sql-driver/mysql/.github/ISSUE_TEMPLATE.md сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,21 @@
### Issue description
Tell us what should happen and what happens instead
### Example code
```go
If possible, please enter some example code here to reproduce the issue.
```
### Error log
```
If you have an error log, please paste it here.
```
### Configuration
*Driver version (or git SHA):*
*Go version:* run `go version` in your console
*Server version:* E.g. MySQL 5.6, MariaDB 10.0.20
*Server OS:* E.g. Debian 8.1 (Jessie), Windows 10

9
vendor/github.com/go-sql-driver/mysql/.github/PULL_REQUEST_TEMPLATE.md сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,9 @@
### Description
Please explain the changes you made here.
### Checklist
- [ ] Code compiles correctly
- [ ] Created tests which fail without the change (if possible)
- [ ] All tests passing
- [ ] Extended the README / documentation, if necessary
- [ ] Added myself / the copyright holder to the AUTHORS file

8
vendor/github.com/go-sql-driver/mysql/.travis.yml сгенерированный поставляемый
Просмотреть файл

@@ -1,6 +1,12 @@
sudo: false
language: go language: go
go: go:
- 1.1 - 1.2
- 1.3
- 1.4
- 1.5
- 1.6
- 1.7
- tip - tip
before_script: before_script:

21
vendor/github.com/go-sql-driver/mysql/AUTHORS сгенерированный поставляемый
Просмотреть файл

@@ -14,22 +14,43 @@
Aaron Hopkins <go-sql-driver at die.net> Aaron Hopkins <go-sql-driver at die.net>
Arne Hormann <arnehormann at gmail.com> Arne Hormann <arnehormann at gmail.com>
Carlos Nieto <jose.carlos at menteslibres.net> Carlos Nieto <jose.carlos at menteslibres.net>
Chris Moos <chris at tech9computers.com>
Daniel Nichter <nil at codenode.com>
Daniël van Eeden <git at myname.nl>
DisposaBoy <disposaboy at dby.me> DisposaBoy <disposaboy at dby.me>
Frederick Mayle <frederickmayle at gmail.com> Frederick Mayle <frederickmayle at gmail.com>
Gustavo Kristic <gkristic at gmail.com> Gustavo Kristic <gkristic at gmail.com>
Hanno Braun <mail at hannobraun.com> Hanno Braun <mail at hannobraun.com>
Henri Yandell <flamefew at gmail.com>
Hirotaka Yamamoto <ymmt2005 at gmail.com>
INADA Naoki <songofacandy at gmail.com>
James Harr <james.harr at gmail.com> James Harr <james.harr at gmail.com>
Jian Zhen <zhenjl 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> Julien Schmidt <go-sql-driver at julienschmidt.com>
Kamil Dziedzic <kamil at klecza.pl>
Kevin Malachowski <kevin at chowski.com>
Lennart Rudolph <lrudolph at hmc.edu>
Leonardo YongUk Kim <dalinaum at gmail.com> Leonardo YongUk Kim <dalinaum at gmail.com>
Luca Looz <luca.looz92 at gmail.com>
Lucas Liu <extrafliu at gmail.com> Lucas Liu <extrafliu at gmail.com>
Luke Scott <luke at webconnex.com> Luke Scott <luke at webconnex.com>
Michael Woolnough <michael.woolnough at gmail.com> Michael Woolnough <michael.woolnough at gmail.com>
Nicola Peduzzi <thenikso at gmail.com> Nicola Peduzzi <thenikso at gmail.com>
Olivier Mengué <dolmen at cpan.org>
Paul Bonser <misterpib at gmail.com>
Runrioter Wung <runrioter at gmail.com>
Soroush Pour <me at soroushjp.com>
Stan Putrya <root.vagner at gmail.com>
Stanley Gunawan <gunawan.stanley at gmail.com>
Xiangyu Hu <xiangyu.hu at outlook.com>
Xiaobing Jiang <s7v7nislands at gmail.com> Xiaobing Jiang <s7v7nislands at gmail.com>
Xiuming Chen <cc at cxm.cc> Xiuming Chen <cc at cxm.cc>
Zhenye Xie <xiezhenye at gmail.com>
# Organizations # Organizations
Barracuda Networks, Inc. Barracuda Networks, Inc.
Google Inc. Google Inc.
Stripe Inc.

47
vendor/github.com/go-sql-driver/mysql/CHANGELOG.md сгенерированный поставляемый
Просмотреть файл

@@ -1,3 +1,50 @@
## Version 1.3 (2016-12-01)
Changes:
- Go 1.1 is no longer supported
- Use decimals fields in MySQL to format time types (#249)
- Buffer optimizations (#269)
- TLS ServerName defaults to the host (#283)
- Refactoring (#400, #410, #437)
- Adjusted documentation for second generation CloudSQL (#485)
- Documented DSN system var quoting rules (#502)
- Made statement.Close() calls idempotent to avoid errors in Go 1.6+ (#512)
New Features:
- Enable microsecond resolution on TIME, DATETIME and TIMESTAMP (#249)
- Support for returning table alias on Columns() (#289, #359, #382)
- Placeholder interpolation, can be actived with the DSN parameter `interpolateParams=true` (#309, #318, #490)
- Support for uint64 parameters with high bit set (#332, #345)
- Cleartext authentication plugin support (#327)
- Exported ParseDSN function and the Config struct (#403, #419, #429)
- Read / Write timeouts (#401)
- Support for JSON field type (#414)
- Support for multi-statements and multi-results (#411, #431)
- DSN parameter to set the driver-side max_allowed_packet value manually (#489)
- Native password authentication plugin support (#494, #524)
Bugfixes:
- Fixed handling of queries without columns and rows (#255)
- Fixed a panic when SetKeepAlive() failed (#298)
- Handle ERR packets while reading rows (#321)
- Fixed reading NULL length-encoded integers in MySQL 5.6+ (#349)
- Fixed absolute paths support in LOAD LOCAL DATA INFILE (#356)
- Actually zero out bytes in handshake response (#378)
- Fixed race condition in registering LOAD DATA INFILE handler (#383)
- Fixed tests with MySQL 5.7.9+ (#380)
- QueryUnescape TLS config names (#397)
- Fixed "broken pipe" error by writing to closed socket (#390)
- Fixed LOAD LOCAL DATA INFILE buffering (#424)
- Fixed parsing of floats into float64 when placeholders are used (#434)
- Fixed DSN tests with Go 1.7+ (#459)
- Handle ERR packets while waiting for EOF (#473)
- Invalidate connection on error while discarding additional results (#513)
- Allow terminating packets of length 0 (#516)
## Version 1.2 (2014-06-03) ## Version 1.2 (2014-06-03)
Changes: Changes:

17
vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md сгенерированный поставляемый
Просмотреть файл

@@ -4,28 +4,11 @@
Before creating a new Issue, please check first if a similar Issue [already exists](https://github.com/go-sql-driver/mysql/issues?state=open) or was [recently closed](https://github.com/go-sql-driver/mysql/issues?direction=desc&page=1&sort=updated&state=closed). Before creating a new Issue, please check first if a similar Issue [already exists](https://github.com/go-sql-driver/mysql/issues?state=open) or was [recently closed](https://github.com/go-sql-driver/mysql/issues?direction=desc&page=1&sort=updated&state=closed).
Please provide the following minimum information:
* Your Go-MySQL-Driver version (or git SHA)
* Your Go version (run `go version` in your console)
* A detailed issue description
* Error Log if present
* If possible, a short example
## Contributing Code ## Contributing Code
By contributing to this project, you share your code under the Mozilla Public License 2, as specified in the LICENSE file. By contributing to this project, you share your code under the Mozilla Public License 2, as specified in the LICENSE file.
Don't forget to add yourself to the AUTHORS file. Don't forget to add yourself to the AUTHORS file.
### Pull Requests Checklist
Please check the following points before submitting your pull request:
- [x] Code compiles correctly
- [x] Created tests, if possible
- [x] All tests pass
- [x] Extended the README / documentation, if necessary
- [x] Added yourself to the AUTHORS file
### Code Review ### Code Review
Everyone is invited to review and comment on pull requests. Everyone is invited to review and comment on pull requests.

143
vendor/github.com/go-sql-driver/mysql/README.md сгенерированный поставляемый
Просмотреть файл

@@ -4,10 +4,6 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa
![Go-MySQL-Driver logo](https://raw.github.com/wiki/go-sql-driver/mysql/gomysql_m.png "Golang Gopher holding the MySQL Dolphin") ![Go-MySQL-Driver logo](https://raw.github.com/wiki/go-sql-driver/mysql/gomysql_m.png "Golang Gopher holding the MySQL Dolphin")
**Latest stable Release:** [Version 1.2 (June 03, 2014)](https://github.com/go-sql-driver/mysql/releases)
[![Build Status](https://travis-ci.org/go-sql-driver/mysql.png?branch=master)](https://travis-ci.org/go-sql-driver/mysql)
--------------------------------------- ---------------------------------------
* [Features](#features) * [Features](#features)
* [Requirements](#requirements) * [Requirements](#requirements)
@@ -30,7 +26,7 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa
## Features ## Features
* Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance") * Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance")
* Native Go implementation. No C-bindings, just pure Go * Native Go implementation. No C-bindings, just pure Go
* Connections over TCP/IPv4, TCP/IPv6 or Unix domain sockets * Connections over TCP/IPv4, TCP/IPv6, Unix domain sockets or [custom protocols](http://godoc.org/github.com/go-sql-driver/mysql#DialFunc)
* Automatic handling of broken connections * Automatic handling of broken connections
* Automatic Connection Pooling *(by database/sql package)* * Automatic Connection Pooling *(by database/sql package)*
* Supports queries larger than 16MB * Supports queries larger than 16MB
@@ -38,9 +34,10 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa
* Intelligent `LONG DATA` handling in prepared statements * Intelligent `LONG DATA` handling in prepared statements
* Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support * Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support
* Optional `time.Time` parsing * Optional `time.Time` parsing
* Optional placeholder interpolation
## Requirements ## Requirements
* Go 1.1 or higher * Go 1.2 or higher
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+) * MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
--------------------------------------- ---------------------------------------
@@ -92,6 +89,8 @@ This has the same effect as an empty DSN string:
``` ```
Alternatively, [Config.FormatDSN](https://godoc.org/github.com/go-sql-driver/mysql#Config.FormatDSN) can be used to create a DSN string by filling a struct.
#### Password #### Password
Passwords can consist of any character. Escaping is **not** necessary. Passwords can consist of any character. Escaping is **not** necessary.
@@ -122,6 +121,25 @@ Default: false
`allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files. `allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files.
[*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html) [*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)
##### `allowCleartextPasswords`
```
Type: bool
Valid Values: true, false
Default: false
```
`allowCleartextPasswords=true` allows using the [cleartext client side plugin](http://dev.mysql.com/doc/en/cleartext-authentication-plugin.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network.
##### `allowNativePasswords`
```
Type: bool
Valid Values: true, false
Default: false
```
`allowNativePasswords=true` allows the usage of the mysql native password method.
##### `allowOldPasswords` ##### `allowOldPasswords`
``` ```
@@ -166,6 +184,33 @@ Default: false
`clientFoundRows=true` causes an UPDATE to return the number of matching rows instead of the number of rows changed. `clientFoundRows=true` causes an UPDATE to return the number of matching rows instead of the number of rows changed.
##### `columnsWithAlias`
```
Type: bool
Valid Values: true, false
Default: false
```
When `columnsWithAlias` is true, calls to `sql.Rows.Columns()` will return the table alias and the column name separated by a dot. For example:
```
SELECT u.id FROM users as u
```
will return `u.id` instead of just `id` if `columnsWithAlias=true`.
##### `interpolateParams`
```
Type: bool
Valid Values: true, false
Default: false
```
If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`.
*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are blacklisted as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!*
##### `loc` ##### `loc`
@@ -177,8 +222,29 @@ 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](http://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](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`.
##### `maxAllowedPacket`
```
Type: decimal number
Default: 0
```
Max packet size allowed in bytes. Use `maxAllowedPacket=0` to automatically fetch the `max_allowed_packet` variable from server.
##### `multiStatements`
```
Type: bool
Valid Values: true, false
Default: false
```
Allow multiple statements in one query. While this allows batch queries, it also greatly increases the risk of SQL injections. Only the result of the first query is returned, all other results are silently discarded.
When `multiStatements` is used, `?` parameters must only be used in the first statement.
##### `parseTime` ##### `parseTime`
@@ -191,6 +257,15 @@ Default: false
`parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string` `parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string`
##### `readTimeout`
```
Type: decimal number
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"*.
##### `strict` ##### `strict`
``` ```
@@ -199,10 +274,11 @@ Valid Values: true, false
Default: false Default: false
``` ```
`strict=true` enables the strict mode in which MySQL warnings are treated as errors. `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.
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. See the [examples](#examples) for an DSN example. 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.
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` ##### `timeout`
@@ -211,8 +287,7 @@ Type: decimal number
Default: OS default Default: OS default
``` ```
*Driver* side connection timeout. The value must be a string of decimal numbers, each with optional fraction and a 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). *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).
##### `tls` ##### `tls`
@@ -224,16 +299,33 @@ 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`](http://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
##### `writeTimeout`
```
Type: decimal number
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"*.
##### System Variables ##### System Variables
All other parameters are interpreted as system variables: Any other parameters are interpreted as system variables:
* `autocommit`: `"SET autocommit=<value>"` * `<boolean_var>=<value>`: `SET <boolean_var>=<value>`
* `time_zone`: `"SET time_zone=<value>"` * `<enum_var>=<value>`: `SET <enum_var>=<value>`
* [`tx_isolation`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `"SET tx_isolation=<value>"` * `<string_var>=%27<value>%27`: `SET <string_var>='<value>'`
* `param`: `"SET <param>=<value>"`
Rules:
* 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`)
Examples:
* `autocommit=1`: `SET autocommit=1`
* [`time_zone=%27Europe%2FParis%27`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `SET time_zone='Europe/Paris'`
* [`tx_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `SET tx_isolation='REPEATABLE-READ'`
*The values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!*
#### Examples #### Examples
``` ```
@@ -248,9 +340,9 @@ root:pw@unix(/tmp/mysql.sock)/myDatabase?loc=Local
user:password@tcp(localhost:5555)/dbname?tls=skip-verify&autocommit=true user:password@tcp(localhost:5555)/dbname?tls=skip-verify&autocommit=true
``` ```
Use the [strict mode](#strict) but ignore notes: Treat warnings as errors by setting the system variable [`sql_mode`](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html):
``` ```
user:password@/dbname?strict=true&sql_notes=false user:password@/dbname?sql_mode=TRADITIONAL
``` ```
TCP via IPv6: TCP via IPv6:
@@ -263,11 +355,16 @@ TCP on a remote host, e.g. Amazon RDS:
id:password@tcp(your-amazonaws-uri.com:3306)/dbname id:password@tcp(your-amazonaws-uri.com:3306)/dbname
``` ```
Google Cloud SQL on App Engine: Google Cloud SQL on App Engine (First Generation MySQL Server):
``` ```
user@cloudsql(project-id:instance-name)/dbname user@cloudsql(project-id:instance-name)/dbname
``` ```
Google Cloud SQL on App Engine (Second Generation MySQL Server):
```
user@cloudsql(project-id:regionname:instance-name)/dbname
```
TCP using default port (3306) on localhost: TCP using default port (3306) on localhost:
``` ```
user:password@tcp/dbname?charset=utf8mb4,utf8&sys_var=esc%40ped user:password@tcp/dbname?charset=utf8mb4,utf8&sys_var=esc%40ped
@@ -291,7 +388,7 @@ import "github.com/go-sql-driver/mysql"
Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)). Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)).
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. 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](http://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details.
@@ -334,9 +431,9 @@ Mozilla summarizes the license scope as follows:
That means: That means:
* You can **use** the **unchanged** source code both in private as also commercial * You can **use** the **unchanged** source code both in private and commercially
* You **needn't publish** the source code of your library as long the files licensed under the MPL 2.0 are **unchanged** * 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 **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](http://www.mozilla.org/MPL/2.0/FAQ.html) if you have further questions regarding the license.

40
vendor/github.com/go-sql-driver/mysql/benchmark_test.go сгенерированный поставляемый
Просмотреть файл

@@ -11,10 +11,13 @@ package mysql
import ( import (
"bytes" "bytes"
"database/sql" "database/sql"
"database/sql/driver"
"math"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"testing" "testing"
"time"
) )
type TB testing.B type TB testing.B
@@ -45,7 +48,11 @@ func initDB(b *testing.B, queries ...string) *sql.DB {
db := tb.checkDB(sql.Open("mysql", dsn)) db := tb.checkDB(sql.Open("mysql", dsn))
for _, query := range queries { for _, query := range queries {
if _, err := db.Exec(query); err != nil { if _, err := db.Exec(query); err != nil {
b.Fatalf("Error on %q: %v", query, err) if w, ok := err.(MySQLWarnings); ok {
b.Logf("warning on %q: %v", query, w)
} else {
b.Fatalf("error on %q: %v", query, err)
}
} }
} }
return db return db
@@ -206,3 +213,34 @@ func BenchmarkRoundtripBin(b *testing.B) {
rows.Close() rows.Close()
} }
} }
func BenchmarkInterpolation(b *testing.B) {
mc := &mysqlConn{
cfg: &Config{
InterpolateParams: true,
Loc: time.UTC,
},
maxAllowedPacket: maxPacketSize,
maxWriteSize: maxPacketSize - 1,
buf: newBuffer(nil),
}
args := []driver.Value{
int64(42424242),
float64(math.Pi),
false,
time.Unix(1423411542, 807015000),
[]byte("bytes containing special chars ' \" \a \x00"),
"string containing special chars ' \" \a \x00",
}
q := "SELECT ?, ?, ?, ?, ?, ?"
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := mc.interpolateParams(q, args)
if err != nil {
b.Fatal(err)
}
}
}

55
vendor/github.com/go-sql-driver/mysql/buffer.go сгенерированный поставляемый
Просмотреть файл

@@ -8,7 +8,11 @@
package mysql package mysql
import "io" import (
"io"
"net"
"time"
)
const defaultBufSize = 4096 const defaultBufSize = 4096
@@ -18,25 +22,28 @@ const defaultBufSize = 4096
// The buffer is similar to bufio.Reader / Writer but zero-copy-ish // The buffer is similar to bufio.Reader / Writer but zero-copy-ish
// Also highly optimized for this particular use case. // Also highly optimized for this particular use case.
type buffer struct { type buffer struct {
buf []byte buf []byte
rd io.Reader nc net.Conn
idx int idx int
length int length int
timeout time.Duration
} }
func newBuffer(rd io.Reader) buffer { func newBuffer(nc net.Conn) buffer {
var b [defaultBufSize]byte var b [defaultBufSize]byte
return buffer{ return buffer{
buf: b[:], buf: b[:],
rd: rd, nc: nc,
} }
} }
// fill reads into the buffer until at least _need_ bytes are in it // fill reads into the buffer until at least _need_ bytes are in it
func (b *buffer) fill(need int) error { func (b *buffer) fill(need int) error {
n := b.length
// move existing data to the beginning // move existing data to the beginning
if b.length > 0 && b.idx > 0 { if n > 0 && b.idx > 0 {
copy(b.buf[0:b.length], b.buf[b.idx:]) copy(b.buf[0:n], b.buf[b.idx:])
} }
// grow buffer if necessary // grow buffer if necessary
@@ -52,19 +59,33 @@ func (b *buffer) fill(need int) error {
b.idx = 0 b.idx = 0
for { for {
n, err := b.rd.Read(b.buf[b.length:]) if b.timeout > 0 {
b.length += n if err := b.nc.SetReadDeadline(time.Now().Add(b.timeout)); err != nil {
return err
}
}
if err == nil { nn, err := b.nc.Read(b.buf[n:])
if b.length < need { n += nn
switch err {
case nil:
if n < need {
continue continue
} }
b.length = n
return nil return nil
case io.EOF:
if n >= need {
b.length = n
return nil
}
return io.ErrUnexpectedEOF
default:
return err
} }
if b.length >= need && err == io.EOF {
return nil
}
return err
} }
} }

16
vendor/github.com/go-sql-driver/mysql/collations.go сгенерированный поставляемый
Просмотреть файл

@@ -8,7 +8,7 @@
package mysql package mysql
const defaultCollation byte = 33 // utf8_general_ci const defaultCollation = "utf8_general_ci"
// A list of available collations mapped to the internal ID. // A list of available collations mapped to the internal ID.
// To update this map use the following MySQL query: // To update this map use the following MySQL query:
@@ -234,3 +234,17 @@ var collations = map[string]byte{
"utf8mb4_unicode_520_ci": 246, "utf8mb4_unicode_520_ci": 246,
"utf8mb4_vietnamese_ci": 247, "utf8mb4_vietnamese_ci": 247,
} }
// A blacklist of collations which is unsafe to interpolate parameters.
// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
var unsafeCollations = map[string]bool{
"big5_chinese_ci": true,
"sjis_japanese_ci": true,
"gbk_chinese_ci": true,
"big5_bin": true,
"gb2312_bin": true,
"gbk_bin": true,
"sjis_bin": true,
"cp932_japanese_ci": true,
"cp932_bin": true,
}

279
vendor/github.com/go-sql-driver/mysql/connection.go сгенерированный поставляемый
Просмотреть файл

@@ -9,10 +9,9 @@
package mysql package mysql
import ( import (
"crypto/tls"
"database/sql/driver" "database/sql/driver"
"errors"
"net" "net"
"strconv"
"strings" "strings"
"time" "time"
) )
@@ -22,34 +21,20 @@ type mysqlConn struct {
netConn net.Conn netConn net.Conn
affectedRows uint64 affectedRows uint64
insertId uint64 insertId uint64
cfg *config cfg *Config
maxPacketAllowed int maxAllowedPacket int
maxWriteSize int maxWriteSize int
writeTimeout time.Duration
flags clientFlag flags clientFlag
status statusFlag
sequence uint8 sequence uint8
parseTime bool parseTime bool
strict bool strict bool
} }
type config struct {
user string
passwd string
net string
addr string
dbname string
params map[string]string
loc *time.Location
tls *tls.Config
timeout time.Duration
collation uint8
allowAllFiles bool
allowOldPasswords bool
clientFoundRows bool
}
// Handles parameters set in DSN after the connection is established // Handles parameters set in DSN after the connection is established
func (mc *mysqlConn) handleParams() (err error) { func (mc *mysqlConn) handleParams() (err error) {
for param, val := range mc.cfg.params { for param, val := range mc.cfg.Params {
switch param { switch param {
// Charset // Charset
case "charset": case "charset":
@@ -65,27 +50,6 @@ func (mc *mysqlConn) handleParams() (err error) {
return return
} }
// time.Time parsing
case "parseTime":
var isBool bool
mc.parseTime, isBool = readBool(val)
if !isBool {
return errors.New("Invalid Bool value: " + val)
}
// Strict mode
case "strict":
var isBool bool
mc.strict, isBool = readBool(val)
if !isBool {
return errors.New("Invalid Bool value: " + val)
}
// Compression
case "compress":
err = errors.New("Compression not implemented yet")
return
// System Vars // System Vars
default: default:
err = mc.exec("SET " + param + "=" + val + "") err = mc.exec("SET " + param + "=" + val + "")
@@ -115,18 +79,27 @@ func (mc *mysqlConn) Close() (err error) {
// Makes Close idempotent // Makes Close idempotent
if mc.netConn != nil { if mc.netConn != nil {
err = mc.writeCommandPacket(comQuit) err = mc.writeCommandPacket(comQuit)
if err == nil { }
err = mc.netConn.Close()
} else { mc.cleanup()
mc.netConn.Close()
return
}
// Closes the network connection and unsets internal variables. Do not call this
// function after successfully authentication, call Close instead. This function
// 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 mc.netConn = nil
} }
mc.cfg = nil mc.cfg = nil
mc.buf.rd = nil mc.buf.nc = nil
return
} }
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) { func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
@@ -161,28 +134,156 @@ func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
return stmt, err return stmt, err
} }
func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) {
// Number of ? should be same to len(args)
if strings.Count(query, "?") != len(args) {
return "", driver.ErrSkip
}
buf := mc.buf.takeCompleteBuffer()
if buf == nil {
// can not take the buffer. Something must be wrong with the connection
errLog.Print(ErrBusyBuffer)
return "", driver.ErrBadConn
}
buf = buf[:0]
argPos := 0
for i := 0; i < len(query); i++ {
q := strings.IndexByte(query[i:], '?')
if q == -1 {
buf = append(buf, query[i:]...)
break
}
buf = append(buf, query[i:i+q]...)
i += q
arg := args[argPos]
argPos++
if arg == nil {
buf = append(buf, "NULL"...)
continue
}
switch v := arg.(type) {
case int64:
buf = strconv.AppendInt(buf, v, 10)
case float64:
buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
case bool:
if v {
buf = append(buf, '1')
} else {
buf = append(buf, '0')
}
case time.Time:
if v.IsZero() {
buf = append(buf, "'0000-00-00'"...)
} else {
v := v.In(mc.cfg.Loc)
v = v.Add(time.Nanosecond * 500) // To round under microsecond
year := v.Year()
year100 := year / 100
year1 := year % 100
month := v.Month()
day := v.Day()
hour := v.Hour()
minute := v.Minute()
second := v.Second()
micro := v.Nanosecond() / 1000
buf = append(buf, []byte{
'\'',
digits10[year100], digits01[year100],
digits10[year1], digits01[year1],
'-',
digits10[month], digits01[month],
'-',
digits10[day], digits01[day],
' ',
digits10[hour], digits01[hour],
':',
digits10[minute], digits01[minute],
':',
digits10[second], digits01[second],
}...)
if micro != 0 {
micro10000 := micro / 10000
micro100 := micro / 100 % 100
micro1 := micro % 100
buf = append(buf, []byte{
'.',
digits10[micro10000], digits01[micro10000],
digits10[micro100], digits01[micro100],
digits10[micro1], digits01[micro1],
}...)
}
buf = append(buf, '\'')
}
case []byte:
if v == nil {
buf = append(buf, "NULL"...)
} else {
buf = append(buf, "_binary'"...)
if mc.status&statusNoBackslashEscapes == 0 {
buf = escapeBytesBackslash(buf, v)
} else {
buf = escapeBytesQuotes(buf, v)
}
buf = append(buf, '\'')
}
case string:
buf = append(buf, '\'')
if mc.status&statusNoBackslashEscapes == 0 {
buf = escapeStringBackslash(buf, v)
} else {
buf = escapeStringQuotes(buf, v)
}
buf = append(buf, '\'')
default:
return "", driver.ErrSkip
}
if len(buf)+4 > mc.maxAllowedPacket {
return "", driver.ErrSkip
}
}
if argPos != len(args) {
return "", driver.ErrSkip
}
return string(buf), nil
}
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) { func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
if mc.netConn == nil { if mc.netConn == nil {
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
if len(args) == 0 { // no args, fastpath if len(args) != 0 {
mc.affectedRows = 0 if !mc.cfg.InterpolateParams {
mc.insertId = 0 return nil, driver.ErrSkip
err := mc.exec(query)
if err == nil {
return &mysqlResult{
affectedRows: int64(mc.affectedRows),
insertId: int64(mc.insertId),
}, err
} }
return nil, err // try to interpolate the parameters to save extra roundtrips for preparing and closing a statement
prepared, err := mc.interpolateParams(query, args)
if err != nil {
return nil, err
}
query = prepared
args = nil
} }
mc.affectedRows = 0
mc.insertId = 0
// with args, must use prepared stmt err := mc.exec(query)
return nil, driver.ErrSkip if err == nil {
return &mysqlResult{
affectedRows: int64(mc.affectedRows),
insertId: int64(mc.insertId),
}, err
}
return nil, err
} }
// Internal function to execute commands // Internal function to execute commands
@@ -211,29 +312,38 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
if len(args) == 0 { // no args, fastpath if len(args) != 0 {
// Send command if !mc.cfg.InterpolateParams {
err := mc.writeCommandPacketStr(comQuery, query) return nil, driver.ErrSkip
if err == nil {
// Read Result
var resLen int
resLen, err = mc.readResultSetHeaderPacket()
if err == nil {
rows := new(textRows)
rows.mc = mc
if resLen > 0 {
// Columns
rows.columns, err = mc.readColumns(resLen)
}
return rows, err
}
} }
return nil, err // try client-side prepare to reduce roundtrip
prepared, err := mc.interpolateParams(query, args)
if err != nil {
return nil, err
}
query = prepared
args = nil
} }
// Send command
err := mc.writeCommandPacketStr(comQuery, query)
if err == nil {
// Read Result
var resLen int
resLen, err = mc.readResultSetHeaderPacket()
if err == nil {
rows := new(textRows)
rows.mc = mc
// with args, must use prepared stmt if resLen == 0 {
return nil, driver.ErrSkip // no columns, no more data
return emptyRows{}, nil
}
// Columns
rows.columns, err = mc.readColumns(resLen)
return rows, err
}
}
return nil, err
} }
// Gets the value of the given MySQL System Variable // Gets the value of the given MySQL System Variable
@@ -249,6 +359,7 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
if err == nil { if err == nil {
rows := new(textRows) rows := new(textRows)
rows.mc = mc rows.mc = mc
rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
if resLen > 0 { if resLen > 0 {
// Columns // Columns

67
vendor/github.com/go-sql-driver/mysql/connection_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,67 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2016 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/.
package mysql
import (
"database/sql/driver"
"testing"
)
func TestInterpolateParams(t *testing.T) {
mc := &mysqlConn{
buf: newBuffer(nil),
maxAllowedPacket: maxPacketSize,
cfg: &Config{
InterpolateParams: true,
},
}
q, err := mc.interpolateParams("SELECT ?+?", []driver.Value{int64(42), "gopher"})
if err != nil {
t.Errorf("Expected err=nil, got %#v", err)
return
}
expected := `SELECT 42+'gopher'`
if q != expected {
t.Errorf("Expected: %q\nGot: %q", expected, q)
}
}
func TestInterpolateParamsTooManyPlaceholders(t *testing.T) {
mc := &mysqlConn{
buf: newBuffer(nil),
maxAllowedPacket: maxPacketSize,
cfg: &Config{
InterpolateParams: true,
},
}
q, err := mc.interpolateParams("SELECT ?+?", []driver.Value{int64(42)})
if err != driver.ErrSkip {
t.Errorf("Expected err=driver.ErrSkip, got err=%#v, q=%#v", err, q)
}
}
// We don't support placeholder in string literal for now.
// https://github.com/go-sql-driver/mysql/pull/490
func TestInterpolateParamsPlaceholderInString(t *testing.T) {
mc := &mysqlConn{
buf: newBuffer(nil),
maxAllowedPacket: maxPacketSize,
cfg: &Config{
InterpolateParams: true,
},
}
q, err := mc.interpolateParams("SELECT 'abc?xyz',?", []driver.Value{int64(42)})
// When InterpolateParams support string literal, this should return `"SELECT 'abc?xyz', 42`
if err != driver.ErrSkip {
t.Errorf("Expected err=driver.ErrSkip, got err=%#v, q=%#v", err, q)
}
}

37
vendor/github.com/go-sql-driver/mysql/const.go сгенерированный поставляемый
Просмотреть файл

@@ -11,7 +11,7 @@ package mysql
const ( const (
minProtocolVersion byte = 10 minProtocolVersion byte = 10
maxPacketSize = 1<<24 - 1 maxPacketSize = 1<<24 - 1
timeFormat = "2006-01-02 15:04:05" timeFormat = "2006-01-02 15:04:05.999999"
) )
// MySQL constants documentation: // MySQL constants documentation:
@@ -24,6 +24,7 @@ const (
iERR byte = 0xff iERR byte = 0xff
) )
// https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags
type clientFlag uint32 type clientFlag uint32
const ( const (
@@ -45,6 +46,13 @@ const (
clientSecureConn clientSecureConn
clientMultiStatements clientMultiStatements
clientMultiResults clientMultiResults
clientPSMultiResults
clientPluginAuth
clientConnectAttrs
clientPluginAuthLenEncClientData
clientCanHandleExpiredPasswords
clientSessionTrack
clientDeprecateEOF
) )
const ( const (
@@ -68,7 +76,7 @@ const (
comBinlogDump comBinlogDump
comTableDump comTableDump
comConnectOut comConnectOut
comRegiserSlave comRegisterSlave
comStmtPrepare comStmtPrepare
comStmtExecute comStmtExecute
comStmtSendLongData comStmtSendLongData
@@ -78,6 +86,7 @@ const (
comStmtFetch comStmtFetch
) )
// https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType
const ( const (
fieldTypeDecimal byte = iota fieldTypeDecimal byte = iota
fieldTypeTiny fieldTypeTiny
@@ -98,7 +107,8 @@ const (
fieldTypeBit fieldTypeBit
) )
const ( const (
fieldTypeNewDecimal byte = iota + 0xf6 fieldTypeJSON byte = iota + 0xf5
fieldTypeNewDecimal
fieldTypeEnum fieldTypeEnum
fieldTypeSet fieldTypeSet
fieldTypeTinyBLOB fieldTypeTinyBLOB
@@ -130,3 +140,24 @@ const (
flagUnknown3 flagUnknown3
flagUnknown4 flagUnknown4
) )
// http://dev.mysql.com/doc/internals/en/status-flags.html
type statusFlag uint16
const (
statusInTrans statusFlag = 1 << iota
statusInAutocommit
statusReserved // Not in documentation
statusMoreResultsExists
statusNoGoodIndexUsed
statusNoIndexUsed
statusCursorExists
statusLastRowSent
statusDbDropped
statusNoBackslashEscapes
statusMetadataChanged
statusQueryWasSlow
statusPsOutParams
statusInTransReadonly
statusSessionStateChanged
)

117
vendor/github.com/go-sql-driver/mysql/driver.go сгенерированный поставляемый
Просмотреть файл

@@ -4,7 +4,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this file, // 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/. // You can obtain one at http://mozilla.org/MPL/2.0/.
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package // Package mysql provides a MySQL driver for Go's database/sql package
// //
// The driver should be used via the database/sql package: // The driver should be used via the database/sql package:
// //
@@ -22,7 +22,7 @@ import (
"net" "net"
) )
// This struct is exported to make the driver directly accessible. // MySQLDriver is exported to make the driver directly accessible.
// In general the driver is used via the database/sql package. // In general the driver is used via the database/sql package.
type MySQLDriver struct{} type MySQLDriver struct{}
@@ -50,20 +50,22 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
// New mysqlConn // New mysqlConn
mc := &mysqlConn{ mc := &mysqlConn{
maxPacketAllowed: maxPacketSize, maxAllowedPacket: maxPacketSize,
maxWriteSize: maxPacketSize - 1, maxWriteSize: maxPacketSize - 1,
} }
mc.cfg, err = parseDSN(dsn) mc.cfg, err = ParseDSN(dsn)
if err != nil { if err != nil {
return nil, err return nil, err
} }
mc.parseTime = mc.cfg.ParseTime
mc.strict = mc.cfg.Strict
// Connect to Server // Connect to Server
if dial, ok := dials[mc.cfg.net]; ok { if dial, ok := dials[mc.cfg.Net]; ok {
mc.netConn, err = dial(mc.cfg.addr) mc.netConn, err = dial(mc.cfg.Addr)
} else { } else {
nd := net.Dialer{Timeout: mc.cfg.timeout} nd := net.Dialer{Timeout: mc.cfg.Timeout}
mc.netConn, err = nd.Dial(mc.cfg.net, mc.cfg.addr) mc.netConn, err = nd.Dial(mc.cfg.Net, mc.cfg.Addr)
} }
if err != nil { if err != nil {
return nil, err return nil, err
@@ -72,55 +74,54 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
// Enable TCP Keepalives on TCP connections // Enable TCP Keepalives on TCP connections
if tc, ok := mc.netConn.(*net.TCPConn); ok { if tc, ok := mc.netConn.(*net.TCPConn); ok {
if err := tc.SetKeepAlive(true); err != nil { if err := tc.SetKeepAlive(true); err != nil {
mc.Close() // Don't send COM_QUIT before handshake.
mc.netConn.Close()
mc.netConn = nil
return nil, err return nil, err
} }
} }
mc.buf = newBuffer(mc.netConn) mc.buf = newBuffer(mc.netConn)
// Set I/O timeouts
mc.buf.timeout = mc.cfg.ReadTimeout
mc.writeTimeout = mc.cfg.WriteTimeout
// Reading Handshake Initialization Packet // Reading Handshake Initialization Packet
cipher, err := mc.readInitPacket() cipher, err := mc.readInitPacket()
if err != nil { if err != nil {
mc.Close() mc.cleanup()
return nil, err return nil, err
} }
// Send Client Authentication Packet // Send Client Authentication Packet
if err = mc.writeAuthPacket(cipher); err != nil { if err = mc.writeAuthPacket(cipher); err != nil {
mc.Close() mc.cleanup()
return nil, err return nil, err
} }
// Read Result Packet // Handle response to auth packet, switch methods if possible
err = mc.readResultOK() if err = handleAuthResult(mc, cipher); err != nil {
if err != nil { // Authentication failed and MySQL has already closed the connection
// Retry with old authentication method, if allowed // (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
if mc.cfg != nil && mc.cfg.allowOldPasswords && err == ErrOldPassword { // Do not send COM_QUIT, just cleanup and return the error.
if err = mc.writeOldAuthPacket(cipher); err != nil { mc.cleanup()
mc.Close() return nil, err
return nil, err }
}
if err = mc.readResultOK(); err != nil { if mc.cfg.MaxAllowedPacket > 0 {
mc.Close() mc.maxAllowedPacket = mc.cfg.MaxAllowedPacket
return nil, err } else {
} // Get max allowed packet size
} else { maxap, err := mc.getSystemVar("max_allowed_packet")
if err != nil {
mc.Close() mc.Close()
return nil, err return nil, err
} }
mc.maxAllowedPacket = stringToInt(maxap) - 1
} }
if mc.maxAllowedPacket < maxPacketSize {
// Get max allowed packet size mc.maxWriteSize = mc.maxAllowedPacket
maxap, err := mc.getSystemVar("max_allowed_packet")
if err != nil {
mc.Close()
return nil, err
}
mc.maxPacketAllowed = stringToInt(maxap) - 1
if mc.maxPacketAllowed < maxPacketSize {
mc.maxWriteSize = mc.maxPacketAllowed
} }
// Handle DSN Params // Handle DSN Params
@@ -133,6 +134,50 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
return mc, nil return mc, nil
} }
func handleAuthResult(mc *mysqlConn, oldCipher []byte) error {
// Read Result Packet
cipher, err := mc.readResultOK()
if err == nil {
return nil // auth successful
}
if mc.cfg == nil {
return err // auth failed and retry not possible
}
// Retry auth if configured to do so.
if mc.cfg.AllowOldPasswords && err == ErrOldPassword {
// Retry with old authentication method. Note: there are edge cases
// where this should work but doesn't; this is currently "wontfix":
// https://github.com/go-sql-driver/mysql/issues/184
// If CLIENT_PLUGIN_AUTH capability is not supported, no new cipher is
// sent and we have to keep using the cipher sent in the init packet.
if cipher == nil {
cipher = oldCipher
}
if err = mc.writeOldAuthPacket(cipher); err != nil {
return err
}
_, err = mc.readResultOK()
} else if mc.cfg.AllowCleartextPasswords && err == ErrCleartextPassword {
// Retry with clear text password for
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
if err = mc.writeClearAuthPacket(); err != nil {
return err
}
_, err = mc.readResultOK()
} else if mc.cfg.AllowNativePasswords && err == ErrNativePassword {
if err = mc.writeNativeAuthPacket(cipher); err != nil {
return err
}
_, err = mc.readResultOK()
}
return err
}
func init() { func init() {
sql.Register("mysql", &MySQLDriver{}) sql.Register("mysql", &MySQLDriver{})
} }

960
vendor/github.com/go-sql-driver/mysql/driver_test.go сгенерированный поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

548
vendor/github.com/go-sql-driver/mysql/dsn.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,548 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2016 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/.
package mysql
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"net"
"net/url"
"strconv"
"strings"
"time"
)
var (
errInvalidDSNUnescaped = errors.New("invalid DSN: did you forget to escape a param value?")
errInvalidDSNAddr = errors.New("invalid DSN: network address not terminated (missing closing brace)")
errInvalidDSNNoSlash = errors.New("invalid DSN: missing the slash separating the database name")
errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations")
)
// Config is a configuration parsed from a DSN string
type Config struct {
User string // Username
Passwd string // Password (requires User)
Net string // Network type
Addr string // Network address (requires Net)
DBName string // Database name
Params map[string]string // Connection parameters
Collation string // Connection collation
Loc *time.Location // Location for time.Time values
MaxAllowedPacket int // Max packet size allowed
TLSConfig string // TLS configuration name
tls *tls.Config // TLS configuration
Timeout time.Duration // Dial timeout
ReadTimeout time.Duration // I/O read timeout
WriteTimeout time.Duration // I/O write timeout
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
AllowCleartextPasswords bool // Allows the cleartext client side plugin
AllowNativePasswords bool // Allows the native password authentication method
AllowOldPasswords bool // Allows the old insecure password method
ClientFoundRows bool // Return number of matching rows instead of rows changed
ColumnsWithAlias bool // Prepend table alias to column names
InterpolateParams bool // Interpolate placeholders into query string
MultiStatements bool // Allow multiple statements in one query
ParseTime bool // Parse time values to time.Time
Strict bool // Return warnings as errors
}
// FormatDSN formats the given Config into a DSN string which can be passed to
// the driver.
func (cfg *Config) FormatDSN() string {
var buf bytes.Buffer
// [username[:password]@]
if len(cfg.User) > 0 {
buf.WriteString(cfg.User)
if len(cfg.Passwd) > 0 {
buf.WriteByte(':')
buf.WriteString(cfg.Passwd)
}
buf.WriteByte('@')
}
// [protocol[(address)]]
if len(cfg.Net) > 0 {
buf.WriteString(cfg.Net)
if len(cfg.Addr) > 0 {
buf.WriteByte('(')
buf.WriteString(cfg.Addr)
buf.WriteByte(')')
}
}
// /dbname
buf.WriteByte('/')
buf.WriteString(cfg.DBName)
// [?param1=value1&...&paramN=valueN]
hasParam := false
if cfg.AllowAllFiles {
hasParam = true
buf.WriteString("?allowAllFiles=true")
}
if cfg.AllowCleartextPasswords {
if hasParam {
buf.WriteString("&allowCleartextPasswords=true")
} else {
hasParam = true
buf.WriteString("?allowCleartextPasswords=true")
}
}
if cfg.AllowNativePasswords {
if hasParam {
buf.WriteString("&allowNativePasswords=true")
} else {
hasParam = true
buf.WriteString("?allowNativePasswords=true")
}
}
if cfg.AllowOldPasswords {
if hasParam {
buf.WriteString("&allowOldPasswords=true")
} else {
hasParam = true
buf.WriteString("?allowOldPasswords=true")
}
}
if cfg.ClientFoundRows {
if hasParam {
buf.WriteString("&clientFoundRows=true")
} else {
hasParam = true
buf.WriteString("?clientFoundRows=true")
}
}
if col := cfg.Collation; col != defaultCollation && len(col) > 0 {
if hasParam {
buf.WriteString("&collation=")
} else {
hasParam = true
buf.WriteString("?collation=")
}
buf.WriteString(col)
}
if cfg.ColumnsWithAlias {
if hasParam {
buf.WriteString("&columnsWithAlias=true")
} else {
hasParam = true
buf.WriteString("?columnsWithAlias=true")
}
}
if cfg.InterpolateParams {
if hasParam {
buf.WriteString("&interpolateParams=true")
} else {
hasParam = true
buf.WriteString("?interpolateParams=true")
}
}
if cfg.Loc != time.UTC && cfg.Loc != nil {
if hasParam {
buf.WriteString("&loc=")
} else {
hasParam = true
buf.WriteString("?loc=")
}
buf.WriteString(url.QueryEscape(cfg.Loc.String()))
}
if cfg.MultiStatements {
if hasParam {
buf.WriteString("&multiStatements=true")
} else {
hasParam = true
buf.WriteString("?multiStatements=true")
}
}
if cfg.ParseTime {
if hasParam {
buf.WriteString("&parseTime=true")
} else {
hasParam = true
buf.WriteString("?parseTime=true")
}
}
if cfg.ReadTimeout > 0 {
if hasParam {
buf.WriteString("&readTimeout=")
} else {
hasParam = true
buf.WriteString("?readTimeout=")
}
buf.WriteString(cfg.ReadTimeout.String())
}
if cfg.Strict {
if hasParam {
buf.WriteString("&strict=true")
} else {
hasParam = true
buf.WriteString("?strict=true")
}
}
if cfg.Timeout > 0 {
if hasParam {
buf.WriteString("&timeout=")
} else {
hasParam = true
buf.WriteString("?timeout=")
}
buf.WriteString(cfg.Timeout.String())
}
if len(cfg.TLSConfig) > 0 {
if hasParam {
buf.WriteString("&tls=")
} else {
hasParam = true
buf.WriteString("?tls=")
}
buf.WriteString(url.QueryEscape(cfg.TLSConfig))
}
if cfg.WriteTimeout > 0 {
if hasParam {
buf.WriteString("&writeTimeout=")
} else {
hasParam = true
buf.WriteString("?writeTimeout=")
}
buf.WriteString(cfg.WriteTimeout.String())
}
if cfg.MaxAllowedPacket > 0 {
if hasParam {
buf.WriteString("&maxAllowedPacket=")
} else {
hasParam = true
buf.WriteString("?maxAllowedPacket=")
}
buf.WriteString(strconv.Itoa(cfg.MaxAllowedPacket))
}
// other params
if cfg.Params != nil {
for param, value := range cfg.Params {
if hasParam {
buf.WriteByte('&')
} else {
hasParam = true
buf.WriteByte('?')
}
buf.WriteString(param)
buf.WriteByte('=')
buf.WriteString(url.QueryEscape(value))
}
}
return buf.String()
}
// ParseDSN parses the DSN string to a Config
func ParseDSN(dsn string) (cfg *Config, err error) {
// New config with some default values
cfg = &Config{
Loc: time.UTC,
Collation: defaultCollation,
}
// [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
// Find the last '/' (since the password or the net addr might contain a '/')
foundSlash := false
for i := len(dsn) - 1; i >= 0; i-- {
if dsn[i] == '/' {
foundSlash = true
var j, k int
// left part is empty if i <= 0
if i > 0 {
// [username[:password]@][protocol[(address)]]
// Find the last '@' in dsn[:i]
for j = i; j >= 0; j-- {
if dsn[j] == '@' {
// username[:password]
// Find the first ':' in dsn[:j]
for k = 0; k < j; k++ {
if dsn[k] == ':' {
cfg.Passwd = dsn[k+1 : j]
break
}
}
cfg.User = dsn[:k]
break
}
}
// [protocol[(address)]]
// Find the first '(' in dsn[j+1:i]
for k = j + 1; k < i; k++ {
if dsn[k] == '(' {
// dsn[i-1] must be == ')' if an address is specified
if dsn[i-1] != ')' {
if strings.ContainsRune(dsn[k+1:i], ')') {
return nil, errInvalidDSNUnescaped
}
return nil, errInvalidDSNAddr
}
cfg.Addr = dsn[k+1 : i-1]
break
}
}
cfg.Net = dsn[j+1 : k]
}
// dbname[?param1=value1&...&paramN=valueN]
// Find the first '?' in dsn[i+1:]
for j = i + 1; j < len(dsn); j++ {
if dsn[j] == '?' {
if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
return
}
break
}
}
cfg.DBName = dsn[i+1 : j]
break
}
}
if !foundSlash && len(dsn) > 0 {
return nil, errInvalidDSNNoSlash
}
if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
return nil, errInvalidDSNUnsafeCollation
}
// Set default network if empty
if cfg.Net == "" {
cfg.Net = "tcp"
}
// Set default address if empty
if cfg.Addr == "" {
switch cfg.Net {
case "tcp":
cfg.Addr = "127.0.0.1:3306"
case "unix":
cfg.Addr = "/tmp/mysql.sock"
default:
return nil, errors.New("default addr for network '" + cfg.Net + "' unknown")
}
}
return
}
// parseDSNParams parses the DSN "query string"
// Values must be url.QueryEscape'ed
func parseDSNParams(cfg *Config, params string) (err error) {
for _, v := range strings.Split(params, "&") {
param := strings.SplitN(v, "=", 2)
if len(param) != 2 {
continue
}
// cfg params
switch value := param[1]; param[0] {
// Disable INFILE whitelist / enable all files
case "allowAllFiles":
var isBool bool
cfg.AllowAllFiles, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// Use cleartext authentication mode (MySQL 5.5.10+)
case "allowCleartextPasswords":
var isBool bool
cfg.AllowCleartextPasswords, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// Use native password authentication
case "allowNativePasswords":
var isBool bool
cfg.AllowNativePasswords, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// Use old authentication mode (pre MySQL 4.1)
case "allowOldPasswords":
var isBool bool
cfg.AllowOldPasswords, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// Switch "rowsAffected" mode
case "clientFoundRows":
var isBool bool
cfg.ClientFoundRows, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// Collation
case "collation":
cfg.Collation = value
break
case "columnsWithAlias":
var isBool bool
cfg.ColumnsWithAlias, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// Compression
case "compress":
return errors.New("compression not implemented yet")
// Enable client side placeholder substitution
case "interpolateParams":
var isBool bool
cfg.InterpolateParams, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// Time Location
case "loc":
if value, err = url.QueryUnescape(value); err != nil {
return
}
cfg.Loc, err = time.LoadLocation(value)
if err != nil {
return
}
// multiple statements in one query
case "multiStatements":
var isBool bool
cfg.MultiStatements, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// time.Time parsing
case "parseTime":
var isBool bool
cfg.ParseTime, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// I/O read Timeout
case "readTimeout":
cfg.ReadTimeout, err = time.ParseDuration(value)
if err != nil {
return
}
// Strict mode
case "strict":
var isBool bool
cfg.Strict, isBool = readBool(value)
if !isBool {
return errors.New("invalid bool value: " + value)
}
// Dial Timeout
case "timeout":
cfg.Timeout, err = time.ParseDuration(value)
if err != nil {
return
}
// TLS-Encryption
case "tls":
boolValue, isBool := readBool(value)
if isBool {
if boolValue {
cfg.TLSConfig = "true"
cfg.tls = &tls.Config{}
} else {
cfg.TLSConfig = "false"
}
} else if vl := strings.ToLower(value); vl == "skip-verify" {
cfg.TLSConfig = vl
cfg.tls = &tls.Config{InsecureSkipVerify: true}
} else {
name, err := url.QueryUnescape(value)
if err != nil {
return fmt.Errorf("invalid value for TLS config name: %v", err)
}
if tlsConfig, ok := tlsConfigRegister[name]; ok {
if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
host, _, err := net.SplitHostPort(cfg.Addr)
if err == nil {
tlsConfig.ServerName = host
}
}
cfg.TLSConfig = name
cfg.tls = tlsConfig
} else {
return errors.New("invalid value / unknown config name: " + name)
}
}
// I/O write Timeout
case "writeTimeout":
cfg.WriteTimeout, err = time.ParseDuration(value)
if err != nil {
return
}
case "maxAllowedPacket":
cfg.MaxAllowedPacket, err = strconv.Atoi(value)
if err != nil {
return
}
default:
// lazy init
if cfg.Params == nil {
cfg.Params = make(map[string]string)
}
if cfg.Params[param[0]], err = url.QueryUnescape(value); err != nil {
return
}
}
}
return
}

231
vendor/github.com/go-sql-driver/mysql/dsn_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,231 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2016 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/.
package mysql
import (
"crypto/tls"
"fmt"
"net/url"
"reflect"
"testing"
"time"
)
var testDSNs = []struct {
in string
out *Config
}{{
"username:password@protocol(address)/dbname?param=value",
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8_general_ci", Loc: time.UTC},
}, {
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true",
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8_general_ci", Loc: time.UTC, ColumnsWithAlias: true},
}, {
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true",
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8_general_ci", Loc: time.UTC, ColumnsWithAlias: true, MultiStatements: true},
}, {
"user@unix(/path/to/socket)/dbname?charset=utf8",
&Config{User: "user", Net: "unix", Addr: "/path/to/socket", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8_general_ci", Loc: time.UTC},
}, {
"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true",
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8_general_ci", Loc: time.UTC, TLSConfig: "true"},
}, {
"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify",
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8mb4,utf8"}, Collation: "utf8_general_ci", Loc: time.UTC, TLSConfig: "skip-verify"},
}, {
"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci&maxAllowedPacket=16777216",
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, Timeout: 30 * time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, AllowAllFiles: true, AllowOldPasswords: true, ClientFoundRows: true, MaxAllowedPacket: 16777216},
}, {
"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local",
&Config{User: "user", Passwd: "p@ss(word)", Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:80", DBName: "dbname", Collation: "utf8_general_ci", Loc: time.Local},
}, {
"/dbname",
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8_general_ci", Loc: time.UTC},
}, {
"@/",
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8_general_ci", Loc: time.UTC},
}, {
"/",
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8_general_ci", Loc: time.UTC},
}, {
"",
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8_general_ci", Loc: time.UTC},
}, {
"user:p@/ssword@/",
&Config{User: "user", Passwd: "p@/ssword", Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8_general_ci", Loc: time.UTC},
}, {
"unix/?arg=%2Fsome%2Fpath.ext",
&Config{Net: "unix", Addr: "/tmp/mysql.sock", Params: map[string]string{"arg": "/some/path.ext"}, Collation: "utf8_general_ci", Loc: time.UTC},
}}
func TestDSNParser(t *testing.T) {
for i, tst := range testDSNs {
cfg, err := ParseDSN(tst.in)
if err != nil {
t.Error(err.Error())
}
// pointer not static
cfg.tls = nil
if !reflect.DeepEqual(cfg, tst.out) {
t.Errorf("%d. ParseDSN(%q) mismatch:\ngot %+v\nwant %+v", i, tst.in, cfg, tst.out)
}
}
}
func TestDSNParserInvalid(t *testing.T) {
var invalidDSNs = []string{
"@net(addr/", // no closing brace
"@tcp(/", // no closing brace
"tcp(/", // no closing brace
"(/", // no closing brace
"net(addr)//", // unescaped
"User:pass@tcp(1.2.3.4:3306)", // no trailing slash
//"/dbname?arg=/some/unescaped/path",
}
for i, tst := range invalidDSNs {
if _, err := ParseDSN(tst); err == nil {
t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst)
}
}
}
func TestDSNReformat(t *testing.T) {
for i, tst := range testDSNs {
dsn1 := tst.in
cfg1, err := ParseDSN(dsn1)
if err != nil {
t.Error(err.Error())
continue
}
cfg1.tls = nil // pointer not static
res1 := fmt.Sprintf("%+v", cfg1)
dsn2 := cfg1.FormatDSN()
cfg2, err := ParseDSN(dsn2)
if err != nil {
t.Error(err.Error())
continue
}
cfg2.tls = nil // pointer not static
res2 := fmt.Sprintf("%+v", cfg2)
if res1 != res2 {
t.Errorf("%d. %q does not match %q", i, res2, res1)
}
}
}
func TestDSNWithCustomTLS(t *testing.T) {
baseDSN := "User:password@tcp(localhost:5555)/dbname?tls="
tlsCfg := tls.Config{}
RegisterTLSConfig("utils_test", &tlsCfg)
// Custom TLS is missing
tst := baseDSN + "invalid_tls"
cfg, err := ParseDSN(tst)
if err == nil {
t.Errorf("invalid custom TLS in DSN (%s) but did not error. Got config: %#v", tst, cfg)
}
tst = baseDSN + "utils_test"
// Custom TLS with a server name
name := "foohost"
tlsCfg.ServerName = name
cfg, err = ParseDSN(tst)
if err != nil {
t.Error(err.Error())
} else if cfg.tls.ServerName != name {
t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst)
}
// Custom TLS without a server name
name = "localhost"
tlsCfg.ServerName = ""
cfg, err = ParseDSN(tst)
if err != nil {
t.Error(err.Error())
} else if cfg.tls.ServerName != name {
t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
}
DeregisterTLSConfig("utils_test")
}
func TestDSNWithCustomTLSQueryEscape(t *testing.T) {
const configKey = "&%!:"
dsn := "User:password@tcp(localhost:5555)/dbname?tls=" + url.QueryEscape(configKey)
name := "foohost"
tlsCfg := tls.Config{ServerName: name}
RegisterTLSConfig(configKey, &tlsCfg)
cfg, err := ParseDSN(dsn)
if err != nil {
t.Error(err.Error())
} else if cfg.tls.ServerName != name {
t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, dsn)
}
}
func TestDSNUnsafeCollation(t *testing.T) {
_, err := ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true")
if err != errInvalidDSNUnsafeCollation {
t.Errorf("expected %v, got %v", errInvalidDSNUnsafeCollation, err)
}
_, err = ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false")
if err != nil {
t.Errorf("expected %v, got %v", nil, err)
}
_, err = ParseDSN("/dbname?collation=gbk_chinese_ci")
if err != nil {
t.Errorf("expected %v, got %v", nil, err)
}
_, err = ParseDSN("/dbname?collation=ascii_bin&interpolateParams=true")
if err != nil {
t.Errorf("expected %v, got %v", nil, err)
}
_, err = ParseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true")
if err != nil {
t.Errorf("expected %v, got %v", nil, err)
}
_, err = ParseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true")
if err != nil {
t.Errorf("expected %v, got %v", nil, err)
}
_, err = ParseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true")
if err != nil {
t.Errorf("expected %v, got %v", nil, err)
}
}
func BenchmarkParseDSN(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for _, tst := range testDSNs {
if _, err := ParseDSN(tst.in); err != nil {
b.Error(err.Error())
}
}
}
}

23
vendor/github.com/go-sql-driver/mysql/errors.go сгенерированный поставляемый
Просмотреть файл

@@ -19,18 +19,21 @@ import (
// Various errors the driver might return. Can change between driver versions. // Various errors the driver might return. Can change between driver versions.
var ( var (
ErrInvalidConn = errors.New("Invalid Connection") ErrInvalidConn = errors.New("invalid connection")
ErrMalformPkt = errors.New("Malformed Packet") ErrMalformPkt = errors.New("malformed packet")
ErrNoTLS = errors.New("TLS encryption requested but server does not support TLS") ErrNoTLS = errors.New("TLS requested but server does not support TLS")
ErrOldPassword = errors.New("This server only supports the insecure old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords") ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN")
ErrOldProtocol = errors.New("MySQL-Server does not support required Protocol 41+") ErrNativePassword = errors.New("this user requires mysql native password authentication.")
ErrPktSync = errors.New("Commands out of sync. You can't run this command now") ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords")
ErrPktSyncMul = errors.New("Commands out of sync. Did you run multiple statements at once?") ErrUnknownPlugin = errors.New("this authentication plugin is not supported")
ErrPktTooLarge = errors.New("Packet for query is too large. You can change this value on the server by adjusting the 'max_allowed_packet' variable.") ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+")
ErrBusyBuffer = errors.New("Busy buffer") ErrPktSync = errors.New("commands out of sync. You can't run this command now")
ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?")
ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server")
ErrBusyBuffer = errors.New("busy buffer")
) )
var errLog Logger = log.New(os.Stderr, "[MySQL] ", log.Ldate|log.Ltime|log.Lshortfile) var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile))
// Logger is used to log critical error messages. // Logger is used to log critical error messages.
type Logger interface { type Logger interface {

56
vendor/github.com/go-sql-driver/mysql/infile.go сгенерированный поставляемый
Просмотреть файл

@@ -13,11 +13,14 @@ import (
"io" "io"
"os" "os"
"strings" "strings"
"sync"
) )
var ( var (
fileRegister map[string]bool fileRegister map[string]bool
readerRegister map[string]func() io.Reader fileRegisterLock sync.RWMutex
readerRegister map[string]func() io.Reader
readerRegisterLock sync.RWMutex
) )
// RegisterLocalFile adds the given file to the file whitelist, // RegisterLocalFile adds the given file to the file whitelist,
@@ -32,17 +35,21 @@ var (
// ... // ...
// //
func RegisterLocalFile(filePath string) { func RegisterLocalFile(filePath string) {
fileRegisterLock.Lock()
// lazy map init // lazy map init
if fileRegister == nil { if fileRegister == nil {
fileRegister = make(map[string]bool) fileRegister = make(map[string]bool)
} }
fileRegister[strings.Trim(filePath, `"`)] = true fileRegister[strings.Trim(filePath, `"`)] = true
fileRegisterLock.Unlock()
} }
// DeregisterLocalFile removes the given filepath from the whitelist. // DeregisterLocalFile removes the given filepath from the whitelist.
func DeregisterLocalFile(filePath string) { func DeregisterLocalFile(filePath string) {
fileRegisterLock.Lock()
delete(fileRegister, strings.Trim(filePath, `"`)) delete(fileRegister, strings.Trim(filePath, `"`))
fileRegisterLock.Unlock()
} }
// RegisterReaderHandler registers a handler function which is used // RegisterReaderHandler registers a handler function which is used
@@ -61,18 +68,22 @@ func DeregisterLocalFile(filePath string) {
// ... // ...
// //
func RegisterReaderHandler(name string, handler func() io.Reader) { func RegisterReaderHandler(name string, handler func() io.Reader) {
readerRegisterLock.Lock()
// lazy map init // lazy map init
if readerRegister == nil { if readerRegister == nil {
readerRegister = make(map[string]func() io.Reader) readerRegister = make(map[string]func() io.Reader)
} }
readerRegister[name] = handler readerRegister[name] = handler
readerRegisterLock.Unlock()
} }
// DeregisterReaderHandler removes the ReaderHandler function with // DeregisterReaderHandler removes the ReaderHandler function with
// the given name from the registry. // the given name from the registry.
func DeregisterReaderHandler(name string) { func DeregisterReaderHandler(name string) {
readerRegisterLock.Lock()
delete(readerRegister, name) delete(readerRegister, name)
readerRegisterLock.Unlock()
} }
func deferredClose(err *error, closer io.Closer) { func deferredClose(err *error, closer io.Closer) {
@@ -85,14 +96,22 @@ func deferredClose(err *error, closer io.Closer) {
func (mc *mysqlConn) handleInFileRequest(name string) (err error) { func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
var rdr io.Reader var rdr io.Reader
var data []byte var data []byte
packetSize := 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
if mc.maxWriteSize < packetSize {
packetSize = mc.maxWriteSize
}
if strings.HasPrefix(name, "Reader::") { // io.Reader if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader
name = name[8:] // The server might return an an absolute path. See issue #355.
if handler, inMap := readerRegister[name]; inMap { name = name[idx+8:]
readerRegisterLock.RLock()
handler, inMap := readerRegister[name]
readerRegisterLock.RUnlock()
if inMap {
rdr = handler() rdr = handler()
if rdr != nil { if rdr != nil {
data = make([]byte, 4+mc.maxWriteSize)
if cl, ok := rdr.(io.Closer); ok { if cl, ok := rdr.(io.Closer); ok {
defer deferredClose(&err, cl) defer deferredClose(&err, cl)
} }
@@ -104,7 +123,10 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
} }
} else { // File } else { // File
name = strings.Trim(name, `"`) name = strings.Trim(name, `"`)
if mc.cfg.allowAllFiles || fileRegister[name] { fileRegisterLock.RLock()
fr := fileRegister[name]
fileRegisterLock.RUnlock()
if mc.cfg.AllowAllFiles || fr {
var file *os.File var file *os.File
var fi os.FileInfo var fi os.FileInfo
@@ -114,22 +136,19 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
// get file size // get file size
if fi, err = file.Stat(); err == nil { if fi, err = file.Stat(); err == nil {
rdr = file rdr = file
if fileSize := int(fi.Size()); fileSize <= mc.maxWriteSize { if fileSize := int(fi.Size()); fileSize < packetSize {
data = make([]byte, 4+fileSize) packetSize = fileSize
} else if fileSize <= mc.maxPacketAllowed {
data = make([]byte, 4+mc.maxWriteSize)
} else {
err = fmt.Errorf("Local File '%s' too large: Size: %d, Max: %d", name, fileSize, mc.maxPacketAllowed)
} }
} }
} }
} else { } else {
err = fmt.Errorf("Local File '%s' is not registered. Use the DSN parameter 'allowAllFiles=true' to allow all files", name) err = fmt.Errorf("local file '%s' is not registered", name)
} }
} }
// send content packets // send content packets
if err == nil { if err == nil {
data := make([]byte, 4+packetSize)
var n int var n int
for err == nil { for err == nil {
n, err = rdr.Read(data[4:]) n, err = rdr.Read(data[4:])
@@ -154,9 +173,10 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
// read OK packet // read OK packet
if err == nil { if err == nil {
return mc.readResultOK() _, err = mc.readResultOK()
} else { return err
mc.readPacket()
} }
mc.readPacket()
return err return err
} }

453
vendor/github.com/go-sql-driver/mysql/packets.go сгенерированный поставляемый
Просмотреть файл

@@ -13,6 +13,7 @@ import (
"crypto/tls" "crypto/tls"
"database/sql/driver" "database/sql/driver"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"io" "io"
"math" "math"
@@ -24,9 +25,9 @@ import (
// Read packet to buffer 'data' // Read packet to buffer 'data'
func (mc *mysqlConn) readPacket() ([]byte, error) { func (mc *mysqlConn) readPacket() ([]byte, error) {
var payload []byte var prevData []byte
for { for {
// Read packet header // read packet header
data, err := mc.buf.readNext(4) data, err := mc.buf.readNext(4)
if err != nil { if err != nil {
errLog.Print(err) errLog.Print(err)
@@ -34,26 +35,32 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
// Packet Length [24 bit] // packet length [24 bit]
pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16) pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16)
if pktLen < 1 { // check packet sync [8 bit]
errLog.Print(ErrMalformPkt)
mc.Close()
return nil, driver.ErrBadConn
}
// Check Packet Sync [8 bit]
if data[3] != mc.sequence { if data[3] != mc.sequence {
if data[3] > mc.sequence { if data[3] > mc.sequence {
return nil, ErrPktSyncMul return nil, ErrPktSyncMul
} else {
return nil, ErrPktSync
} }
return nil, ErrPktSync
} }
mc.sequence++ mc.sequence++
// Read packet body [pktLen bytes] // packets with length 0 terminate a previous packet which is a
// multiple of (2^24)1 bytes long
if pktLen == 0 {
// there was no previous packet
if prevData == nil {
errLog.Print(ErrMalformPkt)
mc.Close()
return nil, driver.ErrBadConn
}
return prevData, nil
}
// read packet body [pktLen bytes]
data, err = mc.buf.readNext(pktLen) data, err = mc.buf.readNext(pktLen)
if err != nil { if err != nil {
errLog.Print(err) errLog.Print(err)
@@ -61,18 +68,17 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
isLastPacket := (pktLen < maxPacketSize) // return data if this was the last packet
if pktLen < maxPacketSize {
// zero allocations for non-split packets
if prevData == nil {
return data, nil
}
// Zero allocations for non-splitting packets return append(prevData, data...), nil
if isLastPacket && payload == nil {
return data, nil
} }
payload = append(payload, data...) prevData = append(prevData, data...)
if isLastPacket {
return payload, nil
}
} }
} }
@@ -80,7 +86,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
func (mc *mysqlConn) writePacket(data []byte) error { func (mc *mysqlConn) writePacket(data []byte) error {
pktLen := len(data) - 4 pktLen := len(data) - 4
if pktLen > mc.maxPacketAllowed { if pktLen > mc.maxAllowedPacket {
return ErrPktTooLarge return ErrPktTooLarge
} }
@@ -100,6 +106,12 @@ func (mc *mysqlConn) writePacket(data []byte) error {
data[3] = mc.sequence data[3] = mc.sequence
// Write packet // Write packet
if mc.writeTimeout > 0 {
if err := mc.netConn.SetWriteDeadline(time.Now().Add(mc.writeTimeout)); err != nil {
return err
}
}
n, err := mc.netConn.Write(data[:4+size]) n, err := mc.netConn.Write(data[:4+size])
if err == nil && n == 4+size { if err == nil && n == 4+size {
mc.sequence++ mc.sequence++
@@ -140,7 +152,7 @@ func (mc *mysqlConn) readInitPacket() ([]byte, error) {
// protocol version [1 byte] // protocol version [1 byte]
if data[0] < minProtocolVersion { if data[0] < minProtocolVersion {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"Unsupported MySQL Protocol Version %d. Protocol Version %d or higher is required", "unsupported protocol version %d. Version %d or higher is required",
data[0], data[0],
minProtocolVersion, minProtocolVersion,
) )
@@ -196,7 +208,11 @@ func (mc *mysqlConn) readInitPacket() ([]byte, error) {
// return // return
//} //}
//return ErrMalformPkt //return ErrMalformPkt
return cipher, nil
// make a memory safe copy of the cipher slice
var b [20]byte
copy(b[:], cipher)
return b[:], nil
} }
// make a memory safe copy of the cipher slice // make a memory safe copy of the cipher slice
@@ -214,9 +230,11 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
clientLongPassword | clientLongPassword |
clientTransactions | clientTransactions |
clientLocalFiles | clientLocalFiles |
clientPluginAuth |
clientMultiResults |
mc.flags&clientLongFlag mc.flags&clientLongFlag
if mc.cfg.clientFoundRows { if mc.cfg.ClientFoundRows {
clientFlags |= clientFoundRows clientFlags |= clientFoundRows
} }
@@ -225,13 +243,17 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
clientFlags |= clientSSL clientFlags |= clientSSL
} }
// User Password if mc.cfg.MultiStatements {
scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.passwd)) clientFlags |= clientMultiStatements
}
pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.user) + 1 + 1 + len(scrambleBuff) // User Password
scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.Passwd))
pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + 1 + len(scrambleBuff) + 21 + 1
// To specify a db name // To specify a db name
if n := len(mc.cfg.dbname); n > 0 { if n := len(mc.cfg.DBName); n > 0 {
clientFlags |= clientConnectWithDB clientFlags |= clientConnectWithDB
pktLen += n + 1 pktLen += n + 1
} }
@@ -257,7 +279,14 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
data[11] = 0x00 data[11] = 0x00
// Charset [1 byte] // Charset [1 byte]
data[12] = mc.cfg.collation var found bool
data[12], found = collations[mc.cfg.Collation]
if !found {
// Note possibility for false negatives:
// could be triggered although the collation is valid if the
// collations map does not contain entries the server supports.
return errors.New("unknown collation")
}
// SSL Connection Request Packet // SSL Connection Request Packet
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
@@ -273,15 +302,18 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
return err return err
} }
mc.netConn = tlsConn mc.netConn = tlsConn
mc.buf.rd = tlsConn mc.buf.nc = tlsConn
} }
// Filler [23 bytes] (all 0x00) // Filler [23 bytes] (all 0x00)
pos := 13 + 23 pos := 13
for ; pos < 13+23; pos++ {
data[pos] = 0
}
// User [null terminated string] // User [null terminated string]
if len(mc.cfg.user) > 0 { if len(mc.cfg.User) > 0 {
pos += copy(data[pos:], mc.cfg.user) pos += copy(data[pos:], mc.cfg.User)
} }
data[pos] = 0x00 data[pos] = 0x00
pos++ pos++
@@ -291,11 +323,16 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
pos += 1 + copy(data[pos+1:], scrambleBuff) pos += 1 + copy(data[pos+1:], scrambleBuff)
// Databasename [null terminated string] // Databasename [null terminated string]
if len(mc.cfg.dbname) > 0 { if len(mc.cfg.DBName) > 0 {
pos += copy(data[pos:], mc.cfg.dbname) pos += copy(data[pos:], mc.cfg.DBName)
data[pos] = 0x00 data[pos] = 0x00
pos++
} }
// Assume native client during response
pos += copy(data[pos:], "mysql_native_password")
data[pos] = 0x00
// Send Auth packet // Send Auth packet
return mc.writePacket(data) return mc.writePacket(data)
} }
@@ -304,9 +341,9 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error { func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error {
// User password // User password
scrambleBuff := scrambleOldPassword(cipher, []byte(mc.cfg.passwd)) scrambleBuff := scrambleOldPassword(cipher, []byte(mc.cfg.Passwd))
// Calculate the packet lenght and add a tailing 0 // Calculate the packet length and add a tailing 0
pktLen := len(scrambleBuff) + 1 pktLen := len(scrambleBuff) + 1
data := mc.buf.takeSmallBuffer(4 + pktLen) data := mc.buf.takeSmallBuffer(4 + pktLen)
if data == nil { if data == nil {
@@ -322,6 +359,45 @@ func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error {
return mc.writePacket(data) return mc.writePacket(data)
} }
// Client clear text authentication packet
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
func (mc *mysqlConn) writeClearAuthPacket() error {
// Calculate the packet length and add a tailing 0
pktLen := len(mc.cfg.Passwd) + 1
data := mc.buf.takeSmallBuffer(4 + pktLen)
if data == nil {
// can not take the buffer. Something must be wrong with the connection
errLog.Print(ErrBusyBuffer)
return driver.ErrBadConn
}
// Add the clear password [null terminated string]
copy(data[4:], mc.cfg.Passwd)
data[4+pktLen-1] = 0x00
return mc.writePacket(data)
}
// Native password authentication method
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
func (mc *mysqlConn) writeNativeAuthPacket(cipher []byte) error {
scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.Passwd))
// Calculate the packet length and add a tailing 0
pktLen := len(scrambleBuff)
data := mc.buf.takeSmallBuffer(4 + pktLen)
if data == nil {
// can not take the buffer. Something must be wrong with the connection
errLog.Print(ErrBusyBuffer)
return driver.ErrBadConn
}
// Add the scramble
copy(data[4:], scrambleBuff)
return mc.writePacket(data)
}
/****************************************************************************** /******************************************************************************
* Command Packets * * Command Packets *
******************************************************************************/ ******************************************************************************/
@@ -395,24 +471,43 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error {
******************************************************************************/ ******************************************************************************/
// Returns error if Packet is not an 'Result OK'-Packet // Returns error if Packet is not an 'Result OK'-Packet
func (mc *mysqlConn) readResultOK() error { func (mc *mysqlConn) readResultOK() ([]byte, error) {
data, err := mc.readPacket() data, err := mc.readPacket()
if err == nil { if err == nil {
// packet indicator // packet indicator
switch data[0] { switch data[0] {
case iOK: case iOK:
return mc.handleOkPacket(data) return nil, mc.handleOkPacket(data)
case iEOF: case iEOF:
// someone is using old_passwords if len(data) > 1 {
return ErrOldPassword pluginEndIndex := bytes.IndexByte(data, 0x00)
plugin := string(data[1:pluginEndIndex])
cipher := data[pluginEndIndex+1 : len(data)-1]
if plugin == "mysql_old_password" {
// using old_passwords
return cipher, ErrOldPassword
} else if plugin == "mysql_clear_password" {
// using clear text password
return cipher, ErrCleartextPassword
} else if plugin == "mysql_native_password" {
// using mysql default authentication method
return cipher, ErrNativePassword
} else {
return cipher, ErrUnknownPlugin
}
} else {
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
return nil, ErrOldPassword
}
default: // Error otherwise default: // Error otherwise
return mc.handleErrorPacket(data) return nil, mc.handleErrorPacket(data)
} }
} }
return err return nil, err
} }
// Result Set Header Packet // Result Set Header Packet
@@ -470,6 +565,10 @@ func (mc *mysqlConn) handleErrorPacket(data []byte) error {
} }
} }
func readStatus(b []byte) statusFlag {
return statusFlag(b[0]) | statusFlag(b[1])<<8
}
// Ok Packet // Ok Packet
// http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-OK_Packet // http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-OK_Packet
func (mc *mysqlConn) handleOkPacket(data []byte) error { func (mc *mysqlConn) handleOkPacket(data []byte) error {
@@ -484,17 +583,21 @@ func (mc *mysqlConn) handleOkPacket(data []byte) error {
mc.insertId, _, m = readLengthEncodedInteger(data[1+n:]) mc.insertId, _, m = readLengthEncodedInteger(data[1+n:])
// server_status [2 bytes] // server_status [2 bytes]
mc.status = readStatus(data[1+n+m : 1+n+m+2])
if err := mc.discardResults(); err != nil {
return err
}
// warning count [2 bytes] // warning count [2 bytes]
if !mc.strict { if !mc.strict {
return nil return nil
} else {
pos := 1 + n + m + 2
if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 {
return mc.getWarnings()
}
return nil
} }
pos := 1 + n + m + 2
if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 {
return mc.getWarnings()
}
return nil
} }
// Read Packets as Field Packets until EOF-Packet or an Error appears // Read Packets as Field Packets until EOF-Packet or an Error appears
@@ -513,7 +616,7 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
if i == count { if i == count {
return columns, nil return columns, nil
} }
return nil, fmt.Errorf("ColumnsCount mismatch n:%d len:%d", count, len(columns)) return nil, fmt.Errorf("column count mismatch n:%d len:%d", count, len(columns))
} }
// Catalog // Catalog
@@ -530,11 +633,20 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
pos += n pos += n
// Table [len coded string] // Table [len coded string]
n, err = skipLengthEncodedString(data[pos:]) if mc.cfg.ColumnsWithAlias {
if err != nil { tableName, _, n, err := readLengthEncodedString(data[pos:])
return nil, err if err != nil {
return nil, err
}
pos += n
columns[i].tableName = string(tableName)
} else {
n, err = skipLengthEncodedString(data[pos:])
if err != nil {
return nil, err
}
pos += n
} }
pos += n
// Original table [len coded string] // Original table [len coded string]
n, err = skipLengthEncodedString(data[pos:]) n, err = skipLengthEncodedString(data[pos:])
@@ -557,20 +669,21 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
return nil, err return nil, err
} }
// Filler [1 byte] // Filler [uint8]
// Charset [16 bit uint] // Charset [charset, collation uint8]
// Length [32 bit uint] // Length [uint32]
pos += n + 1 + 2 + 4 pos += n + 1 + 2 + 4
// Field type [byte] // Field type [uint8]
columns[i].fieldType = data[pos] columns[i].fieldType = data[pos]
pos++ pos++
// Flags [16 bit uint] // Flags [uint16]
columns[i].flags = fieldFlag(binary.LittleEndian.Uint16(data[pos : pos+2])) columns[i].flags = fieldFlag(binary.LittleEndian.Uint16(data[pos : pos+2]))
//pos += 2 pos += 2
// Decimals [8 bit uint] // Decimals [uint8]
columns[i].decimals = data[pos]
//pos++ //pos++
// Default value [len coded binary] // Default value [len coded binary]
@@ -592,7 +705,21 @@ func (rows *textRows) readRow(dest []driver.Value) error {
// EOF Packet // EOF Packet
if data[0] == iEOF && len(data) == 5 { if data[0] == iEOF && len(data) == 5 {
return io.EOF // server_status [2 bytes]
rows.mc.status = readStatus(data[3:])
err = rows.mc.discardResults()
if err == nil {
err = io.EOF
} else {
// connection unusable
rows.mc.Close()
}
rows.mc = nil
return err
}
if data[0] == iERR {
rows.mc = nil
return mc.handleErrorPacket(data)
} }
// RowSet Packet // RowSet Packet
@@ -614,7 +741,7 @@ func (rows *textRows) readRow(dest []driver.Value) error {
fieldTypeDate, fieldTypeNewDate: fieldTypeDate, fieldTypeNewDate:
dest[i], err = parseDateTime( dest[i], err = parseDateTime(
string(dest[i].([]byte)), string(dest[i].([]byte)),
mc.cfg.loc, mc.cfg.Loc,
) )
if err == nil { if err == nil {
continue continue
@@ -639,12 +766,19 @@ func (rows *textRows) readRow(dest []driver.Value) error {
func (mc *mysqlConn) readUntilEOF() error { func (mc *mysqlConn) readUntilEOF() error {
for { for {
data, err := mc.readPacket() data, err := mc.readPacket()
if err != nil {
// No Err and no EOF Packet return err
if err == nil && data[0] != iEOF { }
continue
switch data[0] {
case iERR:
return mc.handleErrorPacket(data)
case iEOF:
if len(data) == 5 {
mc.status = readStatus(data[3:])
}
return nil
} }
return err // Err or EOF
} }
} }
@@ -676,20 +810,20 @@ func (stmt *mysqlStmt) readPrepareResultPacket() (uint16, error) {
// Warning count [16 bit uint] // Warning count [16 bit uint]
if !stmt.mc.strict { if !stmt.mc.strict {
return columnCount, nil return columnCount, nil
} else {
// Check for warnings count > 0, only available in MySQL > 4.1
if len(data) >= 12 && binary.LittleEndian.Uint16(data[10:12]) > 0 {
return columnCount, stmt.mc.getWarnings()
}
return columnCount, nil
} }
// Check for warnings count > 0, only available in MySQL > 4.1
if len(data) >= 12 && binary.LittleEndian.Uint16(data[10:12]) > 0 {
return columnCount, stmt.mc.getWarnings()
}
return columnCount, nil
} }
return 0, err return 0, err
} }
// http://dev.mysql.com/doc/internals/en/com-stmt-send-long-data.html // http://dev.mysql.com/doc/internals/en/com-stmt-send-long-data.html
func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) error { func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) error {
maxLen := stmt.mc.maxPacketAllowed - 1 maxLen := stmt.mc.maxAllowedPacket - 1
pktLen := maxLen pktLen := maxLen
// After the header (bytes 0-3) follows before the data: // After the header (bytes 0-3) follows before the data:
@@ -744,7 +878,7 @@ func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) error {
func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
if len(args) != stmt.paramCount { if len(args) != stmt.paramCount {
return fmt.Errorf( return fmt.Errorf(
"Arguments count mismatch (Got: %d Has: %d)", "argument count mismatch (got: %d; has: %d)",
len(args), len(args),
stmt.paramCount, stmt.paramCount,
) )
@@ -880,7 +1014,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
paramTypes[i+i] = fieldTypeString paramTypes[i+i] = fieldTypeString
paramTypes[i+i+1] = 0x00 paramTypes[i+i+1] = 0x00
if len(v) < mc.maxPacketAllowed-pos-len(paramValues)-(len(args)-(i+1))*64 { if len(v) < mc.maxAllowedPacket-pos-len(paramValues)-(len(args)-(i+1))*64 {
paramValues = appendLengthEncodedInteger(paramValues, paramValues = appendLengthEncodedInteger(paramValues,
uint64(len(v)), uint64(len(v)),
) )
@@ -902,7 +1036,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
paramTypes[i+i] = fieldTypeString paramTypes[i+i] = fieldTypeString
paramTypes[i+i+1] = 0x00 paramTypes[i+i+1] = 0x00
if len(v) < mc.maxPacketAllowed-pos-len(paramValues)-(len(args)-(i+1))*64 { if len(v) < mc.maxAllowedPacket-pos-len(paramValues)-(len(args)-(i+1))*64 {
paramValues = appendLengthEncodedInteger(paramValues, paramValues = appendLengthEncodedInteger(paramValues,
uint64(len(v)), uint64(len(v)),
) )
@@ -921,7 +1055,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
if v.IsZero() { if v.IsZero() {
val = []byte("0000-00-00") val = []byte("0000-00-00")
} else { } else {
val = []byte(v.In(mc.cfg.loc).Format(timeFormat)) val = []byte(v.In(mc.cfg.Loc).Format(timeFormat))
} }
paramValues = appendLengthEncodedInteger(paramValues, paramValues = appendLengthEncodedInteger(paramValues,
@@ -930,7 +1064,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
paramValues = append(paramValues, val...) paramValues = append(paramValues, val...)
default: default:
return fmt.Errorf("Can't convert type: %T", arg) return fmt.Errorf("can not convert type: %T", arg)
} }
} }
@@ -948,6 +1082,28 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
return mc.writePacket(data) return mc.writePacket(data)
} }
func (mc *mysqlConn) discardResults() error {
for mc.status&statusMoreResultsExists != 0 {
resLen, err := mc.readResultSetHeaderPacket()
if err != nil {
return err
}
if resLen > 0 {
// columns
if err := mc.readUntilEOF(); err != nil {
return err
}
// rows
if err := mc.readUntilEOF(); err != nil {
return err
}
} else {
mc.status &^= statusMoreResultsExists
}
}
return nil
}
// http://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html // http://dev.mysql.com/doc/internals/en/binary-protocol-resultset-row.html
func (rows *binaryRows) readRow(dest []driver.Value) error { func (rows *binaryRows) readRow(dest []driver.Value) error {
data, err := rows.mc.readPacket() data, err := rows.mc.readPacket()
@@ -959,8 +1115,18 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
if data[0] != iOK { if data[0] != iOK {
// EOF Packet // EOF Packet
if data[0] == iEOF && len(data) == 5 { if data[0] == iEOF && len(data) == 5 {
return io.EOF rows.mc.status = readStatus(data[3:])
err = rows.mc.discardResults()
if err == nil {
err = io.EOF
} else {
// connection unusable
rows.mc.Close()
}
rows.mc = nil
return err
} }
rows.mc = nil
// Error otherwise // Error otherwise
return rows.mc.handleErrorPacket(data) return rows.mc.handleErrorPacket(data)
@@ -1027,7 +1193,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
continue continue
case fieldTypeFloat: case fieldTypeFloat:
dest[i] = float64(math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4]))) dest[i] = float32(math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4])))
pos += 4 pos += 4
continue continue
@@ -1040,7 +1206,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar, case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar,
fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB, fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB,
fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB, fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB,
fieldTypeVarString, fieldTypeString, fieldTypeGeometry: fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeJSON:
var isNull bool var isNull bool
var n int var n int
dest[i], isNull, n, err = readLengthEncodedString(data[pos:]) dest[i], isNull, n, err = readLengthEncodedString(data[pos:])
@@ -1055,88 +1221,53 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
} }
return err return err
// Date YYYY-MM-DD case
case fieldTypeDate, fieldTypeNewDate: fieldTypeDate, fieldTypeNewDate, // Date YYYY-MM-DD
fieldTypeTime, // Time [-][H]HH:MM:SS[.fractal]
fieldTypeTimestamp, fieldTypeDateTime: // Timestamp YYYY-MM-DD HH:MM:SS[.fractal]
num, isNull, n := readLengthEncodedInteger(data[pos:]) num, isNull, n := readLengthEncodedInteger(data[pos:])
pos += n pos += n
if isNull { switch {
case isNull:
dest[i] = nil dest[i] = nil
continue continue
} case rows.columns[i].fieldType == fieldTypeTime:
// database/sql does not support an equivalent to TIME, return a string
if rows.mc.parseTime { var dstlen uint8
dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.loc) switch decimals := rows.columns[i].decimals; decimals {
} else { case 0x00, 0x1f:
dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], false) dstlen = 8
} case 1, 2, 3, 4, 5, 6:
dstlen = 8 + 1 + decimals
if err == nil { default:
pos += int(num) return fmt.Errorf(
continue "protocol error, illegal decimals value %d",
} else { rows.columns[i].decimals,
return err )
}
// Time [-][H]HH:MM:SS[.fractal]
case fieldTypeTime:
num, isNull, n := readLengthEncodedInteger(data[pos:])
pos += n
if num == 0 {
if isNull {
dest[i] = nil
continue
} else {
dest[i] = []byte("00:00:00")
continue
} }
} dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, true)
case rows.mc.parseTime:
var sign string dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
if data[pos] == 1 {
sign = "-"
}
switch num {
case 8:
dest[i] = []byte(fmt.Sprintf(
sign+"%02d:%02d:%02d",
uint16(data[pos+1])*24+uint16(data[pos+5]),
data[pos+6],
data[pos+7],
))
pos += 8
continue
case 12:
dest[i] = []byte(fmt.Sprintf(
sign+"%02d:%02d:%02d.%06d",
uint16(data[pos+1])*24+uint16(data[pos+5]),
data[pos+6],
data[pos+7],
binary.LittleEndian.Uint32(data[pos+8:pos+12]),
))
pos += 12
continue
default: default:
return fmt.Errorf("Invalid TIME-packet length %d", num) var dstlen uint8
} if rows.columns[i].fieldType == fieldTypeDate {
dstlen = 10
// Timestamp YYYY-MM-DD HH:MM:SS[.fractal] } else {
case fieldTypeTimestamp, fieldTypeDateTime: switch decimals := rows.columns[i].decimals; decimals {
num, isNull, n := readLengthEncodedInteger(data[pos:]) case 0x00, 0x1f:
dstlen = 19
pos += n case 1, 2, 3, 4, 5, 6:
dstlen = 19 + 1 + decimals
if isNull { default:
dest[i] = nil return fmt.Errorf(
continue "protocol error, illegal decimals value %d",
} rows.columns[i].decimals,
)
if rows.mc.parseTime { }
dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.loc) }
} else { dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, false)
dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], true)
} }
if err == nil { if err == nil {
@@ -1148,7 +1279,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
// Please report if this happens! // Please report if this happens!
default: default:
return fmt.Errorf("Unknown FieldType %d", rows.columns[i].fieldType) return fmt.Errorf("unknown field type %d", rows.columns[i].fieldType)
} }
} }

282
vendor/github.com/go-sql-driver/mysql/packets_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,282 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2016 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/.
package mysql
import (
"database/sql/driver"
"errors"
"net"
"testing"
"time"
)
var (
errConnClosed = errors.New("connection is closed")
errConnTooManyReads = errors.New("too many reads")
errConnTooManyWrites = errors.New("too many writes")
)
// struct to mock a net.Conn for testing purposes
type mockConn struct {
laddr net.Addr
raddr net.Addr
data []byte
closed bool
read int
written int
reads int
writes int
maxReads int
maxWrites int
}
func (m *mockConn) Read(b []byte) (n int, err error) {
if m.closed {
return 0, errConnClosed
}
m.reads++
if m.maxReads > 0 && m.reads > m.maxReads {
return 0, errConnTooManyReads
}
n = copy(b, m.data)
m.read += n
m.data = m.data[n:]
return
}
func (m *mockConn) Write(b []byte) (n int, err error) {
if m.closed {
return 0, errConnClosed
}
m.writes++
if m.maxWrites > 0 && m.writes > m.maxWrites {
return 0, errConnTooManyWrites
}
n = len(b)
m.written += n
return
}
func (m *mockConn) Close() error {
m.closed = true
return nil
}
func (m *mockConn) LocalAddr() net.Addr {
return m.laddr
}
func (m *mockConn) RemoteAddr() net.Addr {
return m.raddr
}
func (m *mockConn) SetDeadline(t time.Time) error {
return nil
}
func (m *mockConn) SetReadDeadline(t time.Time) error {
return nil
}
func (m *mockConn) SetWriteDeadline(t time.Time) error {
return nil
}
// make sure mockConn implements the net.Conn interface
var _ net.Conn = new(mockConn)
func TestReadPacketSingleByte(t *testing.T) {
conn := new(mockConn)
mc := &mysqlConn{
buf: newBuffer(conn),
}
conn.data = []byte{0x01, 0x00, 0x00, 0x00, 0xff}
conn.maxReads = 1
packet, err := mc.readPacket()
if err != nil {
t.Fatal(err)
}
if len(packet) != 1 {
t.Fatalf("unexpected packet lenght: expected %d, got %d", 1, len(packet))
}
if packet[0] != 0xff {
t.Fatalf("unexpected packet content: expected %x, got %x", 0xff, packet[0])
}
}
func TestReadPacketWrongSequenceID(t *testing.T) {
conn := new(mockConn)
mc := &mysqlConn{
buf: newBuffer(conn),
}
// too low sequence id
conn.data = []byte{0x01, 0x00, 0x00, 0x00, 0xff}
conn.maxReads = 1
mc.sequence = 1
_, err := mc.readPacket()
if err != ErrPktSync {
t.Errorf("expected ErrPktSync, got %v", err)
}
// reset
conn.reads = 0
mc.sequence = 0
mc.buf = newBuffer(conn)
// too high sequence id
conn.data = []byte{0x01, 0x00, 0x00, 0x42, 0xff}
_, err = mc.readPacket()
if err != ErrPktSyncMul {
t.Errorf("expected ErrPktSyncMul, got %v", err)
}
}
func TestReadPacketSplit(t *testing.T) {
conn := new(mockConn)
mc := &mysqlConn{
buf: newBuffer(conn),
}
data := make([]byte, maxPacketSize*2+4*3)
const pkt2ofs = maxPacketSize + 4
const pkt3ofs = 2 * (maxPacketSize + 4)
// case 1: payload has length maxPacketSize
data = data[:pkt2ofs+4]
// 1st packet has maxPacketSize length and sequence id 0
// ff ff ff 00 ...
data[0] = 0xff
data[1] = 0xff
data[2] = 0xff
// mark the payload start and end of 1st packet so that we can check if the
// content was correctly appended
data[4] = 0x11
data[maxPacketSize+3] = 0x22
// 2nd packet has payload length 0 and squence id 1
// 00 00 00 01
data[pkt2ofs+3] = 0x01
conn.data = data
conn.maxReads = 3
packet, err := mc.readPacket()
if err != nil {
t.Fatal(err)
}
if len(packet) != maxPacketSize {
t.Fatalf("unexpected packet lenght: expected %d, got %d", maxPacketSize, len(packet))
}
if packet[0] != 0x11 {
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
}
if packet[maxPacketSize-1] != 0x22 {
t.Fatalf("unexpected payload end: expected %x, got %x", 0x22, packet[maxPacketSize-1])
}
// case 2: payload has length which is a multiple of maxPacketSize
data = data[:cap(data)]
// 2nd packet now has maxPacketSize length
data[pkt2ofs] = 0xff
data[pkt2ofs+1] = 0xff
data[pkt2ofs+2] = 0xff
// mark the payload start and end of the 2nd packet
data[pkt2ofs+4] = 0x33
data[pkt2ofs+maxPacketSize+3] = 0x44
// 3rd packet has payload length 0 and squence id 2
// 00 00 00 02
data[pkt3ofs+3] = 0x02
conn.data = data
conn.reads = 0
conn.maxReads = 5
mc.sequence = 0
packet, err = mc.readPacket()
if err != nil {
t.Fatal(err)
}
if len(packet) != 2*maxPacketSize {
t.Fatalf("unexpected packet lenght: expected %d, got %d", 2*maxPacketSize, len(packet))
}
if packet[0] != 0x11 {
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
}
if packet[2*maxPacketSize-1] != 0x44 {
t.Fatalf("unexpected payload end: expected %x, got %x", 0x44, packet[2*maxPacketSize-1])
}
// case 3: payload has a length larger maxPacketSize, which is not an exact
// multiple of it
data = data[:pkt2ofs+4+42]
data[pkt2ofs] = 0x2a
data[pkt2ofs+1] = 0x00
data[pkt2ofs+2] = 0x00
data[pkt2ofs+4+41] = 0x44
conn.data = data
conn.reads = 0
conn.maxReads = 4
mc.sequence = 0
packet, err = mc.readPacket()
if err != nil {
t.Fatal(err)
}
if len(packet) != maxPacketSize+42 {
t.Fatalf("unexpected packet lenght: expected %d, got %d", maxPacketSize+42, len(packet))
}
if packet[0] != 0x11 {
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
}
if packet[maxPacketSize+41] != 0x44 {
t.Fatalf("unexpected payload end: expected %x, got %x", 0x44, packet[maxPacketSize+41])
}
}
func TestReadPacketFail(t *testing.T) {
conn := new(mockConn)
mc := &mysqlConn{
buf: newBuffer(conn),
}
// illegal empty (stand-alone) packet
conn.data = []byte{0x00, 0x00, 0x00, 0x00}
conn.maxReads = 1
_, err := mc.readPacket()
if err != driver.ErrBadConn {
t.Errorf("expected ErrBadConn, got %v", err)
}
// reset
conn.reads = 0
mc.sequence = 0
mc.buf = newBuffer(conn)
// fail to read header
conn.closed = true
_, err = mc.readPacket()
if err != driver.ErrBadConn {
t.Errorf("expected ErrBadConn, got %v", err)
}
// reset
conn.closed = false
conn.reads = 0
mc.sequence = 0
mc.buf = newBuffer(conn)
// fail to read body
conn.maxReads = 1
_, err = mc.readPacket()
if err != driver.ErrBadConn {
t.Errorf("expected ErrBadConn, got %v", err)
}
}

50
vendor/github.com/go-sql-driver/mysql/rows.go сгенерированный поставляемый
Просмотреть файл

@@ -14,9 +14,11 @@ import (
) )
type mysqlField struct { type mysqlField struct {
fieldType byte tableName string
flags fieldFlag
name string name string
flags fieldFlag
fieldType byte
decimals byte
} }
type mysqlRows struct { type mysqlRows struct {
@@ -32,10 +34,22 @@ type textRows struct {
mysqlRows mysqlRows
} }
type emptyRows struct{}
func (rows *mysqlRows) Columns() []string { func (rows *mysqlRows) Columns() []string {
columns := make([]string, len(rows.columns)) columns := make([]string, len(rows.columns))
for i := range columns { if rows.mc != nil && rows.mc.cfg.ColumnsWithAlias {
columns[i] = rows.columns[i].name for i := range columns {
if tableName := rows.columns[i].tableName; len(tableName) > 0 {
columns[i] = tableName + "." + rows.columns[i].name
} else {
columns[i] = rows.columns[i].name
}
}
} else {
for i := range columns {
columns[i] = rows.columns[i].name
}
} }
return columns return columns
} }
@@ -51,6 +65,12 @@ func (rows *mysqlRows) Close() error {
// Remove unread packets from stream // Remove unread packets from stream
err := mc.readUntilEOF() err := mc.readUntilEOF()
if err == nil {
if err = mc.discardResults(); err != nil {
return err
}
}
rows.mc = nil rows.mc = nil
return err return err
} }
@@ -62,10 +82,7 @@ func (rows *binaryRows) Next(dest []driver.Value) error {
} }
// Fetch next row from stream // Fetch next row from stream
if err := rows.readRow(dest); err != io.EOF { return rows.readRow(dest)
return err
}
rows.mc = nil
} }
return io.EOF return io.EOF
} }
@@ -77,10 +94,19 @@ func (rows *textRows) Next(dest []driver.Value) error {
} }
// Fetch next row from stream // Fetch next row from stream
if err := rows.readRow(dest); err != io.EOF { return rows.readRow(dest)
return err
}
rows.mc = nil
} }
return io.EOF return io.EOF
} }
func (rows emptyRows) Columns() []string {
return nil
}
func (rows emptyRows) Close() error {
return nil
}
func (rows emptyRows) Next(dest []driver.Value) error {
return io.EOF
}

45
vendor/github.com/go-sql-driver/mysql/statement.go сгенерированный поставляемый
Просмотреть файл

@@ -10,6 +10,9 @@ package mysql
import ( import (
"database/sql/driver" "database/sql/driver"
"fmt"
"reflect"
"strconv"
) )
type mysqlStmt struct { type mysqlStmt struct {
@@ -21,7 +24,10 @@ type mysqlStmt struct {
func (stmt *mysqlStmt) Close() error { func (stmt *mysqlStmt) Close() error {
if stmt.mc == nil || stmt.mc.netConn == nil { if stmt.mc == nil || stmt.mc.netConn == nil {
errLog.Print(ErrInvalidConn) // driver.Stmt.Close can be called more than once, thus this function
// has to be idempotent.
// See also Issue #450 and golang/go#16019.
//errLog.Print(ErrInvalidConn)
return driver.ErrBadConn return driver.ErrBadConn
} }
@@ -34,6 +40,10 @@ func (stmt *mysqlStmt) NumInput() int {
return stmt.paramCount return stmt.paramCount
} }
func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
return converter{}
}
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
if stmt.mc.netConn == nil { if stmt.mc.netConn == nil {
errLog.Print(ErrInvalidConn) errLog.Print(ErrInvalidConn)
@@ -94,9 +104,9 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
} }
rows := new(binaryRows) rows := new(binaryRows)
rows.mc = mc
if resLen > 0 { if resLen > 0 {
rows.mc = mc
// Columns // Columns
// If not cached, read them and cache them // If not cached, read them and cache them
if stmt.columns == nil { if stmt.columns == nil {
@@ -110,3 +120,34 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
return rows, err return rows, err
} }
type converter struct{}
func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
if driver.IsValue(v) {
return v, nil
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Ptr:
// indirect pointers
if rv.IsNil() {
return nil, nil
}
return c.ConvertValue(rv.Elem().Interface())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return rv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
return int64(rv.Uint()), nil
case reflect.Uint64:
u64 := rv.Uint()
if u64 >= 1<<63 {
return strconv.FormatUint(u64, 10), nil
}
return int64(u64), nil
case reflect.Float32, reflect.Float64:
return rv.Float(), nil
}
return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
}

572
vendor/github.com/go-sql-driver/mysql/utils.go сгенерированный поставляемый
Просмотреть файл

@@ -13,26 +13,16 @@ import (
"crypto/tls" "crypto/tls"
"database/sql/driver" "database/sql/driver"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"io" "io"
"net/url"
"strings" "strings"
"time" "time"
) )
var ( var (
tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
errInvalidDSNUnescaped = errors.New("Invalid DSN: Did you forget to escape a param value?")
errInvalidDSNAddr = errors.New("Invalid DSN: Network Address not terminated (missing closing brace)")
errInvalidDSNNoSlash = errors.New("Invalid DSN: Missing the slash separating the database name")
) )
func init() {
tlsConfigRegister = make(map[string]*tls.Config)
}
// RegisterTLSConfig registers a custom tls.Config to be used with sql.Open. // RegisterTLSConfig registers a custom tls.Config to be used with sql.Open.
// Use the key as a value in the DSN where tls=value. // Use the key as a value in the DSN where tls=value.
// //
@@ -58,7 +48,11 @@ func init() {
// //
func RegisterTLSConfig(key string, config *tls.Config) error { func RegisterTLSConfig(key string, config *tls.Config) error {
if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" { if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" {
return fmt.Errorf("Key '%s' is reserved", key) return fmt.Errorf("key '%s' is reserved", key)
}
if tlsConfigRegister == nil {
tlsConfigRegister = make(map[string]*tls.Config)
} }
tlsConfigRegister[key] = config tlsConfigRegister[key] = config
@@ -67,202 +61,9 @@ func RegisterTLSConfig(key string, config *tls.Config) error {
// DeregisterTLSConfig removes the tls.Config associated with key. // DeregisterTLSConfig removes the tls.Config associated with key.
func DeregisterTLSConfig(key string) { func DeregisterTLSConfig(key string) {
delete(tlsConfigRegister, key) if tlsConfigRegister != nil {
} delete(tlsConfigRegister, key)
// parseDSN parses the DSN string to a config
func parseDSN(dsn string) (cfg *config, err error) {
// New config with some default values
cfg = &config{
loc: time.UTC,
collation: defaultCollation,
} }
// TODO: use strings.IndexByte when we can depend on Go 1.2
// [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
// Find the last '/' (since the password or the net addr might contain a '/')
foundSlash := false
for i := len(dsn) - 1; i >= 0; i-- {
if dsn[i] == '/' {
foundSlash = true
var j, k int
// left part is empty if i <= 0
if i > 0 {
// [username[:password]@][protocol[(address)]]
// Find the last '@' in dsn[:i]
for j = i; j >= 0; j-- {
if dsn[j] == '@' {
// username[:password]
// Find the first ':' in dsn[:j]
for k = 0; k < j; k++ {
if dsn[k] == ':' {
cfg.passwd = dsn[k+1 : j]
break
}
}
cfg.user = dsn[:k]
break
}
}
// [protocol[(address)]]
// Find the first '(' in dsn[j+1:i]
for k = j + 1; k < i; k++ {
if dsn[k] == '(' {
// dsn[i-1] must be == ')' if an address is specified
if dsn[i-1] != ')' {
if strings.ContainsRune(dsn[k+1:i], ')') {
return nil, errInvalidDSNUnescaped
}
return nil, errInvalidDSNAddr
}
cfg.addr = dsn[k+1 : i-1]
break
}
}
cfg.net = dsn[j+1 : k]
}
// dbname[?param1=value1&...&paramN=valueN]
// Find the first '?' in dsn[i+1:]
for j = i + 1; j < len(dsn); j++ {
if dsn[j] == '?' {
if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
return
}
break
}
}
cfg.dbname = dsn[i+1 : j]
break
}
}
if !foundSlash && len(dsn) > 0 {
return nil, errInvalidDSNNoSlash
}
// Set default network if empty
if cfg.net == "" {
cfg.net = "tcp"
}
// Set default address if empty
if cfg.addr == "" {
switch cfg.net {
case "tcp":
cfg.addr = "127.0.0.1:3306"
case "unix":
cfg.addr = "/tmp/mysql.sock"
default:
return nil, errors.New("Default addr for network '" + cfg.net + "' unknown")
}
}
return
}
// parseDSNParams parses the DSN "query string"
// Values must be url.QueryEscape'ed
func parseDSNParams(cfg *config, params string) (err error) {
for _, v := range strings.Split(params, "&") {
param := strings.SplitN(v, "=", 2)
if len(param) != 2 {
continue
}
// cfg params
switch value := param[1]; param[0] {
// Disable INFILE whitelist / enable all files
case "allowAllFiles":
var isBool bool
cfg.allowAllFiles, isBool = readBool(value)
if !isBool {
return fmt.Errorf("Invalid Bool value: %s", value)
}
// Use old authentication mode (pre MySQL 4.1)
case "allowOldPasswords":
var isBool bool
cfg.allowOldPasswords, isBool = readBool(value)
if !isBool {
return fmt.Errorf("Invalid Bool value: %s", value)
}
// Switch "rowsAffected" mode
case "clientFoundRows":
var isBool bool
cfg.clientFoundRows, isBool = readBool(value)
if !isBool {
return fmt.Errorf("Invalid Bool value: %s", value)
}
// Collation
case "collation":
collation, ok := collations[value]
if !ok {
// Note possibility for false negatives:
// could be triggered although the collation is valid if the
// collations map does not contain entries the server supports.
err = errors.New("unknown collation")
return
}
cfg.collation = collation
break
// Time Location
case "loc":
if value, err = url.QueryUnescape(value); err != nil {
return
}
cfg.loc, err = time.LoadLocation(value)
if err != nil {
return
}
// Dial Timeout
case "timeout":
cfg.timeout, err = time.ParseDuration(value)
if err != nil {
return
}
// TLS-Encryption
case "tls":
boolValue, isBool := readBool(value)
if isBool {
if boolValue {
cfg.tls = &tls.Config{}
}
} else {
if strings.ToLower(value) == "skip-verify" {
cfg.tls = &tls.Config{InsecureSkipVerify: true}
} else if tlsConfig, ok := tlsConfigRegister[value]; ok {
cfg.tls = tlsConfig
} else {
return fmt.Errorf("Invalid value / unknown config name: %s", value)
}
}
default:
// lazy init
if cfg.params == nil {
cfg.params = make(map[string]string)
}
if cfg.params[param[0]], err = url.QueryUnescape(value); err != nil {
return
}
}
}
return
} }
// Returns the bool value of the input. // Returns the bool value of the input.
@@ -451,19 +252,15 @@ func (nt NullTime) Value() (driver.Value, error) {
} }
func parseDateTime(str string, loc *time.Location) (t time.Time, err error) { func parseDateTime(str string, loc *time.Location) (t time.Time, err error) {
base := "0000-00-00 00:00:00.0000000"
switch len(str) { switch len(str) {
case 10: // YYYY-MM-DD case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
if str == "0000-00-00" { if str == base[:len(str)] {
return return
} }
t, err = time.Parse(timeFormat[:10], str) t, err = time.Parse(timeFormat[:len(str)], str)
case 19: // YYYY-MM-DD HH:MM:SS
if str == "0000-00-00 00:00:00" {
return
}
t, err = time.Parse(timeFormat, str)
default: default:
err = fmt.Errorf("Invalid Time-String: %s", str) err = fmt.Errorf("invalid time string: %s", str)
return return
} }
@@ -512,87 +309,151 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va
loc, loc,
), nil ), nil
} }
return nil, fmt.Errorf("Invalid DATETIME-packet length %d", num) return nil, fmt.Errorf("invalid DATETIME packet length %d", num)
} }
// zeroDateTime is used in formatBinaryDateTime to avoid an allocation // zeroDateTime is used in formatBinaryDateTime to avoid an allocation
// if the DATE or DATETIME has the zero value. // if the DATE or DATETIME has the zero value.
// It must never be changed. // It must never be changed.
// The current behavior depends on database/sql copying the result. // The current behavior depends on database/sql copying the result.
var zeroDateTime = []byte("0000-00-00 00:00:00") var zeroDateTime = []byte("0000-00-00 00:00:00.000000")
func formatBinaryDateTime(src []byte, withTime bool) (driver.Value, error) { const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value, error) {
// length expects the deterministic length of the zero value,
// negative time and 100+ hours are automatically added if needed
if len(src) == 0 { if len(src) == 0 {
if withTime { if justTime {
return zeroDateTime, nil return zeroDateTime[11 : 11+length], nil
} }
return zeroDateTime[:10], nil return zeroDateTime[:length], nil
} }
var dst []byte var dst []byte // return value
if withTime { var pt, p1, p2, p3 byte // current digit pair
if len(src) == 11 { var zOffs byte // offset of value in zeroDateTime
dst = []byte("0000-00-00 00:00:00.000000") if justTime {
switch length {
case
8, // time (can be up to 10 when negative and 100+ hours)
10, 11, 12, 13, 14, 15: // time with fractional seconds
default:
return nil, fmt.Errorf("illegal TIME length %d", length)
}
switch len(src) {
case 8, 12:
default:
return nil, fmt.Errorf("invalid TIME packet length %d", len(src))
}
// +2 to enable negative time and 100+ hours
dst = make([]byte, 0, length+2)
if src[0] == 1 {
dst = append(dst, '-')
}
if src[1] != 0 {
hour := uint16(src[1])*24 + uint16(src[5])
pt = byte(hour / 100)
p1 = byte(hour - 100*uint16(pt))
dst = append(dst, digits01[pt])
} else { } else {
dst = []byte("0000-00-00 00:00:00") p1 = src[5]
} }
zOffs = 11
src = src[6:]
} else { } else {
dst = []byte("0000-00-00") switch length {
} case 10, 19, 21, 22, 23, 24, 25, 26:
switch len(src) { default:
case 11: t := "DATE"
microsecs := binary.LittleEndian.Uint32(src[7:11]) if length > 10 {
tmp32 := microsecs / 10 t += "TIME"
dst[25] += byte(microsecs - 10*tmp32) }
tmp32, microsecs = tmp32/10, tmp32 return nil, fmt.Errorf("illegal %s length %d", t, length)
dst[24] += byte(microsecs - 10*tmp32) }
tmp32, microsecs = tmp32/10, tmp32 switch len(src) {
dst[23] += byte(microsecs - 10*tmp32) case 4, 7, 11:
tmp32, microsecs = tmp32/10, tmp32 default:
dst[22] += byte(microsecs - 10*tmp32) t := "DATE"
tmp32, microsecs = tmp32/10, tmp32 if length > 10 {
dst[21] += byte(microsecs - 10*tmp32) t += "TIME"
dst[20] += byte(microsecs / 10) }
fallthrough return nil, fmt.Errorf("illegal %s packet length %d", t, len(src))
case 7: }
second := src[6] dst = make([]byte, 0, length)
tmp := second / 10 // start with the date
dst[18] += second - 10*tmp
dst[17] += tmp
minute := src[5]
tmp = minute / 10
dst[15] += minute - 10*tmp
dst[14] += tmp
hour := src[4]
tmp = hour / 10
dst[12] += hour - 10*tmp
dst[11] += tmp
fallthrough
case 4:
day := src[3]
tmp := day / 10
dst[9] += day - 10*tmp
dst[8] += tmp
month := src[2]
tmp = month / 10
dst[6] += month - 10*tmp
dst[5] += tmp
year := binary.LittleEndian.Uint16(src[:2]) year := binary.LittleEndian.Uint16(src[:2])
tmp16 := year / 10 pt = byte(year / 100)
dst[3] += byte(year - 10*tmp16) p1 = byte(year - 100*uint16(pt))
tmp16, year = tmp16/10, tmp16 p2, p3 = src[2], src[3]
dst[2] += byte(year - 10*tmp16) dst = append(dst,
tmp16, year = tmp16/10, tmp16 digits10[pt], digits01[pt],
dst[1] += byte(year - 10*tmp16) digits10[p1], digits01[p1], '-',
dst[0] += byte(tmp16) digits10[p2], digits01[p2], '-',
digits10[p3], digits01[p3],
)
if length == 10 {
return dst, nil
}
if len(src) == 4 {
return append(dst, zeroDateTime[10:length]...), nil
}
dst = append(dst, ' ')
p1 = src[4] // hour
src = src[5:]
}
// p1 is 2-digit hour, src is after hour
p2, p3 = src[0], src[1]
dst = append(dst,
digits10[p1], digits01[p1], ':',
digits10[p2], digits01[p2], ':',
digits10[p3], digits01[p3],
)
if length <= byte(len(dst)) {
return dst, nil return dst, nil
} }
var t string src = src[2:]
if withTime { if len(src) == 0 {
t = "DATETIME" return append(dst, zeroDateTime[19:zOffs+length]...), nil
} else { }
t = "DATE" microsecs := binary.LittleEndian.Uint32(src[:4])
p1 = byte(microsecs / 10000)
microsecs -= 10000 * uint32(p1)
p2 = byte(microsecs / 100)
microsecs -= 100 * uint32(p2)
p3 = byte(microsecs)
switch decimals := zOffs + length - 20; decimals {
default:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2], digits01[p2],
digits10[p3], digits01[p3],
), nil
case 1:
return append(dst, '.',
digits10[p1],
), nil
case 2:
return append(dst, '.',
digits10[p1], digits01[p1],
), nil
case 3:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2],
), nil
case 4:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2], digits01[p2],
), nil
case 5:
return append(dst, '.',
digits10[p1], digits01[p1],
digits10[p2], digits01[p2],
digits10[p3],
), nil
} }
return nil, fmt.Errorf("invalid %s-packet length %d", t, len(src))
} }
/****************************************************************************** /******************************************************************************
@@ -683,6 +544,10 @@ func skipLengthEncodedString(b []byte) (int, error) {
// returns the number read, whether the value is NULL and the number of bytes read // returns the number read, whether the value is NULL and the number of bytes read
func readLengthEncodedInteger(b []byte) (uint64, bool, int) { func readLengthEncodedInteger(b []byte) (uint64, bool, int) {
// See issue #349
if len(b) == 0 {
return 0, true, 1
}
switch b[0] { switch b[0] {
// 251: NULL // 251: NULL
@@ -724,3 +589,152 @@ func appendLengthEncodedInteger(b []byte, n uint64) []byte {
return append(b, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24), return append(b, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24),
byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56)) byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56))
} }
// reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize.
// If cap(buf) is not enough, reallocate new buffer.
func reserveBuffer(buf []byte, appendSize int) []byte {
newSize := len(buf) + appendSize
if cap(buf) < newSize {
// Grow buffer exponentially
newBuf := make([]byte, len(buf)*2+appendSize)
copy(newBuf, buf)
buf = newBuf
}
return buf[:newSize]
}
// escapeBytesBackslash escapes []byte with backslashes (\)
// This escapes the contents of a string (provided as []byte) by adding backslashes before special
// characters, and turning others into specific escape sequences, such as
// turning newlines into \n and null bytes into \0.
// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L823-L932
func escapeBytesBackslash(buf, v []byte) []byte {
pos := len(buf)
buf = reserveBuffer(buf, len(v)*2)
for _, c := range v {
switch c {
case '\x00':
buf[pos] = '\\'
buf[pos+1] = '0'
pos += 2
case '\n':
buf[pos] = '\\'
buf[pos+1] = 'n'
pos += 2
case '\r':
buf[pos] = '\\'
buf[pos+1] = 'r'
pos += 2
case '\x1a':
buf[pos] = '\\'
buf[pos+1] = 'Z'
pos += 2
case '\'':
buf[pos] = '\\'
buf[pos+1] = '\''
pos += 2
case '"':
buf[pos] = '\\'
buf[pos+1] = '"'
pos += 2
case '\\':
buf[pos] = '\\'
buf[pos+1] = '\\'
pos += 2
default:
buf[pos] = c
pos++
}
}
return buf[:pos]
}
// escapeStringBackslash is similar to escapeBytesBackslash but for string.
func escapeStringBackslash(buf []byte, v string) []byte {
pos := len(buf)
buf = reserveBuffer(buf, len(v)*2)
for i := 0; i < len(v); i++ {
c := v[i]
switch c {
case '\x00':
buf[pos] = '\\'
buf[pos+1] = '0'
pos += 2
case '\n':
buf[pos] = '\\'
buf[pos+1] = 'n'
pos += 2
case '\r':
buf[pos] = '\\'
buf[pos+1] = 'r'
pos += 2
case '\x1a':
buf[pos] = '\\'
buf[pos+1] = 'Z'
pos += 2
case '\'':
buf[pos] = '\\'
buf[pos+1] = '\''
pos += 2
case '"':
buf[pos] = '\\'
buf[pos+1] = '"'
pos += 2
case '\\':
buf[pos] = '\\'
buf[pos+1] = '\\'
pos += 2
default:
buf[pos] = c
pos++
}
}
return buf[:pos]
}
// escapeBytesQuotes escapes apostrophes in []byte by doubling them up.
// This escapes the contents of a string by doubling up any apostrophes that
// it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
// effect on the server.
// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L963-L1038
func escapeBytesQuotes(buf, v []byte) []byte {
pos := len(buf)
buf = reserveBuffer(buf, len(v)*2)
for _, c := range v {
if c == '\'' {
buf[pos] = '\''
buf[pos+1] = '\''
pos += 2
} else {
buf[pos] = c
pos++
}
}
return buf[:pos]
}
// escapeStringQuotes is similar to escapeBytesQuotes but for string.
func escapeStringQuotes(buf []byte, v string) []byte {
pos := len(buf)
buf = reserveBuffer(buf, len(v)*2)
for i := 0; i < len(v); i++ {
c := v[i]
if c == '\'' {
buf[pos] = '\''
buf[pos+1] = '\''
pos += 2
} else {
buf[pos] = c
pos++
}
}
return buf[:pos]
}

143
vendor/github.com/go-sql-driver/mysql/utils_test.go сгенерированный поставляемый
Просмотреть файл

@@ -16,76 +16,6 @@ import (
"time" "time"
) )
var testDSNs = []struct {
in string
out string
loc *time.Location
}{
{"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
{"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
{"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8mb4,utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
{"user:password@/dbname?loc=UTC&timeout=30s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:30000000000 collation:224 allowAllFiles:true allowOldPasswords:true clientFoundRows:true}", time.UTC},
{"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{user:user passwd:p@ss(word) net:tcp addr:[de:ad:be:ef::ca:fe]:80 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.Local},
{"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
{"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
{"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
{"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
{"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
{"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
}
func TestDSNParser(t *testing.T) {
var cfg *config
var err error
var res string
for i, tst := range testDSNs {
cfg, err = parseDSN(tst.in)
if err != nil {
t.Error(err.Error())
}
// pointer not static
cfg.tls = nil
res = fmt.Sprintf("%+v", cfg)
if res != fmt.Sprintf(tst.out, tst.loc) {
t.Errorf("%d. parseDSN(%q) => %q, want %q", i, tst.in, res, fmt.Sprintf(tst.out, tst.loc))
}
}
}
func TestDSNParserInvalid(t *testing.T) {
var invalidDSNs = []string{
"@net(addr/", // no closing brace
"@tcp(/", // no closing brace
"tcp(/", // no closing brace
"(/", // no closing brace
"net(addr)//", // unescaped
"user:pass@tcp(1.2.3.4:3306)", // no trailing slash
//"/dbname?arg=/some/unescaped/path",
}
for i, tst := range invalidDSNs {
if _, err := parseDSN(tst); err == nil {
t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst)
}
}
}
func BenchmarkParseDSN(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for _, tst := range testDSNs {
if _, err := parseDSN(tst.in); err != nil {
b.Error(err.Error())
}
}
}
}
func TestScanNullTime(t *testing.T) { func TestScanNullTime(t *testing.T) {
var scanTests = []struct { var scanTests = []struct {
in interface{} in interface{}
@@ -191,22 +121,77 @@ func TestFormatBinaryDateTime(t *testing.T) {
rawDate[5] = 46 // minutes rawDate[5] = 46 // minutes
rawDate[6] = 23 // seconds rawDate[6] = 23 // seconds
binary.LittleEndian.PutUint32(rawDate[7:], 987654) // microseconds binary.LittleEndian.PutUint32(rawDate[7:], 987654) // microseconds
expect := func(expected string, length int, withTime bool) { expect := func(expected string, inlen, outlen uint8) {
actual, _ := formatBinaryDateTime(rawDate[:length], withTime) actual, _ := formatBinaryDateTime(rawDate[:inlen], outlen, false)
bytes, ok := actual.([]byte) bytes, ok := actual.([]byte)
if !ok { if !ok {
t.Errorf("formatBinaryDateTime must return []byte, was %T", actual) t.Errorf("formatBinaryDateTime must return []byte, was %T", actual)
} }
if string(bytes) != expected { if string(bytes) != expected {
t.Errorf( t.Errorf(
"expected %q, got %q for length %d, withTime %v", "expected %q, got %q for length in %d, out %d",
bytes, actual, length, withTime, bytes, actual, inlen, outlen,
) )
} }
} }
expect("0000-00-00", 0, false) expect("0000-00-00", 0, 10)
expect("0000-00-00 00:00:00", 0, true) expect("0000-00-00 00:00:00", 0, 19)
expect("1978-12-30", 4, false) expect("1978-12-30", 4, 10)
expect("1978-12-30 15:46:23", 7, true) expect("1978-12-30 15:46:23", 7, 19)
expect("1978-12-30 15:46:23.987654", 11, true) expect("1978-12-30 15:46:23.987654", 11, 26)
}
func TestEscapeBackslash(t *testing.T) {
expect := func(expected, value string) {
actual := string(escapeBytesBackslash([]byte{}, []byte(value)))
if actual != expected {
t.Errorf(
"expected %s, got %s",
expected, actual,
)
}
actual = string(escapeStringBackslash([]byte{}, value))
if actual != expected {
t.Errorf(
"expected %s, got %s",
expected, actual,
)
}
}
expect("foo\\0bar", "foo\x00bar")
expect("foo\\nbar", "foo\nbar")
expect("foo\\rbar", "foo\rbar")
expect("foo\\Zbar", "foo\x1abar")
expect("foo\\\"bar", "foo\"bar")
expect("foo\\\\bar", "foo\\bar")
expect("foo\\'bar", "foo'bar")
}
func TestEscapeQuotes(t *testing.T) {
expect := func(expected, value string) {
actual := string(escapeBytesQuotes([]byte{}, []byte(value)))
if actual != expected {
t.Errorf(
"expected %s, got %s",
expected, actual,
)
}
actual = string(escapeStringQuotes([]byte{}, value))
if actual != expected {
t.Errorf(
"expected %s, got %s",
expected, actual,
)
}
}
expect("foo\x00bar", "foo\x00bar") // not affected
expect("foo\nbar", "foo\nbar") // not affected
expect("foo\rbar", "foo\rbar") // not affected
expect("foo\x1abar", "foo\x1abar") // not affected
expect("foo''bar", "foo'bar") // affected
expect("foo\"bar", "foo\"bar") // not affected
} }

3
vendor/github.com/goamz/goamz/s3/s3.go сгенерированный поставляемый
Просмотреть файл

@@ -984,6 +984,9 @@ func (s3 *S3) run(req *request, resp interface{}) (*http.Response, error) {
if v, ok := req.headers["Content-Length"]; ok { if v, ok := req.headers["Content-Length"]; ok {
hreq.ContentLength, _ = strconv.ParseInt(v[0], 10, 64) hreq.ContentLength, _ = strconv.ParseInt(v[0], 10, 64)
delete(req.headers, "Content-Length") delete(req.headers, "Content-Length")
if hreq.ContentLength == 0 {
req.payload = nil
}
} }
if req.payload != nil { if req.payload != nil {
hreq.Body = ioutil.NopCloser(req.payload) hreq.Body = ioutil.NopCloser(req.payload)

5
vendor/github.com/golang/freetype/truetype/glyph.go сгенерированный поставляемый
Просмотреть файл

@@ -209,6 +209,7 @@ func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error
g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true) g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true)
copy(g.phantomPoints[:], g.Points[len(g.Points)-4:]) copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
g.Points = g.Points[:len(g.Points)-4] g.Points = g.Points[:len(g.Points)-4]
// TODO: also trim g.InFontUnits and g.Unhinted?
return nil return nil
} }
@@ -282,6 +283,10 @@ func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
program = glyf[offset : offset+instrLen] program = glyf[offset : offset+instrLen]
offset += instrLen offset += instrLen
if ne == 0 {
return program
}
np0 := len(g.Points) np0 := len(g.Points)
np1 := np0 + int(g.Ends[len(g.Ends)-1]) np1 := np0 + int(g.Ends[len(g.Ends)-1])

15
vendor/github.com/gorilla/handlers/recovery.go сгенерированный поставляемый
Просмотреть файл

@@ -6,9 +6,14 @@ import (
"runtime/debug" "runtime/debug"
) )
// RecoveryHandlerLogger is an interface used by the recovering handler to print logs.
type RecoveryHandlerLogger interface {
Println(...interface{})
}
type recoveryHandler struct { type recoveryHandler struct {
handler http.Handler handler http.Handler
logger *log.Logger logger RecoveryHandlerLogger
printStack bool printStack bool
} }
@@ -46,7 +51,7 @@ func RecoveryHandler(opts ...RecoveryOption) func(h http.Handler) http.Handler {
// RecoveryLogger is a functional option to override // RecoveryLogger is a functional option to override
// the default logger // the default logger
func RecoveryLogger(logger *log.Logger) RecoveryOption { func RecoveryLogger(logger RecoveryHandlerLogger) RecoveryOption {
return func(h http.Handler) { return func(h http.Handler) {
r := h.(*recoveryHandler) r := h.(*recoveryHandler)
r.logger = logger r.logger = logger
@@ -73,11 +78,11 @@ func (h recoveryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
h.handler.ServeHTTP(w, req) h.handler.ServeHTTP(w, req)
} }
func (h recoveryHandler) log(message interface{}) { func (h recoveryHandler) log(v ...interface{}) {
if h.logger != nil { if h.logger != nil {
h.logger.Println(message) h.logger.Println(v...)
} else { } else {
log.Println(message) log.Println(v...)
} }
if h.printStack { if h.printStack {

44
vendor/github.com/gorilla/mux/README.md сгенерированный поставляемый
Просмотреть файл

@@ -23,6 +23,7 @@ The name mux stands for "HTTP request multiplexer". Like the standard `http.Serv
* [Install](#install) * [Install](#install)
* [Examples](#examples) * [Examples](#examples)
* [Matching Routes](#matching-routes) * [Matching Routes](#matching-routes)
* [Listing Routes](#listing-routes)
* [Static Files](#static-files) * [Static Files](#static-files)
* [Registered URLs](#registered-urls) * [Registered URLs](#registered-urls)
* [Full Example](#full-example) * [Full Example](#full-example)
@@ -65,8 +66,11 @@ r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`: The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`:
```go ```go
vars := mux.Vars(request) func ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) {
category := vars["category"] vars := mux.Vars(r)
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Category: %v\n", vars["category"])
}
``` ```
And this is all you need to know about the basic usage. More advanced options are explained below. And this is all you need to know about the basic usage. More advanced options are explained below.
@@ -164,6 +168,42 @@ s.HandleFunc("/{key}/", ProductHandler)
s.HandleFunc("/{key}/details", ProductDetailsHandler) s.HandleFunc("/{key}/details", ProductDetailsHandler)
``` ```
### Listing Routes
Routes on a mux can be listed using the Router.Walk method—useful for generating documentation:
```go
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func handler(w http.ResponseWriter, r *http.Request) {
return
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/", handler)
r.HandleFunc("/products", handler)
r.HandleFunc("/articles", handler)
r.HandleFunc("/articles/{id}", handler)
r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
t, err := route.GetPathTemplate()
if err != nil {
return err
}
fmt.Println(t)
return nil
})
http.Handle("/", r)
}
```
### Static Files ### Static Files
Note that the path provided to `PathPrefix()` represents a "wildcard": calling Note that the path provided to `PathPrefix()` represents a "wildcard": calling

5
vendor/github.com/gorilla/mux/doc.go сгенерированный поставляемый
Просмотреть файл

@@ -57,6 +57,11 @@ calling mux.Vars():
vars := mux.Vars(request) vars := mux.Vars(request)
category := vars["category"] category := vars["category"]
Note that if any capturing groups are present, mux will panic() during parsing. To prevent
this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to
"/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably
when capturing groups were present.
And this is all you need to know about the basic usage. More advanced options And this is all you need to know about the basic usage. More advanced options
are explained below. are explained below.

10
vendor/github.com/gorilla/mux/mux_test.go сгенерированный поставляемый
Просмотреть файл

@@ -1389,6 +1389,16 @@ func TestSubrouterErrorHandling(t *testing.T) {
} }
} }
// See: https://github.com/gorilla/mux/issues/200
func TestPanicOnCapturingGroups(t *testing.T) {
defer func() {
if recover() == nil {
t.Errorf("(Test that capturing groups now fail fast) Expected panic, however test completed sucessfully.\n")
}
}()
NewRouter().NewRoute().Path("/{type:(promo|special)}/{promoId}.json")
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Helpers // Helpers
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

7
vendor/github.com/gorilla/mux/regexp.go сгенерированный поставляемый
Просмотреть файл

@@ -109,6 +109,13 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
if errCompile != nil { if errCompile != nil {
return nil, errCompile return nil, errCompile
} }
// Check for capturing groups which used to work in older versions
if reg.NumSubexp() != len(idxs)/2 {
panic(fmt.Sprintf("route %s contains capture groups in its regexp. ", template) +
"Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)")
}
// Done! // Done!
return &routeRegexp{ return &routeRegexp{
template: template, template: template,

26
vendor/github.com/gorilla/websocket/compression.go сгенерированный поставляемый
Просмотреть файл

@@ -1,4 +1,4 @@
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@@ -9,6 +9,11 @@ import (
"errors" "errors"
"io" "io"
"strings" "strings"
"sync"
)
var (
flateWriterPool = sync.Pool{}
) )
func decompressNoContextTakeover(r io.Reader) io.Reader { func decompressNoContextTakeover(r io.Reader) io.Reader {
@@ -17,13 +22,20 @@ func decompressNoContextTakeover(r io.Reader) io.Reader {
"\x00\x00\xff\xff" + "\x00\x00\xff\xff" +
// Add final block to squelch unexpected EOF error from flate reader. // Add final block to squelch unexpected EOF error from flate reader.
"\x01\x00\x00\xff\xff" "\x01\x00\x00\xff\xff"
return flate.NewReader(io.MultiReader(r, strings.NewReader(tail))) return flate.NewReader(io.MultiReader(r, strings.NewReader(tail)))
} }
func compressNoContextTakeover(w io.WriteCloser) (io.WriteCloser, error) { func compressNoContextTakeover(w io.WriteCloser) (io.WriteCloser, error) {
tw := &truncWriter{w: w} tw := &truncWriter{w: w}
fw, err := flate.NewWriter(tw, 3) i := flateWriterPool.Get()
var fw *flate.Writer
var err error
if i == nil {
fw, err = flate.NewWriter(tw, 3)
} else {
fw = i.(*flate.Writer)
fw.Reset(tw)
}
return &flateWrapper{fw: fw, tw: tw}, err return &flateWrapper{fw: fw, tw: tw}, err
} }
@@ -69,11 +81,19 @@ type flateWrapper struct {
} }
func (w *flateWrapper) Write(p []byte) (int, error) { func (w *flateWrapper) Write(p []byte) (int, error) {
if w.fw == nil {
return 0, errWriteClosed
}
return w.fw.Write(p) return w.fw.Write(p)
} }
func (w *flateWrapper) Close() error { func (w *flateWrapper) Close() error {
if w.fw == nil {
return errWriteClosed
}
err1 := w.fw.Flush() err1 := w.fw.Flush()
flateWriterPool.Put(w.fw)
w.fw = nil
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
return errors.New("websocket: internal error, unexpected bytes at end of flate stream") return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
} }

42
vendor/github.com/gorilla/websocket/conn.go сгенерированный поставляемый
Просмотреть файл

@@ -218,6 +218,7 @@ func isValidReceivedCloseCode(code int) bool {
return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
} }
// The Conn type represents a WebSocket connection.
type Conn struct { type Conn struct {
conn net.Conn conn net.Conn
isServer bool isServer bool
@@ -406,12 +407,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er
return err return err
} }
// NextWriter returns a writer for the next message to send. The writer's Close func (c *Conn) prepWrite(messageType int) error {
// method flushes the complete message to the network.
//
// There can be at most one open writer on a connection. NextWriter closes the
// previous writer if the application has not already done so.
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
// Close previous writer if not already closed by the application. It's // Close previous writer if not already closed by the application. It's
// probably better to return an error in this situation, but we cannot // probably better to return an error in this situation, but we cannot
// change this without breaking existing applications. // change this without breaking existing applications.
@@ -421,13 +417,22 @@ func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
} }
if !isControl(messageType) && !isData(messageType) { if !isControl(messageType) && !isData(messageType) {
return nil, errBadWriteOpCode return errBadWriteOpCode
} }
c.writeErrMu.Lock() c.writeErrMu.Lock()
err := c.writeErr err := c.writeErr
c.writeErrMu.Unlock() c.writeErrMu.Unlock()
if err != nil { return err
}
// NextWriter returns a writer for the next message to send. The writer's Close
// method flushes the complete message to the network.
//
// There can be at most one open writer on a connection. NextWriter closes the
// previous writer if the application has not already done so.
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
if err := c.prepWrite(messageType); err != nil {
return nil, err return nil, err
} }
@@ -652,16 +657,23 @@ func (w *messageWriter) Close() error {
// WriteMessage is a helper method for getting a writer using NextWriter, // WriteMessage is a helper method for getting a writer using NextWriter,
// writing the message and closing the writer. // writing the message and closing the writer.
func (c *Conn) WriteMessage(messageType int, data []byte) error { func (c *Conn) WriteMessage(messageType int, data []byte) error {
w, err := c.NextWriter(messageType)
if err != nil { if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {
return err
} // Fast path with no allocations and single frame.
if mw, ok := w.(*messageWriter); ok && c.isServer {
// Optimize write as a single frame. if err := c.prepWrite(messageType); err != nil {
return err
}
mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize}
n := copy(c.writeBuf[mw.pos:], data) n := copy(c.writeBuf[mw.pos:], data)
mw.pos += n mw.pos += n
data = data[n:] data = data[n:]
err = mw.flushFrame(true, data) return mw.flushFrame(true, data)
}
w, err := c.NextWriter(messageType)
if err != nil {
return err return err
} }
if _, err = w.Write(data); err != nil { if _, err = w.Write(data); err != nil {

14
vendor/github.com/lib/pq/.travis.yml сгенерированный поставляемый
Просмотреть файл

@@ -1,10 +1,11 @@
language: go language: go
go: go:
- 1.5 - 1.5.x
- 1.6 - 1.6.x
- 1.7 - 1.7.x
- tip - 1.8.x
- master
sudo: true sudo: true
@@ -15,6 +16,7 @@ env:
- PQSSLCERTTEST_PATH=$PWD/certs - PQSSLCERTTEST_PATH=$PWD/certs
- PGHOST=127.0.0.1 - PGHOST=127.0.0.1
matrix: matrix:
- PGVERSION=9.6
- PGVERSION=9.5 - PGVERSION=9.5
- PGVERSION=9.4 - PGVERSION=9.4
- PGVERSION=9.3 - PGVERSION=9.3
@@ -39,5 +41,5 @@ script:
- > - >
goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }' goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
- go vet ./... - go vet ./...
- PQTEST_BINARY_PARAMETERS=no go test -v ./... - PQTEST_BINARY_PARAMETERS=no go test -race -v ./...
- PQTEST_BINARY_PARAMETERS=yes go test -v ./... - PQTEST_BINARY_PARAMETERS=yes go test -race -v ./...

45
vendor/github.com/lib/pq/array.go сгенерированный поставляемый
Просмотреть файл

@@ -70,6 +70,9 @@ func (a *BoolArray) Scan(src interface{}) error {
return a.scanBytes(src) return a.scanBytes(src)
case string: case string:
return a.scanBytes([]byte(src)) return a.scanBytes([]byte(src))
case nil:
*a = nil
return nil
} }
return fmt.Errorf("pq: cannot convert %T to BoolArray", src) return fmt.Errorf("pq: cannot convert %T to BoolArray", src)
@@ -80,7 +83,7 @@ func (a *BoolArray) scanBytes(src []byte) error {
if err != nil { if err != nil {
return err return err
} }
if len(elems) == 0 { if *a != nil && len(elems) == 0 {
*a = (*a)[:0] *a = (*a)[:0]
} else { } else {
b := make(BoolArray, len(elems)) b := make(BoolArray, len(elems))
@@ -141,6 +144,9 @@ func (a *ByteaArray) Scan(src interface{}) error {
return a.scanBytes(src) return a.scanBytes(src)
case string: case string:
return a.scanBytes([]byte(src)) return a.scanBytes([]byte(src))
case nil:
*a = nil
return nil
} }
return fmt.Errorf("pq: cannot convert %T to ByteaArray", src) return fmt.Errorf("pq: cannot convert %T to ByteaArray", src)
@@ -151,7 +157,7 @@ func (a *ByteaArray) scanBytes(src []byte) error {
if err != nil { if err != nil {
return err return err
} }
if len(elems) == 0 { if *a != nil && len(elems) == 0 {
*a = (*a)[:0] *a = (*a)[:0]
} else { } else {
b := make(ByteaArray, len(elems)) b := make(ByteaArray, len(elems))
@@ -210,6 +216,9 @@ func (a *Float64Array) Scan(src interface{}) error {
return a.scanBytes(src) return a.scanBytes(src)
case string: case string:
return a.scanBytes([]byte(src)) return a.scanBytes([]byte(src))
case nil:
*a = nil
return nil
} }
return fmt.Errorf("pq: cannot convert %T to Float64Array", src) return fmt.Errorf("pq: cannot convert %T to Float64Array", src)
@@ -220,7 +229,7 @@ func (a *Float64Array) scanBytes(src []byte) error {
if err != nil { if err != nil {
return err return err
} }
if len(elems) == 0 { if *a != nil && len(elems) == 0 {
*a = (*a)[:0] *a = (*a)[:0]
} else { } else {
b := make(Float64Array, len(elems)) b := make(Float64Array, len(elems))
@@ -320,6 +329,11 @@ func (a GenericArray) Scan(src interface{}) error {
return a.scanBytes(src, dv) return a.scanBytes(src, dv)
case string: case string:
return a.scanBytes([]byte(src), dv) return a.scanBytes([]byte(src), dv)
case nil:
if dv.Kind() == reflect.Slice {
dv.Set(reflect.Zero(dv.Type()))
return nil
}
} }
return fmt.Errorf("pq: cannot convert %T to %s", src, dv.Type()) return fmt.Errorf("pq: cannot convert %T to %s", src, dv.Type())
@@ -386,7 +400,13 @@ func (a GenericArray) Value() (driver.Value, error) {
rv := reflect.ValueOf(a.A) rv := reflect.ValueOf(a.A)
if k := rv.Kind(); k != reflect.Array && k != reflect.Slice { switch rv.Kind() {
case reflect.Slice:
if rv.IsNil() {
return nil, nil
}
case reflect.Array:
default:
return nil, fmt.Errorf("pq: Unable to convert %T to array", a.A) return nil, fmt.Errorf("pq: Unable to convert %T to array", a.A)
} }
@@ -412,6 +432,9 @@ func (a *Int64Array) Scan(src interface{}) error {
return a.scanBytes(src) return a.scanBytes(src)
case string: case string:
return a.scanBytes([]byte(src)) return a.scanBytes([]byte(src))
case nil:
*a = nil
return nil
} }
return fmt.Errorf("pq: cannot convert %T to Int64Array", src) return fmt.Errorf("pq: cannot convert %T to Int64Array", src)
@@ -422,7 +445,7 @@ func (a *Int64Array) scanBytes(src []byte) error {
if err != nil { if err != nil {
return err return err
} }
if len(elems) == 0 { if *a != nil && len(elems) == 0 {
*a = (*a)[:0] *a = (*a)[:0]
} else { } else {
b := make(Int64Array, len(elems)) b := make(Int64Array, len(elems))
@@ -470,6 +493,9 @@ func (a *StringArray) Scan(src interface{}) error {
return a.scanBytes(src) return a.scanBytes(src)
case string: case string:
return a.scanBytes([]byte(src)) return a.scanBytes([]byte(src))
case nil:
*a = nil
return nil
} }
return fmt.Errorf("pq: cannot convert %T to StringArray", src) return fmt.Errorf("pq: cannot convert %T to StringArray", src)
@@ -480,7 +506,7 @@ func (a *StringArray) scanBytes(src []byte) error {
if err != nil { if err != nil {
return err return err
} }
if len(elems) == 0 { if *a != nil && len(elems) == 0 {
*a = (*a)[:0] *a = (*a)[:0]
} else { } else {
b := make(StringArray, len(elems)) b := make(StringArray, len(elems))
@@ -639,6 +665,9 @@ Element:
for i < len(src) { for i < len(src) {
switch src[i] { switch src[i] {
case '{': case '{':
if depth == len(dims) {
break Element
}
depth++ depth++
dims[depth-1] = 0 dims[depth-1] = 0
i++ i++
@@ -680,11 +709,11 @@ Element:
} }
for i < len(src) { for i < len(src) {
if bytes.HasPrefix(src[i:], del) { if bytes.HasPrefix(src[i:], del) && depth > 0 {
dims[depth-1]++ dims[depth-1]++
i += len(del) i += len(del)
goto Element goto Element
} else if src[i] == '}' { } else if src[i] == '}' && depth > 0 {
dims[depth-1]++ dims[depth-1]++
depth-- depth--
i++ i++

164
vendor/github.com/lib/pq/array_test.go сгенерированный поставляемый
Просмотреть файл

@@ -70,6 +70,10 @@ func TestParseArrayError(t *testing.T) {
{`{,}`, "unexpected ',' at offset 1"}, {`{,}`, "unexpected ',' at offset 1"},
{`{,x}`, "unexpected ',' at offset 1"}, {`{,x}`, "unexpected ',' at offset 1"},
{`{x,}`, "unexpected '}' at offset 3"}, {`{x,}`, "unexpected '}' at offset 3"},
{`{x,{`, "unexpected '{' at offset 3"},
{`{x},`, "unexpected ',' at offset 3"},
{`{x}}`, "unexpected '}' at offset 3"},
{`{{x}`, "expected '}' at offset 4"},
{`{""x}`, "unexpected 'x' at offset 3"}, {`{""x}`, "unexpected 'x' at offset 3"},
{`{{a},{b,c}}`, "multidimensional arrays must have elements with matching dimensions"}, {`{{a},{b,c}}`, "multidimensional arrays must have elements with matching dimensions"},
} { } {
@@ -171,6 +175,30 @@ func TestBoolArrayScanUnsupported(t *testing.T) {
} }
} }
func TestBoolArrayScanEmpty(t *testing.T) {
var arr BoolArray
err := arr.Scan(`{}`)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if arr == nil || len(arr) != 0 {
t.Errorf("Expected empty, got %#v", arr)
}
}
func TestBoolArrayScanNil(t *testing.T) {
arr := BoolArray{true, true, true}
err := arr.Scan(nil)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if arr != nil {
t.Errorf("Expected nil, got %+v", arr)
}
}
var BoolArrayStringTests = []struct { var BoolArrayStringTests = []struct {
str string str string
arr BoolArray arr BoolArray
@@ -300,6 +328,30 @@ func TestByteaArrayScanUnsupported(t *testing.T) {
} }
} }
func TestByteaArrayScanEmpty(t *testing.T) {
var arr ByteaArray
err := arr.Scan(`{}`)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if arr == nil || len(arr) != 0 {
t.Errorf("Expected empty, got %#v", arr)
}
}
func TestByteaArrayScanNil(t *testing.T) {
arr := ByteaArray{{2}, {6}, {0, 0}}
err := arr.Scan(nil)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if arr != nil {
t.Errorf("Expected nil, got %+v", arr)
}
}
var ByteaArrayStringTests = []struct { var ByteaArrayStringTests = []struct {
str string str string
arr ByteaArray arr ByteaArray
@@ -430,6 +482,30 @@ func TestFloat64ArrayScanUnsupported(t *testing.T) {
} }
} }
func TestFloat64ArrayScanEmpty(t *testing.T) {
var arr Float64Array
err := arr.Scan(`{}`)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if arr == nil || len(arr) != 0 {
t.Errorf("Expected empty, got %#v", arr)
}
}
func TestFloat64ArrayScanNil(t *testing.T) {
arr := Float64Array{5, 5, 5}
err := arr.Scan(nil)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if arr != nil {
t.Errorf("Expected nil, got %+v", arr)
}
}
var Float64ArrayStringTests = []struct { var Float64ArrayStringTests = []struct {
str string str string
arr Float64Array arr Float64Array
@@ -560,6 +636,30 @@ func TestInt64ArrayScanUnsupported(t *testing.T) {
} }
} }
func TestInt64ArrayScanEmpty(t *testing.T) {
var arr Int64Array
err := arr.Scan(`{}`)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if arr == nil || len(arr) != 0 {
t.Errorf("Expected empty, got %#v", arr)
}
}
func TestInt64ArrayScanNil(t *testing.T) {
arr := Int64Array{5, 5, 5}
err := arr.Scan(nil)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if arr != nil {
t.Errorf("Expected nil, got %+v", arr)
}
}
var Int64ArrayStringTests = []struct { var Int64ArrayStringTests = []struct {
str string str string
arr Int64Array arr Int64Array
@@ -689,6 +789,30 @@ func TestStringArrayScanUnsupported(t *testing.T) {
} }
} }
func TestStringArrayScanEmpty(t *testing.T) {
var arr StringArray
err := arr.Scan(`{}`)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if arr == nil || len(arr) != 0 {
t.Errorf("Expected empty, got %#v", arr)
}
}
func TestStringArrayScanNil(t *testing.T) {
arr := StringArray{"x", "x", "x"}
err := arr.Scan(nil)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if arr != nil {
t.Errorf("Expected nil, got %+v", arr)
}
}
var StringArrayStringTests = []struct { var StringArrayStringTests = []struct {
str string str string
arr StringArray arr StringArray
@@ -811,6 +935,7 @@ func BenchmarkStringArrayValue(b *testing.B) {
func TestGenericArrayScanUnsupported(t *testing.T) { func TestGenericArrayScanUnsupported(t *testing.T) {
var s string var s string
var ss []string var ss []string
var nsa [1]sql.NullString
for _, tt := range []struct { for _, tt := range []struct {
src, dest interface{} src, dest interface{}
@@ -820,6 +945,7 @@ func TestGenericArrayScanUnsupported(t *testing.T) {
{nil, true, "destination bool is not a pointer to array or slice"}, {nil, true, "destination bool is not a pointer to array or slice"},
{nil, &s, "destination *string is not a pointer to array or slice"}, {nil, &s, "destination *string is not a pointer to array or slice"},
{nil, ss, "destination []string is not a pointer to array or slice"}, {nil, ss, "destination []string is not a pointer to array or slice"},
{nil, &nsa, "<nil> to [1]sql.NullString"},
{true, &ss, "bool to []string"}, {true, &ss, "bool to []string"},
{`{{x}}`, &ss, "multidimensional ARRAY[1][1] is not implemented"}, {`{{x}}`, &ss, "multidimensional ARRAY[1][1] is not implemented"},
{`{{x},{x}}`, &ss, "multidimensional ARRAY[2][1] is not implemented"}, {`{{x},{x}}`, &ss, "multidimensional ARRAY[2][1] is not implemented"},
@@ -862,6 +988,28 @@ func TestGenericArrayScanScannerArrayString(t *testing.T) {
} }
} }
func TestGenericArrayScanScannerSliceEmpty(t *testing.T) {
var nss []sql.NullString
if err := (GenericArray{&nss}).Scan(`{}`); err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if nss == nil || len(nss) != 0 {
t.Errorf("Expected empty, got %#v", nss)
}
}
func TestGenericArrayScanScannerSliceNil(t *testing.T) {
nss := []sql.NullString{{String: ``, Valid: true}, {}}
if err := (GenericArray{&nss}).Scan(nil); err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if nss != nil {
t.Errorf("Expected nil, got %+v", nss)
}
}
func TestGenericArrayScanScannerSliceBytes(t *testing.T) { func TestGenericArrayScanScannerSliceBytes(t *testing.T) {
src, expected, nss := []byte(`{NULL,abc,"\""}`), src, expected, nss := []byte(`{NULL,abc,"\""}`),
[]sql.NullString{{}, {String: `abc`, Valid: true}, {String: `"`, Valid: true}}, []sql.NullString{{}, {String: `abc`, Valid: true}, {String: `"`, Valid: true}},
@@ -977,6 +1125,22 @@ func TestGenericArrayValue(t *testing.T) {
t.Errorf("Expected nil, got %q", result) t.Errorf("Expected nil, got %q", result)
} }
for _, tt := range []interface{}{
[]bool(nil),
[][]int(nil),
[]*int(nil),
[]sql.NullString(nil),
} {
result, err := GenericArray{tt}.Value()
if err != nil {
t.Fatalf("Expected no error for %#v, got %v", tt, err)
}
if result != nil {
t.Errorf("Expected nil for %#v, got %q", tt, result)
}
}
Tilde := func(v driver.Value) FuncArrayValuer { Tilde := func(v driver.Value) FuncArrayValuer {
return FuncArrayValuer{ return FuncArrayValuer{
func() string { return "~" }, func() string { return "~" },

189
vendor/github.com/lib/pq/conn.go сгенерированный поставляемый
Просмотреть файл

@@ -3,15 +3,12 @@ package pq
import ( import (
"bufio" "bufio"
"crypto/md5" "crypto/md5"
"crypto/tls"
"crypto/x509"
"database/sql" "database/sql"
"database/sql/driver" "database/sql/driver"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"os" "os"
"os/user" "os/user"
@@ -101,6 +98,15 @@ type conn struct {
namei int namei int
scratch [512]byte scratch [512]byte
txnStatus transactionStatus txnStatus transactionStatus
txnClosed chan<- struct{}
// Save connection arguments to use during CancelRequest.
dialer Dialer
opts values
// Cancellation key data for use with CancelRequest messages.
processID int
secretKey int
parameterStatus parameterStatus parameterStatus parameterStatus
@@ -310,7 +316,10 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
} }
} }
cn := &conn{} cn := &conn{
opts: o,
dialer: d,
}
err = cn.handleDriverSettings(o) err = cn.handleDriverSettings(o)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -532,7 +541,15 @@ func (cn *conn) Begin() (_ driver.Tx, err error) {
return cn, nil return cn, nil
} }
func (cn *conn) closeTxn() {
if cn.txnClosed != nil {
close(cn.txnClosed)
cn.txnClosed = nil
}
}
func (cn *conn) Commit() (err error) { func (cn *conn) Commit() (err error) {
defer cn.closeTxn()
if cn.bad { if cn.bad {
return driver.ErrBadConn return driver.ErrBadConn
} }
@@ -568,6 +585,7 @@ func (cn *conn) Commit() (err error) {
} }
func (cn *conn) Rollback() (err error) { func (cn *conn) Rollback() (err error) {
defer cn.closeTxn()
if cn.bad { if cn.bad {
return driver.ErrBadConn return driver.ErrBadConn
} }
@@ -718,6 +736,8 @@ func decideColumnFormats(colTyps []oid.Oid, forceText bool) (colFmts []format, c
case oid.T_int4: case oid.T_int4:
fallthrough fallthrough
case oid.T_int2: case oid.T_int2:
fallthrough
case oid.T_uuid:
colFmts[i] = formatBinary colFmts[i] = formatBinary
allText = false allText = false
@@ -797,7 +817,11 @@ func (cn *conn) Close() (err error) {
} }
// Implement the "Queryer" interface // Implement the "Queryer" interface
func (cn *conn) Query(query string, args []driver.Value) (_ driver.Rows, err error) { func (cn *conn) Query(query string, args []driver.Value) (driver.Rows, error) {
return cn.query(query, args)
}
func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
if cn.bad { if cn.bad {
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
@@ -1000,42 +1024,12 @@ func (cn *conn) recv1() (t byte, r *readBuf) {
} }
func (cn *conn) ssl(o values) { func (cn *conn) ssl(o values) {
verifyCaOnly := false upgrade := ssl(o)
tlsConf := tls.Config{} if upgrade == nil {
switch mode := o.Get("sslmode"); mode { // Nothing to do
// "require" is the default.
case "", "require":
// We must skip TLS's own verification since it requires full
// verification since Go 1.3.
tlsConf.InsecureSkipVerify = true
// From http://www.postgresql.org/docs/current/static/libpq-ssl.html:
// Note: For backwards compatibility with earlier versions of PostgreSQL, if a
// root CA file exists, the behavior of sslmode=require will be the same as
// that of verify-ca, meaning the server certificate is validated against the
// CA. Relying on this behavior is discouraged, and applications that need
// certificate validation should always use verify-ca or verify-full.
if _, err := os.Stat(o.Get("sslrootcert")); err == nil {
verifyCaOnly = true
} else {
o.Set("sslrootcert", "")
}
case "verify-ca":
// We must skip TLS's own verification since it requires full
// verification since Go 1.3.
tlsConf.InsecureSkipVerify = true
verifyCaOnly = true
case "verify-full":
tlsConf.ServerName = o.Get("host")
case "disable":
return return
default:
errorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode)
} }
cn.setupSSLClientCertificates(&tlsConf, o)
cn.setupSSLCA(&tlsConf, o)
w := cn.writeBuf(0) w := cn.writeBuf(0)
w.int32(80877103) w.int32(80877103)
cn.sendStartupPacket(w) cn.sendStartupPacket(w)
@@ -1050,114 +1044,7 @@ func (cn *conn) ssl(o values) {
panic(ErrSSLNotSupported) panic(ErrSSLNotSupported)
} }
client := tls.Client(cn.c, &tlsConf) cn.c = upgrade(cn.c)
if verifyCaOnly {
cn.verifyCA(client, &tlsConf)
}
cn.c = client
}
// verifyCA carries out a TLS handshake to the server and verifies the
// presented certificate against the effective CA, i.e. the one specified in
// sslrootcert or the system CA if sslrootcert was not specified.
func (cn *conn) verifyCA(client *tls.Conn, tlsConf *tls.Config) {
err := client.Handshake()
if err != nil {
panic(err)
}
certs := client.ConnectionState().PeerCertificates
opts := x509.VerifyOptions{
DNSName: client.ConnectionState().ServerName,
Intermediates: x509.NewCertPool(),
Roots: tlsConf.RootCAs,
}
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
if err != nil {
panic(err)
}
}
// This function sets up SSL client certificates based on either the "sslkey"
// and "sslcert" settings (possibly set via the environment variables PGSSLKEY
// and PGSSLCERT, respectively), or if they aren't set, from the .postgresql
// directory in the user's home directory. If the file paths are set
// explicitly, the files must exist. The key file must also not be
// world-readable, or this function will panic with
// ErrSSLKeyHasWorldPermissions.
func (cn *conn) setupSSLClientCertificates(tlsConf *tls.Config, o values) {
var missingOk bool
sslkey := o.Get("sslkey")
sslcert := o.Get("sslcert")
if sslkey != "" && sslcert != "" {
// If the user has set an sslkey and sslcert, they *must* exist.
missingOk = false
} else {
// Automatically load certificates from ~/.postgresql.
user, err := user.Current()
if err != nil {
// user.Current() might fail when cross-compiling. We have to
// ignore the error and continue without client certificates, since
// we wouldn't know where to load them from.
return
}
sslkey = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key")
sslcert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt")
missingOk = true
}
// Check that both files exist, and report the error or stop, depending on
// which behaviour we want. Note that we don't do any more extensive
// checks than this (such as checking that the paths aren't directories);
// LoadX509KeyPair() will take care of the rest.
keyfinfo, err := os.Stat(sslkey)
if err != nil && missingOk {
return
} else if err != nil {
panic(err)
}
_, err = os.Stat(sslcert)
if err != nil && missingOk {
return
} else if err != nil {
panic(err)
}
// If we got this far, the key file must also have the correct permissions
kmode := keyfinfo.Mode()
if kmode != kmode&0600 {
panic(ErrSSLKeyHasWorldPermissions)
}
cert, err := tls.LoadX509KeyPair(sslcert, sslkey)
if err != nil {
panic(err)
}
tlsConf.Certificates = []tls.Certificate{cert}
}
// Sets up RootCAs in the TLS configuration if sslrootcert is set.
func (cn *conn) setupSSLCA(tlsConf *tls.Config, o values) {
if sslrootcert := o.Get("sslrootcert"); sslrootcert != "" {
tlsConf.RootCAs = x509.NewCertPool()
cert, err := ioutil.ReadFile(sslrootcert)
if err != nil {
panic(err)
}
ok := tlsConf.RootCAs.AppendCertsFromPEM(cert)
if !ok {
errorf("couldn't parse pem in sslrootcert")
}
}
} }
// isDriverSetting returns true iff a setting is purely for configuring the // isDriverSetting returns true iff a setting is purely for configuring the
@@ -1212,6 +1099,7 @@ func (cn *conn) startup(o values) {
t, r := cn.recv() t, r := cn.recv()
switch t { switch t {
case 'K': case 'K':
cn.processBackendKeyData(r)
case 'S': case 'S':
cn.processParameterStatus(r) cn.processParameterStatus(r)
case 'R': case 'R':
@@ -1439,6 +1327,7 @@ func (cn *conn) parseComplete(commandTag string) (driver.Result, string) {
type rows struct { type rows struct {
cn *conn cn *conn
closed chan<- struct{}
colNames []string colNames []string
colTyps []oid.Oid colTyps []oid.Oid
colFmts []format colFmts []format
@@ -1447,6 +1336,9 @@ type rows struct {
} }
func (rs *rows) Close() error { func (rs *rows) Close() error {
if rs.closed != nil {
defer close(rs.closed)
}
// no need to look at cn.bad as Next() will // no need to look at cn.bad as Next() will
for { for {
err := rs.Next(nil) err := rs.Next(nil)
@@ -1651,6 +1543,11 @@ func (cn *conn) readReadyForQuery() {
} }
} }
func (c *conn) processBackendKeyData(r *readBuf) {
c.processID = r.int32()
c.secretKey = r.int32()
}
func (cn *conn) readParseResponse() { func (cn *conn) readParseResponse() {
t, r := cn.recv1() t, r := cn.recv1()
switch t { switch t {

92
vendor/github.com/lib/pq/conn_go18.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,92 @@
// +build go1.8
package pq
import (
"context"
"database/sql/driver"
"errors"
)
// Implement the "QueryerContext" interface
func (cn *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
list := make([]driver.Value, len(args))
for i, nv := range args {
list[i] = nv.Value
}
var closed chan<- struct{}
if ctx.Done() != nil {
closed = watchCancel(ctx, cn.cancel)
}
r, err := cn.query(query, list)
if err != nil {
return nil, err
}
r.closed = closed
return r, nil
}
// Implement the "ExecerContext" interface
func (cn *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
list := make([]driver.Value, len(args))
for i, nv := range args {
list[i] = nv.Value
}
if ctx.Done() != nil {
closed := watchCancel(ctx, cn.cancel)
defer close(closed)
}
return cn.Exec(query, list)
}
// Implement the "ConnBeginTx" interface
func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if opts.Isolation != 0 {
return nil, errors.New("isolation levels not supported")
}
if opts.ReadOnly {
return nil, errors.New("read-only transactions not supported")
}
tx, err := cn.Begin()
if err != nil {
return nil, err
}
if ctx.Done() != nil {
cn.txnClosed = watchCancel(ctx, cn.cancel)
}
return tx, nil
}
func watchCancel(ctx context.Context, cancel func()) chan<- struct{} {
closed := make(chan struct{})
go func() {
select {
case <-ctx.Done():
cancel()
case <-closed:
}
}()
return closed
}
func (cn *conn) cancel() {
var err error
can := &conn{}
can.c, err = dial(cn.dialer, cn.opts)
if err != nil {
return
}
can.ssl(cn.opts)
defer can.errRecover(&err)
w := can.writeBuf(0)
w.int32(80877102) // cancel request code
w.int32(cn.processID)
w.int32(cn.secretKey)
can.sendStartupPacket(w)
_ = can.c.Close()
}

27
vendor/github.com/lib/pq/copy.go сгенерированный поставляемый
Просмотреть файл

@@ -97,13 +97,13 @@ awaitCopyInResponse:
err = parseError(r) err = parseError(r)
case 'Z': case 'Z':
if err == nil { if err == nil {
cn.bad = true ci.setBad()
errorf("unexpected ReadyForQuery in response to COPY") errorf("unexpected ReadyForQuery in response to COPY")
} }
cn.processReadyForQuery(r) cn.processReadyForQuery(r)
return nil, err return nil, err
default: default:
cn.bad = true ci.setBad()
errorf("unknown response for copy query: %q", t) errorf("unknown response for copy query: %q", t)
} }
} }
@@ -122,7 +122,7 @@ awaitCopyInResponse:
cn.processReadyForQuery(r) cn.processReadyForQuery(r)
return nil, err return nil, err
default: default:
cn.bad = true ci.setBad()
errorf("unknown response for CopyFail: %q", t) errorf("unknown response for CopyFail: %q", t)
} }
} }
@@ -143,7 +143,7 @@ func (ci *copyin) resploop() {
var r readBuf var r readBuf
t, err := ci.cn.recvMessage(&r) t, err := ci.cn.recvMessage(&r)
if err != nil { if err != nil {
ci.cn.bad = true ci.setBad()
ci.setError(err) ci.setError(err)
ci.done <- true ci.done <- true
return return
@@ -161,7 +161,7 @@ func (ci *copyin) resploop() {
err := parseError(&r) err := parseError(&r)
ci.setError(err) ci.setError(err)
default: default:
ci.cn.bad = true ci.setBad()
ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t)) ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t))
ci.done <- true ci.done <- true
return return
@@ -169,6 +169,19 @@ func (ci *copyin) resploop() {
} }
} }
func (ci *copyin) setBad() {
ci.Lock()
ci.cn.bad = true
ci.Unlock()
}
func (ci *copyin) isBad() bool {
ci.Lock()
b := ci.cn.bad
ci.Unlock()
return b
}
func (ci *copyin) isErrorSet() bool { func (ci *copyin) isErrorSet() bool {
ci.Lock() ci.Lock()
isSet := (ci.err != nil) isSet := (ci.err != nil)
@@ -206,7 +219,7 @@ func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
return nil, errCopyInClosed return nil, errCopyInClosed
} }
if ci.cn.bad { if ci.isBad() {
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
defer ci.cn.errRecover(&err) defer ci.cn.errRecover(&err)
@@ -244,7 +257,7 @@ func (ci *copyin) Close() (err error) {
} }
ci.closed = true ci.closed = true
if ci.cn.bad { if ci.isBad() {
return driver.ErrBadConn return driver.ErrBadConn
} }
defer ci.cn.errRecover(&err) defer ci.cn.errRecover(&err)

8
vendor/github.com/lib/pq/encode.go сгенерированный поставляемый
Просмотреть файл

@@ -76,6 +76,12 @@ func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) inter
return int64(int32(binary.BigEndian.Uint32(s))) return int64(int32(binary.BigEndian.Uint32(s)))
case oid.T_int2: case oid.T_int2:
return int64(int16(binary.BigEndian.Uint16(s))) return int64(int16(binary.BigEndian.Uint16(s)))
case oid.T_uuid:
b, err := decodeUUIDBinary(s)
if err != nil {
panic(err)
}
return b
default: default:
errorf("don't know how to decode binary parameter of type %d", uint32(typ)) errorf("don't know how to decode binary parameter of type %d", uint32(typ))
@@ -471,7 +477,7 @@ func FormatTimestamp(t time.Time) []byte {
t = t.AddDate((-t.Year())*2+1, 0, 0) t = t.AddDate((-t.Year())*2+1, 0, 0)
bc = true bc = true
} }
b := []byte(t.Format(time.RFC3339Nano)) b := []byte(t.Format("2006-01-02 15:04:05.999999999Z07:00"))
_, offset := t.Zone() _, offset := t.Zone()
offset = offset % 60 offset = offset % 60

46
vendor/github.com/lib/pq/encode_test.go сгенерированный поставляемый
Просмотреть файл

@@ -141,22 +141,22 @@ var formatTimeTests = []struct {
time time.Time time time.Time
expected string expected string
}{ }{
{time.Time{}, "0001-01-01T00:00:00Z"}, {time.Time{}, "0001-01-01 00:00:00Z"},
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03T04:05:06.123456789Z"}, {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03 04:05:06.123456789Z"},
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03T04:05:06.123456789+02:00"}, {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03 04:05:06.123456789+02:00"},
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03T04:05:06.123456789-06:00"}, {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03 04:05:06.123456789-06:00"},
{time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "2001-02-03T04:05:06-07:30:09"}, {time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "2001-02-03 04:05:06-07:30:09"},
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z"}, {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03 04:05:06.123456789Z"},
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00"}, {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03 04:05:06.123456789+02:00"},
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00"}, {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03 04:05:06.123456789-06:00"},
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z BC"}, {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03 04:05:06.123456789Z BC"},
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00 BC"}, {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03 04:05:06.123456789+02:00 BC"},
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00 BC"}, {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03 04:05:06.123456789-06:00 BC"},
{time.Date(1, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09"}, {time.Date(1, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03 04:05:06-07:30:09"},
{time.Date(0, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09 BC"}, {time.Date(0, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03 04:05:06-07:30:09 BC"},
} }
func TestFormatTs(t *testing.T) { func TestFormatTs(t *testing.T) {
@@ -168,6 +168,26 @@ func TestFormatTs(t *testing.T) {
} }
} }
func TestFormatTsBackend(t *testing.T) {
db := openTestConn(t)
defer db.Close()
var str string
err := db.QueryRow("SELECT '2001-02-03T04:05:06.007-08:09:10'::time::text").Scan(&str)
if err == nil {
t.Fatalf("PostgreSQL is accepting an ISO timestamp input for time")
}
for i, tt := range formatTimeTests {
for _, typ := range []string{"date", "time", "timetz", "timestamp", "timestamptz"} {
err = db.QueryRow("SELECT $1::"+typ+"::text", tt.time).Scan(&str)
if err != nil {
t.Errorf("%d: incorrect time format for %v on the backend: %v", i, typ, err)
}
}
}
}
func TestTimestampWithTimeZone(t *testing.T) { func TestTimestampWithTimeZone(t *testing.T) {
db := openTestConn(t) db := openTestConn(t)
defer db.Close() defer db.Close()

99
vendor/github.com/lib/pq/go18_test.go сгенерированный поставляемый
Просмотреть файл

@@ -2,7 +2,12 @@
package pq package pq
import "testing" import (
"context"
"database/sql"
"testing"
"time"
)
func TestMultipleSimpleQuery(t *testing.T) { func TestMultipleSimpleQuery(t *testing.T) {
db := openTestConn(t) db := openTestConn(t)
@@ -66,3 +71,95 @@ func TestMultipleSimpleQuery(t *testing.T) {
t.Fatal("unexpected result set") t.Fatal("unexpected result set")
} }
} }
func TestContextCancelExec(t *testing.T) {
db := openTestConn(t)
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
// Delay execution for just a bit until db.ExecContext has begun.
go func() {
time.Sleep(time.Millisecond * 10)
cancel()
}()
// Not canceled until after the exec has started.
if _, err := db.ExecContext(ctx, "select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err.Error() != "pq: canceling statement due to user request" {
t.Fatalf("unexpected error: %s", err)
}
// Context is already canceled, so error should come before execution.
if _, err := db.ExecContext(ctx, "select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err.Error() != "context canceled" {
t.Fatalf("unexpected error: %s", err)
}
}
func TestContextCancelQuery(t *testing.T) {
db := openTestConn(t)
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
// Delay execution for just a bit until db.QueryContext has begun.
go func() {
time.Sleep(time.Millisecond * 10)
cancel()
}()
// Not canceled until after the exec has started.
if _, err := db.QueryContext(ctx, "select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err.Error() != "pq: canceling statement due to user request" {
t.Fatalf("unexpected error: %s", err)
}
// Context is already canceled, so error should come before execution.
if _, err := db.QueryContext(ctx, "select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err.Error() != "context canceled" {
t.Fatalf("unexpected error: %s", err)
}
}
func TestContextCancelBegin(t *testing.T) {
db := openTestConn(t)
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
tx, err := db.BeginTx(ctx, nil)
if err != nil {
t.Fatal(err)
}
// Delay execution for just a bit until tx.Exec has begun.
go func() {
time.Sleep(time.Millisecond * 10)
cancel()
}()
// Not canceled until after the exec has started.
if _, err := tx.Exec("select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err.Error() != "pq: canceling statement due to user request" {
t.Fatalf("unexpected error: %s", err)
}
// Transaction is canceled, so expect an error.
if _, err := tx.Query("select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err != sql.ErrTxDone {
t.Fatalf("unexpected error: %s", err)
}
// Context is canceled, so cannot begin a transaction.
if _, err := db.BeginTx(ctx, nil); err == nil {
t.Fatal("expected error")
} else if err.Error() != "context canceled" {
t.Fatalf("unexpected error: %s", err)
}
}

175
vendor/github.com/lib/pq/ssl.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,175 @@
package pq
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net"
"os"
"os/user"
"path/filepath"
)
// ssl generates a function to upgrade a net.Conn based on the "sslmode" and
// related settings. The function is nil when no upgrade should take place.
func ssl(o values) func(net.Conn) net.Conn {
verifyCaOnly := false
tlsConf := tls.Config{}
switch mode := o.Get("sslmode"); mode {
// "require" is the default.
case "", "require":
// We must skip TLS's own verification since it requires full
// verification since Go 1.3.
tlsConf.InsecureSkipVerify = true
// From http://www.postgresql.org/docs/current/static/libpq-ssl.html:
// Note: For backwards compatibility with earlier versions of PostgreSQL, if a
// root CA file exists, the behavior of sslmode=require will be the same as
// that of verify-ca, meaning the server certificate is validated against the
// CA. Relying on this behavior is discouraged, and applications that need
// certificate validation should always use verify-ca or verify-full.
if _, err := os.Stat(o.Get("sslrootcert")); err == nil {
verifyCaOnly = true
} else {
o.Set("sslrootcert", "")
}
case "verify-ca":
// We must skip TLS's own verification since it requires full
// verification since Go 1.3.
tlsConf.InsecureSkipVerify = true
verifyCaOnly = true
case "verify-full":
tlsConf.ServerName = o.Get("host")
case "disable":
return nil
default:
errorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode)
}
sslClientCertificates(&tlsConf, o)
sslCertificateAuthority(&tlsConf, o)
sslRenegotiation(&tlsConf)
return func(conn net.Conn) net.Conn {
client := tls.Client(conn, &tlsConf)
if verifyCaOnly {
sslVerifyCertificateAuthority(client, &tlsConf)
}
return client
}
}
// sslClientCertificates adds the certificate specified in the "sslcert" and
// "sslkey" settings, or if they aren't set, from the .postgresql directory
// in the user's home directory. The configured files must exist and have
// the correct permissions.
func sslClientCertificates(tlsConf *tls.Config, o values) {
sslkey := o.Get("sslkey")
sslcert := o.Get("sslcert")
var cinfo, kinfo os.FileInfo
var err error
if sslcert != "" && sslkey != "" {
// Check that both files exist. Note that we don't do any more extensive
// checks than this (such as checking that the paths aren't directories);
// LoadX509KeyPair() will take care of the rest.
cinfo, err = os.Stat(sslcert)
if err != nil {
panic(err)
}
kinfo, err = os.Stat(sslkey)
if err != nil {
panic(err)
}
} else {
// Automatically find certificates from ~/.postgresql
sslcert, sslkey, cinfo, kinfo = sslHomeCertificates()
if cinfo == nil || kinfo == nil {
// No certificates to load
return
}
}
// The files must also have the correct permissions
sslCertificatePermissions(cinfo, kinfo)
cert, err := tls.LoadX509KeyPair(sslcert, sslkey)
if err != nil {
panic(err)
}
tlsConf.Certificates = []tls.Certificate{cert}
}
// sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting.
func sslCertificateAuthority(tlsConf *tls.Config, o values) {
if sslrootcert := o.Get("sslrootcert"); sslrootcert != "" {
tlsConf.RootCAs = x509.NewCertPool()
cert, err := ioutil.ReadFile(sslrootcert)
if err != nil {
panic(err)
}
ok := tlsConf.RootCAs.AppendCertsFromPEM(cert)
if !ok {
errorf("couldn't parse pem in sslrootcert")
}
}
}
// sslHomeCertificates returns the path and stats of certificates in the current
// user's home directory.
func sslHomeCertificates() (cert, key string, cinfo, kinfo os.FileInfo) {
user, err := user.Current()
if err != nil {
// user.Current() might fail when cross-compiling. We have to ignore the
// error and continue without client certificates, since we wouldn't know
// from where to load them.
return
}
cert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt")
key = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key")
cinfo, err = os.Stat(cert)
if err != nil {
cinfo = nil
}
kinfo, err = os.Stat(key)
if err != nil {
kinfo = nil
}
return
}
// sslVerifyCertificateAuthority carries out a TLS handshake to the server and
// verifies the presented certificate against the CA, i.e. the one specified in
// sslrootcert or the system CA if sslrootcert was not specified.
func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) {
err := client.Handshake()
if err != nil {
panic(err)
}
certs := client.ConnectionState().PeerCertificates
opts := x509.VerifyOptions{
DNSName: client.ConnectionState().ServerName,
Intermediates: x509.NewCertPool(),
Roots: tlsConf.RootCAs,
}
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
if err != nil {
panic(err)
}
}

14
vendor/github.com/lib/pq/ssl_go1.7.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,14 @@
// +build go1.7
package pq
import "crypto/tls"
// Accept renegotiation requests initiated by the backend.
//
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
// the default configuration of older versions has it enabled. Redshift
// also initiates renegotiations and cannot be reconfigured.
func sslRenegotiation(conf *tls.Config) {
conf.Renegotiation = tls.RenegotiateFreelyAsClient
}

16
vendor/github.com/lib/pq/ssl_permissions.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,16 @@
// +build !windows
package pq
import "os"
// sslCertificatePermissions checks the permissions on user-supplied certificate
// files. The key file should have very little access.
//
// libpq does not check key file permissions on Windows.
func sslCertificatePermissions(cert, key os.FileInfo) {
kmode := key.Mode()
if kmode != kmode&0600 {
panic(ErrSSLKeyHasWorldPermissions)
}
}

8
vendor/github.com/lib/pq/ssl_renegotiation.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,8 @@
// +build !go1.7
package pq
import "crypto/tls"
// Renegotiation is not supported by crypto/tls until Go 1.7.
func sslRenegotiation(*tls.Config) {}

9
vendor/github.com/lib/pq/ssl_windows.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,9 @@
// +build windows
package pq
import "os"
// sslCertificatePermissions checks the permissions on user-supplied certificate
// files. In libpq, this is a no-op on Windows.
func sslCertificatePermissions(cert, key os.FileInfo) {}

23
vendor/github.com/lib/pq/uuid.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,23 @@
package pq
import (
"encoding/hex"
"fmt"
)
// decodeUUIDBinary interprets the binary format of a uuid, returning it in text format.
func decodeUUIDBinary(src []byte) ([]byte, error) {
if len(src) != 16 {
return nil, fmt.Errorf("pq: unable to decode uuid; bad length: %d", len(src))
}
dst := make([]byte, 36)
dst[8], dst[13], dst[18], dst[23] = '-', '-', '-', '-'
hex.Encode(dst[0:], src[0:4])
hex.Encode(dst[9:], src[4:6])
hex.Encode(dst[14:], src[6:8])
hex.Encode(dst[19:], src[8:10])
hex.Encode(dst[24:], src[10:16])
return dst, nil
}

46
vendor/github.com/lib/pq/uuid_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,46 @@
package pq
import (
"reflect"
"strings"
"testing"
)
func TestDecodeUUIDBinaryError(t *testing.T) {
t.Parallel()
_, err := decodeUUIDBinary([]byte{0x12, 0x34})
if err == nil {
t.Fatal("Expected error, got none")
}
if !strings.HasPrefix(err.Error(), "pq:") {
t.Errorf("Expected error to start with %q, got %q", "pq:", err.Error())
}
if !strings.Contains(err.Error(), "bad length: 2") {
t.Errorf("Expected error to contain length, got %q", err.Error())
}
}
func BenchmarkDecodeUUIDBinary(b *testing.B) {
x := []byte{0x03, 0xa3, 0x52, 0x2f, 0x89, 0x28, 0x49, 0x87, 0x84, 0xd6, 0x93, 0x7b, 0x36, 0xec, 0x27, 0x6f}
for i := 0; i < b.N; i++ {
decodeUUIDBinary(x)
}
}
func TestDecodeUUIDBackend(t *testing.T) {
db := openTestConn(t)
defer db.Close()
var s string = "a0ecc91d-a13f-4fe4-9fce-7e09777cc70a"
var scanned interface{}
err := db.QueryRow(`SELECT $1::uuid`, s).Scan(&scanned)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !reflect.DeepEqual(scanned, []byte(s)) {
t.Errorf("Expected []byte(%q), got %T(%q)", s, scanned, scanned)
}
}

8
vendor/github.com/miekg/dns/.travis.yml сгенерированный поставляемый
Просмотреть файл

@@ -1,7 +1,13 @@
language: go language: go
sudo: false sudo: false
go: go:
- 1.5
- 1.6 - 1.6
- 1.7
before_install:
# don't use the miekg/dns when testing forks
- mkdir -p $GOPATH/src/github.com/miekg
- ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true
script: script:
- go test -race -v -bench=. - go test -race -v -bench=.

5
vendor/github.com/miekg/dns/README.md сгенерированный поставляемый
Просмотреть файл

@@ -1,4 +1,5 @@
[![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns) [![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns) [![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns)
[![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns)
# Alternative (more granular) approach to a DNS library # Alternative (more granular) approach to a DNS library
@@ -12,7 +13,7 @@ can build servers and resolvers with it.
We try to keep the "master" branch as sane as possible and at the bleeding edge We try to keep the "master" branch as sane as possible and at the bleeding edge
of standards, avoiding breaking changes wherever reasonable. We support the last of standards, avoiding breaking changes wherever reasonable. We support the last
two versions of Go, currently: 1.5 and 1.6. two versions of Go, currently: 1.6 and 1.7.
# Goals # Goals

4
vendor/github.com/miekg/dns/defaults.go сгенерированный поставляемый
Просмотреть файл

@@ -102,11 +102,11 @@ func (dns *Msg) SetAxfr(z string) *Msg {
// SetTsig appends a TSIG RR to the message. // SetTsig appends a TSIG RR to the message.
// This is only a skeleton TSIG RR that is added as the last RR in the // This is only a skeleton TSIG RR that is added as the last RR in the
// additional section. The Tsig is calculated when the message is being send. // additional section. The Tsig is calculated when the message is being send.
func (dns *Msg) SetTsig(z, algo string, fudge, timesigned int64) *Msg { func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg {
t := new(TSIG) t := new(TSIG)
t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0} t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
t.Algorithm = algo t.Algorithm = algo
t.Fudge = 300 t.Fudge = fudge
t.TimeSigned = uint64(timesigned) t.TimeSigned = uint64(timesigned)
t.OrigId = dns.Id t.OrigId = dns.Id
dns.Extra = append(dns.Extra, t) dns.Extra = append(dns.Extra, t)

2
vendor/github.com/miekg/dns/edns.go сгенерированный поставляемый
Просмотреть файл

@@ -197,7 +197,7 @@ func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
// e := new(dns.EDNS0_SUBNET) // e := new(dns.EDNS0_SUBNET)
// e.Code = dns.EDNS0SUBNET // e.Code = dns.EDNS0SUBNET
// e.Family = 1 // 1 for IPv4 source address, 2 for IPv6 // e.Family = 1 // 1 for IPv4 source address, 2 for IPv6
// e.NetMask = 32 // 32 for IPV4, 128 for IPv6 // e.SourceNetMask = 32 // 32 for IPV4, 128 for IPv6
// e.SourceScope = 0 // e.SourceScope = 0
// e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4 // e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4
// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6 // // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6

29
vendor/github.com/miekg/dns/msg.go сгенерированный поставляемый
Просмотреть файл

@@ -203,12 +203,6 @@ func packDomainName(s string, msg []byte, off int, compression map[string]int, c
bs[j] = bs[j+2] bs[j] = bs[j+2]
} }
ls -= 2 ls -= 2
} else if bs[i] == 't' {
bs[i] = '\t'
} else if bs[i] == 'r' {
bs[i] = '\r'
} else if bs[i] == 'n' {
bs[i] = '\n'
} }
escapedDot = bs[i] == '.' escapedDot = bs[i] == '.'
bsFresh = false bsFresh = false
@@ -335,10 +329,6 @@ Loop:
fallthrough fallthrough
case '"', '\\': case '"', '\\':
s = append(s, '\\', b) s = append(s, '\\', b)
case '\t':
s = append(s, '\\', 't')
case '\r':
s = append(s, '\\', 'r')
default: default:
if b < 32 || b >= 127 { // unprintable use \DDD if b < 32 || b >= 127 { // unprintable use \DDD
var buf [3]byte var buf [3]byte
@@ -431,12 +421,6 @@ func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) {
if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
msg[offset] = dddToByte(bs[i:]) msg[offset] = dddToByte(bs[i:])
i += 2 i += 2
} else if bs[i] == 't' {
msg[offset] = '\t'
} else if bs[i] == 'r' {
msg[offset] = '\r'
} else if bs[i] == 'n' {
msg[offset] = '\n'
} else { } else {
msg[offset] = bs[i] msg[offset] = bs[i]
} }
@@ -508,12 +492,6 @@ func unpackTxtString(msg []byte, offset int) (string, int, error) {
switch b { switch b {
case '"', '\\': case '"', '\\':
s = append(s, '\\', b) s = append(s, '\\', b)
case '\t':
s = append(s, `\t`...)
case '\r':
s = append(s, `\r`...)
case '\n':
s = append(s, `\n`...)
default: default:
if b < 32 || b > 127 { // unprintable if b < 32 || b > 127 { // unprintable
var buf [3]byte var buf [3]byte
@@ -781,9 +759,6 @@ func (dns *Msg) Unpack(msg []byte) (err error) {
if dh, off, err = unpackMsgHdr(msg, off); err != nil { if dh, off, err = unpackMsgHdr(msg, off); err != nil {
return err return err
} }
if off == len(msg) {
return ErrTruncated
}
dns.Id = dh.Id dns.Id = dh.Id
dns.Response = (dh.Bits & _QR) != 0 dns.Response = (dh.Bits & _QR) != 0
@@ -797,6 +772,10 @@ func (dns *Msg) Unpack(msg []byte) (err error) {
dns.CheckingDisabled = (dh.Bits & _CD) != 0 dns.CheckingDisabled = (dh.Bits & _CD) != 0
dns.Rcode = int(dh.Bits & 0xF) dns.Rcode = int(dh.Bits & 0xF)
if off == len(msg) {
return ErrTruncated
}
// Optimistically use the count given to us in the header // Optimistically use the count given to us in the header
dns.Question = make([]Question, 0, int(dh.Qdcount)) dns.Question = make([]Question, 0, int(dh.Qdcount))

4
vendor/github.com/miekg/dns/msg_generate.go сгенерированный поставляемый
Просмотреть файл

@@ -117,9 +117,9 @@ return off, err
switch { switch {
case st.Tag(i) == `dns:"-"`: // ignored case st.Tag(i) == `dns:"-"`: // ignored
case st.Tag(i) == `dns:"cdomain-name"`: case st.Tag(i) == `dns:"cdomain-name"`:
fallthrough
case st.Tag(i) == `dns:"domain-name"`:
o("off, err = PackDomainName(rr.%s, msg, off, compression, compress)\n") o("off, err = PackDomainName(rr.%s, msg, off, compression, compress)\n")
case st.Tag(i) == `dns:"domain-name"`:
o("off, err = PackDomainName(rr.%s, msg, off, compression, false)\n")
case st.Tag(i) == `dns:"a"`: case st.Tag(i) == `dns:"a"`:
o("off, err = packDataA(rr.%s, msg, off)\n") o("off, err = packDataA(rr.%s, msg, off)\n")
case st.Tag(i) == `dns:"aaaa"`: case st.Tag(i) == `dns:"aaaa"`:

2
vendor/github.com/miekg/dns/msg_helpers.go сгенерированный поставляемый
Просмотреть файл

@@ -263,8 +263,6 @@ func unpackString(msg []byte, off int) (string, int, error) {
switch b { switch b {
case '"', '\\': case '"', '\\':
s = append(s, '\\', b) s = append(s, '\\', b)
case '\t', '\r', '\n':
s = append(s, b)
default: default:
if b < 32 || b > 127 { // unprintable if b < 32 || b > 127 { // unprintable
var buf [3]byte var buf [3]byte

19
vendor/github.com/miekg/dns/parse_test.go сгенерированный поставляемый
Просмотреть файл

@@ -86,7 +86,7 @@ func TestDomainName(t *testing.T) {
} }
func TestDomainNameAndTXTEscapes(t *testing.T) { func TestDomainNameAndTXTEscapes(t *testing.T) {
tests := []byte{'.', '(', ')', ';', ' ', '@', '"', '\\', '\t', '\r', '\n', 0, 255} tests := []byte{'.', '(', ')', ';', ' ', '@', '"', '\\', 9, 13, 10, 0, 255}
for _, b := range tests { for _, b := range tests {
rrbytes := []byte{ rrbytes := []byte{
1, b, 0, // owner 1, b, 0, // owner
@@ -127,8 +127,8 @@ func TestTXTEscapeParsing(t *testing.T) {
test := [][]string{ test := [][]string{
{`";"`, `";"`}, {`";"`, `";"`},
{`\;`, `";"`}, {`\;`, `";"`},
{`"\t"`, `"\t"`}, {`"\t"`, `"t"`},
{`"\r"`, `"\r"`}, {`"\r"`, `"r"`},
{`"\ "`, `" "`}, {`"\ "`, `" "`},
{`"\;"`, `";"`}, {`"\;"`, `";"`},
{`"\;\""`, `";\""`}, {`"\;\""`, `";\""`},
@@ -137,8 +137,9 @@ func TestTXTEscapeParsing(t *testing.T) {
{`"(a\)"`, `"(a)"`}, {`"(a\)"`, `"(a)"`},
{`"(a)"`, `"(a)"`}, {`"(a)"`, `"(a)"`},
{`"\048"`, `"0"`}, {`"\048"`, `"0"`},
{`"\` + "\n" + `"`, `"\n"`}, {`"\` + "\t" + `"`, `"\009"`},
{`"\` + "\r" + `"`, `"\r"`}, {`"\` + "\n" + `"`, `"\010"`},
{`"\` + "\r" + `"`, `"\013"`},
{`"\` + "\x11" + `"`, `"\017"`}, {`"\` + "\x11" + `"`, `"\017"`},
{`"\'"`, `"'"`}, {`"\'"`, `"'"`},
} }
@@ -417,16 +418,16 @@ func TestQuotes(t *testing.T) {
tests := map[string]string{ tests := map[string]string{
`t.example.com. IN TXT "a bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a bc\"", `t.example.com. IN TXT "a bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a bc\"",
`t.example.com. IN TXT "a `t.example.com. IN TXT "a
bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a\\n bc\"", bc"`: "t.example.com.\t3600\tIN\tTXT\t\"a\\010 bc\"",
`t.example.com. IN TXT ""`: "t.example.com.\t3600\tIN\tTXT\t\"\"", `t.example.com. IN TXT ""`: "t.example.com.\t3600\tIN\tTXT\t\"\"",
`t.example.com. IN TXT "a"`: "t.example.com.\t3600\tIN\tTXT\t\"a\"", `t.example.com. IN TXT "a"`: "t.example.com.\t3600\tIN\tTXT\t\"a\"",
`t.example.com. IN TXT "aa"`: "t.example.com.\t3600\tIN\tTXT\t\"aa\"", `t.example.com. IN TXT "aa"`: "t.example.com.\t3600\tIN\tTXT\t\"aa\"",
`t.example.com. IN TXT "aaa" ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"", `t.example.com. IN TXT "aaa" ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
`t.example.com. IN TXT "abc" "DEF"`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"", `t.example.com. IN TXT "abc" "DEF"`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"",
`t.example.com. IN TXT "abc" ( "DEF" )`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"", `t.example.com. IN TXT "abc" ( "DEF" )`: "t.example.com.\t3600\tIN\tTXT\t\"abc\" \"DEF\"",
`t.example.com. IN TXT aaa ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa \"", `t.example.com. IN TXT aaa ;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
`t.example.com. IN TXT aaa aaa;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa aaa\"", `t.example.com. IN TXT aaa aaa;`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\" \"aaa\"",
`t.example.com. IN TXT aaa aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa aaa\"", `t.example.com. IN TXT aaa aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\" \"aaa\"",
`t.example.com. IN TXT aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"", `t.example.com. IN TXT aaa`: "t.example.com.\t3600\tIN\tTXT\t\"aaa\"",
"cid.urn.arpa. NAPTR 100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.", "cid.urn.arpa. NAPTR 100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"z3950+I2L+I2C\" \"\" _z3950._tcp.gatech.edu.",
"cid.urn.arpa. NAPTR 100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.", "cid.urn.arpa. NAPTR 100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.": "cid.urn.arpa.\t3600\tIN\tNAPTR\t100 50 \"s\" \"rcds+I2C\" \"\" _rcds._udp.gatech.edu.",

2
vendor/github.com/miekg/dns/sanitize_test.go сгенерированный поставляемый
Просмотреть файл

@@ -65,7 +65,7 @@ func TestNormalizedString(t *testing.T) {
tests := map[RR]string{ tests := map[RR]string{
newRR(t, "mIEk.Nl. 3600 IN A 127.0.0.1"): "miek.nl.\tIN\tA\t127.0.0.1", newRR(t, "mIEk.Nl. 3600 IN A 127.0.0.1"): "miek.nl.\tIN\tA\t127.0.0.1",
newRR(t, "m\\ iek.nL. 3600 IN A 127.0.0.1"): "m\\ iek.nl.\tIN\tA\t127.0.0.1", newRR(t, "m\\ iek.nL. 3600 IN A 127.0.0.1"): "m\\ iek.nl.\tIN\tA\t127.0.0.1",
newRR(t, "m\\\tIeK.nl. 3600 in A 127.0.0.1"): "m\\tiek.nl.\tIN\tA\t127.0.0.1", newRR(t, "m\\\tIeK.nl. 3600 in A 127.0.0.1"): "m\\009iek.nl.\tIN\tA\t127.0.0.1",
} }
for tc, expected := range tests { for tc, expected := range tests {
n := normalizedString(tc) n := normalizedString(tc)

112
vendor/github.com/miekg/dns/scan_rr.go сгенерированный поставляемый
Просмотреть файл

@@ -64,74 +64,63 @@ func endingToString(c chan lex, errstr, f string) (string, *ParseError, string)
return s, nil, l.comment return s, nil, l.comment
} }
// A remainder of the rdata with embedded spaces, return the parsed string slice (sans the spaces) // A remainder of the rdata with embedded spaces, split on unquoted whitespace
// or an error // and return the parsed string slice or an error
func endingToTxtSlice(c chan lex, errstr, f string) ([]string, *ParseError, string) { func endingToTxtSlice(c chan lex, errstr, f string) ([]string, *ParseError, string) {
// Get the remaining data until we see a zNewline // Get the remaining data until we see a zNewline
quote := false
l := <-c l := <-c
var s []string
if l.err { if l.err {
return s, &ParseError{f, errstr, l}, "" return nil, &ParseError{f, errstr, l}, ""
} }
switch l.value == zQuote {
case true: // A number of quoted string
s = make([]string, 0)
empty := true
for l.value != zNewline && l.value != zEOF {
if l.err {
return nil, &ParseError{f, errstr, l}, ""
}
switch l.value {
case zString:
empty = false
if len(l.token) > 255 {
// split up tokens that are larger than 255 into 255-chunks
sx := []string{}
p, i := 0, 255
for {
if i <= len(l.token) {
sx = append(sx, l.token[p:i])
} else {
sx = append(sx, l.token[p:])
break
} // Build the slice
p, i = p+255, i+255 s := make([]string, 0)
} quote := false
s = append(s, sx...) empty := false
break for l.value != zNewline && l.value != zEOF {
} if l.err {
s = append(s, l.token)
case zBlank:
if quote {
// zBlank can only be seen in between txt parts.
return nil, &ParseError{f, errstr, l}, ""
}
case zQuote:
if empty && quote {
s = append(s, "")
}
quote = !quote
empty = true
default:
return nil, &ParseError{f, errstr, l}, ""
}
l = <-c
}
if quote {
return nil, &ParseError{f, errstr, l}, "" return nil, &ParseError{f, errstr, l}, ""
} }
case false: // Unquoted text record switch l.value {
s = make([]string, 1) case zString:
for l.value != zNewline && l.value != zEOF { empty = false
if l.err { if len(l.token) > 255 {
return s, &ParseError{f, errstr, l}, "" // split up tokens that are larger than 255 into 255-chunks
sx := []string{}
p, i := 0, 255
for {
if i <= len(l.token) {
sx = append(sx, l.token[p:i])
} else {
sx = append(sx, l.token[p:])
break
}
p, i = p+255, i+255
}
s = append(s, sx...)
break
} }
s[0] += l.token
l = <-c s = append(s, l.token)
case zBlank:
if quote {
// zBlank can only be seen in between txt parts.
return nil, &ParseError{f, errstr, l}, ""
}
case zQuote:
if empty && quote {
s = append(s, "")
}
quote = !quote
empty = true
default:
return nil, &ParseError{f, errstr, l}, ""
} }
l = <-c
}
if quote {
return nil, &ParseError{f, errstr, l}, ""
} }
return s, nil, l.comment return s, nil, l.comment
} }
@@ -2027,9 +2016,12 @@ func setUINFO(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
rr.Hdr = h rr.Hdr = h
s, e, c1 := endingToTxtSlice(c, "bad UINFO Uinfo", f) s, e, c1 := endingToTxtSlice(c, "bad UINFO Uinfo", f)
if e != nil { if e != nil {
return nil, e, "" return nil, e, c1
} }
rr.Uinfo = s[0] // silently discard anything above if ln := len(s); ln == 0 {
return rr, nil, c1
}
rr.Uinfo = s[0] // silently discard anything after the first character-string
return rr, nil, c1 return rr, nil, c1
} }

2
vendor/github.com/miekg/dns/server.go сгенерированный поставляемый
Просмотреть файл

@@ -339,7 +339,7 @@ func (srv *Server) ListenAndServe() error {
network := "tcp" network := "tcp"
if srv.Net == "tcp4-tls" { if srv.Net == "tcp4-tls" {
network = "tcp4" network = "tcp4"
} else if srv.Net == "tcp6" { } else if srv.Net == "tcp6-tls" {
network = "tcp6" network = "tcp6"
} }

40
vendor/github.com/miekg/dns/server_test.go сгенерированный поставляемый
Просмотреть файл

@@ -677,3 +677,43 @@ zDCJkckCgYEAndqM5KXGk5xYo+MAA1paZcbTUXwaWwjLU+XSRSSoyBEi5xMtfvUb
kFsxKCqxAnBVGEWAvVZAiiTOxleQFjz5RnL0BQp9Lg2cQe+dvuUmIAA= kFsxKCqxAnBVGEWAvVZAiiTOxleQFjz5RnL0BQp9Lg2cQe+dvuUmIAA=
-----END RSA PRIVATE KEY-----`) -----END RSA PRIVATE KEY-----`)
) )
func testShutdownBindPort(t *testing.T, protocol string, port string) {
handler := NewServeMux()
handler.HandleFunc(".", func(w ResponseWriter, r *Msg) {})
startedCh := make(chan struct{})
s := &Server{
Addr: net.JoinHostPort("127.0.0.1", port),
Net: protocol,
Handler: handler,
NotifyStartedFunc: func() {
startedCh <- struct{}{}
},
}
go func() {
if err := s.ListenAndServe(); err != nil {
t.Log(err)
}
}()
<-startedCh
t.Logf("DNS server is started on: %s", s.Addr)
if err := s.Shutdown(); err != nil {
t.Fatal(err)
}
time.Sleep(100 * time.Millisecond)
go func() {
if err := s.ListenAndServe(); err != nil {
t.Fatal(err)
}
}()
<-startedCh
t.Logf("DNS server is started on: %s", s.Addr)
}
func TestShutdownBindPortUDP(t *testing.T) {
testShutdownBindPort(t, "udp", "1153")
}
func TestShutdownBindPortTCP(t *testing.T) {
testShutdownBindPort(t, "tcp", "1154")
}

19
vendor/github.com/miekg/dns/types.go сгенерированный поставляемый
Просмотреть файл

@@ -480,12 +480,6 @@ func appendDomainNameByte(s []byte, b byte) []byte {
func appendTXTStringByte(s []byte, b byte) []byte { func appendTXTStringByte(s []byte, b byte) []byte {
switch b { switch b {
case '\t':
return append(s, '\\', 't')
case '\r':
return append(s, '\\', 'r')
case '\n':
return append(s, '\\', 'n')
case '"', '\\': case '"', '\\':
return append(s, '\\', b) return append(s, '\\', b)
} }
@@ -525,17 +519,8 @@ func nextByte(b []byte, offset int) (byte, int) {
return dddToByte(b[offset+1:]), 4 return dddToByte(b[offset+1:]), 4
} }
} }
// not \ddd, maybe a control char // not \ddd, just an RFC 1035 "quoted" character
switch b[offset+1] { return b[offset+1], 2
case 't':
return '\t', 2
case 'r':
return '\r', 2
case 'n':
return '\n', 2
default:
return b[offset+1], 2
}
} }
type SPF struct { type SPF struct {

9
vendor/github.com/miekg/dns/udp_linux.go сгенерированный поставляемый
Просмотреть файл

@@ -22,14 +22,17 @@ func setUDPSocketOptions4(conn *net.UDPConn) error {
return err return err
} }
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil { if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil {
file.Close()
return err return err
} }
// Calling File() above results in the connection becoming blocking, we must fix that. // Calling File() above results in the connection becoming blocking, we must fix that.
// See https://github.com/miekg/dns/issues/279 // See https://github.com/miekg/dns/issues/279
err = syscall.SetNonblock(int(file.Fd()), true) err = syscall.SetNonblock(int(file.Fd()), true)
if err != nil { if err != nil {
file.Close()
return err return err
} }
file.Close()
return nil return nil
} }
@@ -40,12 +43,15 @@ func setUDPSocketOptions6(conn *net.UDPConn) error {
return err return err
} }
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil { if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil {
file.Close()
return err return err
} }
err = syscall.SetNonblock(int(file.Fd()), true) err = syscall.SetNonblock(int(file.Fd()), true)
if err != nil { if err != nil {
file.Close()
return err return err
} }
file.Close()
return nil return nil
} }
@@ -59,8 +65,10 @@ func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) {
// dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections // dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections
v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY) v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY)
if err != nil { if err != nil {
file.Close()
return false, err return false, err
} }
file.Close()
return v6only == 1, nil return v6only == 1, nil
} }
@@ -69,5 +77,6 @@ func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer file.Close()
return syscall.Getsockname(int(file.Fd())) return syscall.Getsockname(int(file.Fd()))
} }

34
vendor/github.com/miekg/dns/zmsg.go сгенерированный поставляемый
Просмотреть файл

@@ -221,7 +221,7 @@ func (rr *DNAME) pack(msg []byte, off int, compression map[string]int, compress
return off, err return off, err
} }
headerEnd := off headerEnd := off
off, err = PackDomainName(rr.Target, msg, off, compression, compress) off, err = PackDomainName(rr.Target, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -447,7 +447,7 @@ func (rr *KX) pack(msg []byte, off int, compression map[string]int, compress boo
if err != nil { if err != nil {
return off, err return off, err
} }
off, err = PackDomainName(rr.Exchanger, msg, off, compression, compress) off, err = PackDomainName(rr.Exchanger, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -539,7 +539,7 @@ func (rr *LP) pack(msg []byte, off int, compression map[string]int, compress boo
if err != nil { if err != nil {
return off, err return off, err
} }
off, err = PackDomainName(rr.Fqdn, msg, off, compression, compress) off, err = PackDomainName(rr.Fqdn, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -679,7 +679,7 @@ func (rr *NAPTR) pack(msg []byte, off int, compression map[string]int, compress
if err != nil { if err != nil {
return off, err return off, err
} }
off, err = PackDomainName(rr.Replacement, msg, off, compression, compress) off, err = PackDomainName(rr.Replacement, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -753,7 +753,7 @@ func (rr *NSAPPTR) pack(msg []byte, off int, compression map[string]int, compres
return off, err return off, err
} }
headerEnd := off headerEnd := off
off, err = PackDomainName(rr.Ptr, msg, off, compression, compress) off, err = PackDomainName(rr.Ptr, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -767,7 +767,7 @@ func (rr *NSEC) pack(msg []byte, off int, compression map[string]int, compress b
return off, err return off, err
} }
headerEnd := off headerEnd := off
off, err = PackDomainName(rr.NextDomain, msg, off, compression, compress) off, err = PackDomainName(rr.NextDomain, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -905,11 +905,11 @@ func (rr *PX) pack(msg []byte, off int, compression map[string]int, compress boo
if err != nil { if err != nil {
return off, err return off, err
} }
off, err = PackDomainName(rr.Map822, msg, off, compression, compress) off, err = PackDomainName(rr.Map822, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
off, err = PackDomainName(rr.Mapx400, msg, off, compression, compress) off, err = PackDomainName(rr.Mapx400, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -963,11 +963,11 @@ func (rr *RP) pack(msg []byte, off int, compression map[string]int, compress boo
return off, err return off, err
} }
headerEnd := off headerEnd := off
off, err = PackDomainName(rr.Mbox, msg, off, compression, compress) off, err = PackDomainName(rr.Mbox, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
off, err = PackDomainName(rr.Txt, msg, off, compression, compress) off, err = PackDomainName(rr.Txt, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -1009,7 +1009,7 @@ func (rr *RRSIG) pack(msg []byte, off int, compression map[string]int, compress
if err != nil { if err != nil {
return off, err return off, err
} }
off, err = PackDomainName(rr.SignerName, msg, off, compression, compress) off, err = PackDomainName(rr.SignerName, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -1073,7 +1073,7 @@ func (rr *SIG) pack(msg []byte, off int, compression map[string]int, compress bo
if err != nil { if err != nil {
return off, err return off, err
} }
off, err = PackDomainName(rr.SignerName, msg, off, compression, compress) off, err = PackDomainName(rr.SignerName, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -1181,7 +1181,7 @@ func (rr *SRV) pack(msg []byte, off int, compression map[string]int, compress bo
if err != nil { if err != nil {
return off, err return off, err
} }
off, err = PackDomainName(rr.Target, msg, off, compression, compress) off, err = PackDomainName(rr.Target, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -1243,11 +1243,11 @@ func (rr *TALINK) pack(msg []byte, off int, compression map[string]int, compress
return off, err return off, err
} }
headerEnd := off headerEnd := off
off, err = PackDomainName(rr.PreviousName, msg, off, compression, compress) off, err = PackDomainName(rr.PreviousName, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
off, err = PackDomainName(rr.NextName, msg, off, compression, compress) off, err = PackDomainName(rr.NextName, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -1261,7 +1261,7 @@ func (rr *TKEY) pack(msg []byte, off int, compression map[string]int, compress b
return off, err return off, err
} }
headerEnd := off headerEnd := off
off, err = PackDomainName(rr.Algorithm, msg, off, compression, compress) off, err = PackDomainName(rr.Algorithm, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }
@@ -1333,7 +1333,7 @@ func (rr *TSIG) pack(msg []byte, off int, compression map[string]int, compress b
return off, err return off, err
} }
headerEnd := off headerEnd := off
off, err = PackDomainName(rr.Algorithm, msg, off, compression, compress) off, err = PackDomainName(rr.Algorithm, msg, off, compression, false)
if err != nil { if err != nil {
return off, err return off, err
} }

6
vendor/github.com/minio/minio-go/.travis.yml сгенерированный поставляемый
Просмотреть файл

@@ -3,7 +3,6 @@ language: go
os: os:
- linux - linux
- osx
env: env:
- ARCH=x86_64 - ARCH=x86_64
@@ -12,12 +11,9 @@ env:
go: go:
- 1.5.3 - 1.5.3
- 1.6 - 1.6
- 1.7.4
script: script:
- diff -au <(gofmt -d .) <(printf "") - diff -au <(gofmt -d .) <(printf "")
- go vet ./... - go vet ./...
- go test -short -race -v ./... - go test -short -race -v ./...
notifications:
slack:
secure: HrOX2k6F/sEl6Rr4m5vHOdJCIwV42be0kz1Jy/WSMvrl/fQ8YkldKviLeWh4aWt1kclsYhNQ4FqGML+RIZYsdOqej4fAw9Vi5pZkI1MzPJq0UjrtMqkqzvD90eDGQYCKwaXjEIN8cohwJeb6X0B0HKAd9sqJW5GH5SwnhH5WWP8=

28
vendor/github.com/minio/minio-go/README.md сгенерированный поставляемый
Просмотреть файл

@@ -1,5 +1,5 @@
# Minio Golang Library for Amazon S3 Compatible Cloud Storage [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Minio/minio?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # Minio Go Client SDK for Amazon S3 Compatible Cloud Storage [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Minio/minio?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
The Minio Golang Client SDK provides simple APIs to access any Amazon S3 compatible object storage server. The Minio Go Client SDK provides simple APIs to access any Amazon S3 compatible object storage.
**Supported cloud storage providers:** **Supported cloud storage providers:**
@@ -14,22 +14,21 @@ The Minio Golang Client SDK provides simple APIs to access any Amazon S3 compati
- Ceph Object Gateway - Ceph Object Gateway
- Riak CS - Riak CS
This quickstart guide will show you how to install the Minio client SDK, connect to Minio, and provide a walkthrough of a simple file uploader. For a complete list of APIs and examples, please take a look at the [Golang Client API Reference](https://docs.minio.io/docs/golang-client-api-reference). This quickstart guide will show you how to install the Minio client SDK, connect to Minio, and provide a walkthrough for a simple file uploader. For a complete list of APIs and examples, please take a look at the [Go Client API Reference](https://docs.minio.io/docs/golang-client-api-reference).
This document assumes that you have a working [Golang setup](https://docs.minio.io/docs/how-to-install-golang). This document assumes that you have a working [Go development environment](https://docs.minio.io/docs/how-to-install-golang).
## Download from Github ## Download from Github
```sh ```sh
$ go get -u github.com/minio/minio-go go get -u github.com/minio/minio-go
``` ```
## Initialize Minio Client ## Initialize Minio Client
You need four items to connect to Minio object storage server. Minio client requires the following four parameters specified to connect to an Amazon S3 compatible object storage.
| Parameter | Description| | Parameter | Description|
@@ -68,7 +67,7 @@ func main() {
## Quick Start Example - File Uploader ## Quick Start Example - File Uploader
This example program connects to an object storage server, makes a bucket on the server and then uploads a file to the bucket. This example program connects to an object storage server, creates a bucket and uploads a file to the bucket.
@@ -132,11 +131,11 @@ func main() {
```sh ```sh
$ go run file-uploader.go go run file-uploader.go
2016/08/13 17:03:28 Successfully created mymusic 2016/08/13 17:03:28 Successfully created mymusic
2016/08/13 17:03:40 Successfully uploaded golden-oldies.zip of size 16253413 2016/08/13 17:03:40 Successfully uploaded golden-oldies.zip of size 16253413
$ mc ls play/mymusic/ mc ls play/mymusic/
[2016-05-27 16:02:16 PDT] 17MiB golden-oldies.zip [2016-05-27 16:02:16 PDT] 17MiB golden-oldies.zip
``` ```
@@ -191,6 +190,13 @@ The full API Reference is available here.
* [`PresignedPutObject`](https://docs.minio.io/docs/golang-client-api-reference#PresignedPutObject) * [`PresignedPutObject`](https://docs.minio.io/docs/golang-client-api-reference#PresignedPutObject)
* [`PresignedPostPolicy`](https://docs.minio.io/docs/golang-client-api-reference#PresignedPostPolicy) * [`PresignedPostPolicy`](https://docs.minio.io/docs/golang-client-api-reference#PresignedPostPolicy)
### API Reference : Client custom settings
* [`SetAppInfo`](http://docs.minio.io/docs/golang-client-api-reference#SetAppInfo)
* [`SetCustomTransport`](http://docs.minio.io/docs/golang-client-api-reference#SetCustomTransport)
* [`TraceOn`](http://docs.minio.io/docs/golang-client-api-reference#TraceOn)
* [`TraceOff`](http://docs.minio.io/docs/golang-client-api-reference#TraceOff)
## Full Examples ## Full Examples
#### Full Examples : Bucket Operations #### Full Examples : Bucket Operations
@@ -238,7 +244,7 @@ The full API Reference is available here.
## Explore Further ## Explore Further
* [Complete Documentation](https://docs.minio.io) * [Complete Documentation](https://docs.minio.io)
* [Minio Golang Client SDK API Reference](https://docs.minio.io/docs/golang-client-api-reference) * [Minio Go Client SDK API Reference](https://docs.minio.io/docs/golang-client-api-reference)
* [Go Music Player App- Full Application Example ](https://docs.minio.io/docs/go-music-player-app) * [Go Music Player App- Full Application Example ](https://docs.minio.io/docs/go-music-player-app)
## Contribute ## Contribute

9
vendor/github.com/minio/minio-go/api-datatypes.go сгенерированный поставляемый
Просмотреть файл

@@ -16,7 +16,10 @@
package minio package minio
import "time" import (
"net/http"
"time"
)
// BucketInfo container for bucket metadata. // BucketInfo container for bucket metadata.
type BucketInfo struct { type BucketInfo struct {
@@ -38,6 +41,10 @@ type ObjectInfo struct {
Size int64 `json:"size"` // Size in bytes of the object. Size int64 `json:"size"` // Size in bytes of the object.
ContentType string `json:"contentType"` // A standard MIME type describing the format of the object data. ContentType string `json:"contentType"` // A standard MIME type describing the format of the object data.
// Collection of additional metadata on the object.
// eg: x-amz-meta-*, content-encoding etc.
Metadata http.Header `json:"metadata"`
// Owner name. // Owner name.
Owner struct { Owner struct {
DisplayName string `json:"name"` DisplayName string `json:"name"`

4
vendor/github.com/minio/minio-go/api-get-object.go сгенерированный поставляемый
Просмотреть файл

@@ -275,7 +275,7 @@ func (o *Object) setOffset(bytesRead int64) error {
return nil return nil
} }
// Read reads up to len(p) bytes into p. It returns the number of // Read reads up to len(b) bytes into b. It returns the number of
// bytes read (0 <= n <= len(p)) and any error encountered. Returns // bytes read (0 <= n <= len(p)) and any error encountered. Returns
// io.EOF upon end of file. // io.EOF upon end of file.
func (o *Object) Read(b []byte) (n int, err error) { func (o *Object) Read(b []byte) (n int, err error) {
@@ -343,7 +343,7 @@ func (o *Object) Read(b []byte) (n int, err error) {
return response.Size, err return response.Size, err
} }
// Stat returns the ObjectInfo structure describing object. // Stat returns the ObjectInfo structure describing Object.
func (o *Object) Stat() (ObjectInfo, error) { func (o *Object) Stat() (ObjectInfo, error) {
if o == nil { if o == nil {
return ObjectInfo{}, ErrInvalidArgument("Object is nil") return ObjectInfo{}, ErrInvalidArgument("Object is nil")

4
vendor/github.com/minio/minio-go/api-get-policy.go сгенерированный поставляемый
Просмотреть файл

@@ -41,7 +41,7 @@ func (c Client) GetBucketPolicy(bucketName, objectPrefix string) (bucketPolicy p
return policy.GetPolicy(policyInfo.Statements, bucketName, objectPrefix), nil return policy.GetPolicy(policyInfo.Statements, bucketName, objectPrefix), nil
} }
// GetBucketPolicy - get bucket policy rules at a given path. // ListBucketPolicies - list all policies for a given prefix and all its children.
func (c Client) ListBucketPolicies(bucketName, objectPrefix string) (bucketPolicies map[string]policy.BucketPolicy, err error) { func (c Client) ListBucketPolicies(bucketName, objectPrefix string) (bucketPolicies map[string]policy.BucketPolicy, err error) {
// Input validation. // Input validation.
if err := isValidBucketName(bucketName); err != nil { if err := isValidBucketName(bucketName); err != nil {
@@ -57,7 +57,7 @@ func (c Client) ListBucketPolicies(bucketName, objectPrefix string) (bucketPolic
return policy.GetPolicies(policyInfo.Statements, bucketName), nil return policy.GetPolicies(policyInfo.Statements, bucketName), nil
} }
// Request server for policy. // Request server for current bucket policy.
func (c Client) getBucketPolicy(bucketName string, objectPrefix string) (policy.BucketAccessPolicy, error) { func (c Client) getBucketPolicy(bucketName string, objectPrefix string) (policy.BucketAccessPolicy, error) {
// Get resources properly escaped and lined up before // Get resources properly escaped and lined up before
// using them in http request. // using them in http request.

1
vendor/github.com/minio/minio-go/api-list.go сгенерированный поставляемый
Просмотреть файл

@@ -482,6 +482,7 @@ func (c Client) listIncompleteUploads(bucketName, objectPrefix string, recursive
objectMultipartStatCh <- ObjectMultipartInfo{ objectMultipartStatCh <- ObjectMultipartInfo{
Err: err, Err: err,
} }
continue
} }
} }
select { select {

29
vendor/github.com/minio/minio-go/api-notification.go сгенерированный поставляемый
Просмотреть файл

@@ -22,6 +22,9 @@ import (
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"time"
"github.com/minio/minio-go/pkg/s3utils"
) )
// GetBucketNotification - get bucket notification at a given path. // GetBucketNotification - get bucket notification at a given path.
@@ -135,7 +138,7 @@ func (c Client) ListenBucketNotification(bucketName, prefix, suffix string, even
} }
// Check ARN partition to verify if listening bucket is supported // Check ARN partition to verify if listening bucket is supported
if isAmazonEndpoint(c.endpointURL) || isGoogleEndpoint(c.endpointURL) { if s3utils.IsAmazonEndpoint(c.endpointURL) || s3utils.IsGoogleEndpoint(c.endpointURL) {
notificationInfoCh <- NotificationInfo{ notificationInfoCh <- NotificationInfo{
Err: ErrAPINotSupported("Listening bucket notification is specific only to `minio` partitions"), Err: ErrAPINotSupported("Listening bucket notification is specific only to `minio` partitions"),
} }
@@ -143,7 +146,14 @@ func (c Client) ListenBucketNotification(bucketName, prefix, suffix string, even
} }
// Continously run and listen on bucket notification. // Continously run and listen on bucket notification.
for { // Create a done channel to control 'ListObjects' go routine.
retryDoneCh := make(chan struct{}, 1)
// Indicate to our routine to exit cleanly upon return.
defer close(retryDoneCh)
// Wait on the jitter retry loop.
for range c.newRetryTimerContinous(time.Second, time.Second*30, MaxJitter, retryDoneCh) {
urlValues := make(url.Values) urlValues := make(url.Values)
urlValues.Set("prefix", prefix) urlValues.Set("prefix", prefix)
urlValues.Set("suffix", suffix) urlValues.Set("suffix", suffix)
@@ -155,10 +165,7 @@ func (c Client) ListenBucketNotification(bucketName, prefix, suffix string, even
queryValues: urlValues, queryValues: urlValues,
}) })
if err != nil { if err != nil {
notificationInfoCh <- NotificationInfo{ continue
Err: err,
}
return
} }
// Validate http response, upon error return quickly. // Validate http response, upon error return quickly.
@@ -180,10 +187,7 @@ func (c Client) ListenBucketNotification(bucketName, prefix, suffix string, even
for bio.Scan() { for bio.Scan() {
var notificationInfo NotificationInfo var notificationInfo NotificationInfo
if err = json.Unmarshal(bio.Bytes(), &notificationInfo); err != nil { if err = json.Unmarshal(bio.Bytes(), &notificationInfo); err != nil {
notificationInfoCh <- NotificationInfo{ continue
Err: err,
}
return
} }
// Send notifications on channel only if there are events received. // Send notifications on channel only if there are events received.
if len(notificationInfo.Records) > 0 { if len(notificationInfo.Records) > 0 {
@@ -200,12 +204,7 @@ func (c Client) ListenBucketNotification(bucketName, prefix, suffix string, even
// and re-connect. // and re-connect.
if err == io.ErrUnexpectedEOF { if err == io.ErrUnexpectedEOF {
resp.Body.Close() resp.Body.Close()
continue
} }
notificationInfoCh <- NotificationInfo{
Err: err,
}
return
} }
} }
}(notificationInfoCh) }(notificationInfoCh)

11
vendor/github.com/minio/minio-go/api-presigned.go сгенерированный поставляемый
Просмотреть файл

@@ -20,6 +20,9 @@ import (
"errors" "errors"
"net/url" "net/url"
"time" "time"
"github.com/minio/minio-go/pkg/s3signer"
"github.com/minio/minio-go/pkg/s3utils"
) )
// supportedGetReqParams - supported request parameters for GET presigned request. // supportedGetReqParams - supported request parameters for GET presigned request.
@@ -126,14 +129,14 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (u *url.URL, formData map[str
policyBase64 := p.base64() policyBase64 := p.base64()
p.formData["policy"] = policyBase64 p.formData["policy"] = policyBase64
// For Google endpoint set this value to be 'GoogleAccessId'. // For Google endpoint set this value to be 'GoogleAccessId'.
if isGoogleEndpoint(c.endpointURL) { if s3utils.IsGoogleEndpoint(c.endpointURL) {
p.formData["GoogleAccessId"] = c.accessKeyID p.formData["GoogleAccessId"] = c.accessKeyID
} else { } else {
// For all other endpoints set this value to be 'AWSAccessKeyId'. // For all other endpoints set this value to be 'AWSAccessKeyId'.
p.formData["AWSAccessKeyId"] = c.accessKeyID p.formData["AWSAccessKeyId"] = c.accessKeyID
} }
// Sign the policy. // Sign the policy.
p.formData["signature"] = postPresignSignatureV2(policyBase64, c.secretAccessKey) p.formData["signature"] = s3signer.PostPresignSignatureV2(policyBase64, c.secretAccessKey)
return u, p.formData, nil return u, p.formData, nil
} }
@@ -156,7 +159,7 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (u *url.URL, formData map[str
} }
// Add a credential policy. // Add a credential policy.
credential := getCredential(c.accessKeyID, location, t) credential := s3signer.GetCredential(c.accessKeyID, location, t)
if err = p.addNewPolicy(policyCondition{ if err = p.addNewPolicy(policyCondition{
matchType: "eq", matchType: "eq",
condition: "$x-amz-credential", condition: "$x-amz-credential",
@@ -172,6 +175,6 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (u *url.URL, formData map[str
p.formData["x-amz-algorithm"] = signV4Algorithm p.formData["x-amz-algorithm"] = signV4Algorithm
p.formData["x-amz-credential"] = credential p.formData["x-amz-credential"] = credential
p.formData["x-amz-date"] = t.Format(iso8601DateFormat) p.formData["x-amz-date"] = t.Format(iso8601DateFormat)
p.formData["x-amz-signature"] = postPresignSignatureV4(policyBase64, t, c.secretAccessKey, location) p.formData["x-amz-signature"] = s3signer.PostPresignSignatureV4(policyBase64, t, c.secretAccessKey, location)
return u, p.formData, nil return u, p.formData, nil
} }

13
vendor/github.com/minio/minio-go/api-put-bucket.go сгенерированный поставляемый
Просмотреть файл

@@ -26,8 +26,10 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"path"
"github.com/minio/minio-go/pkg/policy" "github.com/minio/minio-go/pkg/policy"
"github.com/minio/minio-go/pkg/s3signer"
) )
/// Bucket operations /// Bucket operations
@@ -89,11 +91,8 @@ func (c Client) makeBucketRequest(bucketName string, location string) (*http.Req
// is the preferred method here. The final location of the // is the preferred method here. The final location of the
// 'bucket' is provided through XML LocationConstraint data with // 'bucket' is provided through XML LocationConstraint data with
// the request. // the request.
targetURL, err := url.Parse(c.endpointURL) targetURL := c.endpointURL
if err != nil { targetURL.Path = path.Join(bucketName, "") + "/"
return nil, err
}
targetURL.Path = "/" + bucketName + "/"
// get a new HTTP request for the method. // get a new HTTP request for the method.
req, err := http.NewRequest("PUT", targetURL.String(), nil) req, err := http.NewRequest("PUT", targetURL.String(), nil)
@@ -133,9 +132,9 @@ func (c Client) makeBucketRequest(bucketName string, location string) (*http.Req
if c.signature.isV4() { if c.signature.isV4() {
// Signature calculated for MakeBucket request should be for 'us-east-1', // Signature calculated for MakeBucket request should be for 'us-east-1',
// regardless of the bucket's location constraint. // regardless of the bucket's location constraint.
req = signV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1") req = s3signer.SignV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1")
} else if c.signature.isV2() { } else if c.signature.isV2() {
req = signV2(*req, c.accessKeyID, c.secretAccessKey) req = s3signer.SignV2(*req, c.accessKeyID, c.secretAccessKey)
} }
// Return signed request. // Return signed request.

17
vendor/github.com/minio/minio-go/api-put-bucket_test.go сгенерированный поставляемый
Просмотреть файл

@@ -24,8 +24,10 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "path"
"testing" "testing"
"github.com/minio/minio-go/pkg/s3signer"
) )
// Tests validate http request formulated for creation of bucket. // Tests validate http request formulated for creation of bucket.
@@ -33,14 +35,11 @@ func TestMakeBucketRequest(t *testing.T) {
// Generates expected http request for bucket creation. // Generates expected http request for bucket creation.
// Used for asserting with the actual request generated. // Used for asserting with the actual request generated.
createExpectedRequest := func(c *Client, bucketName string, location string, req *http.Request) (*http.Request, error) { createExpectedRequest := func(c *Client, bucketName string, location string, req *http.Request) (*http.Request, error) {
targetURL := c.endpointURL
targetURL, err := url.Parse(c.endpointURL) targetURL.Path = path.Join(bucketName, "") + "/"
if err != nil {
return nil, err
}
targetURL.Path = "/" + bucketName + "/"
// get a new HTTP request for the method. // get a new HTTP request for the method.
var err error
req, err = http.NewRequest("PUT", targetURL.String(), nil) req, err = http.NewRequest("PUT", targetURL.String(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -78,9 +77,9 @@ func TestMakeBucketRequest(t *testing.T) {
if c.signature.isV4() { if c.signature.isV4() {
// Signature calculated for MakeBucket request should be for 'us-east-1', // Signature calculated for MakeBucket request should be for 'us-east-1',
// regardless of the bucket's location constraint. // regardless of the bucket's location constraint.
req = signV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1") req = s3signer.SignV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1")
} else if c.signature.isV2() { } else if c.signature.isV2() {
req = signV2(*req, c.accessKeyID, c.secretAccessKey) req = s3signer.SignV2(*req, c.accessKeyID, c.secretAccessKey)
} }
// Return signed request. // Return signed request.

76
vendor/github.com/minio/minio-go/api-put-object-common.go сгенерированный поставляемый
Просмотреть файл

@@ -44,18 +44,17 @@ func isReadAt(reader io.Reader) (ok bool) {
} }
// shouldUploadPart - verify if part should be uploaded. // shouldUploadPart - verify if part should be uploaded.
func shouldUploadPart(objPart objectPart, objectParts map[int]objectPart) bool { func shouldUploadPart(objPart objectPart, uploadReq uploadPartReq) bool {
// If part not found should upload the part. // If part not found should upload the part.
uploadedPart, found := objectParts[objPart.PartNumber] if uploadReq.Part == nil {
if !found {
return true return true
} }
// if size mismatches should upload the part. // if size mismatches should upload the part.
if objPart.Size != uploadedPart.Size { if objPart.Size != uploadReq.Part.Size {
return true return true
} }
// if md5sum mismatches should upload the part. // if md5sum mismatches should upload the part.
if objPart.ETag != uploadedPart.ETag { if objPart.ETag != uploadReq.Part.ETag {
return true return true
} }
return false return false
@@ -68,7 +67,7 @@ func shouldUploadPart(objPart objectPart, objectParts map[int]objectPart) bool {
// object storage it will have the following parameters as constants. // object storage it will have the following parameters as constants.
// //
// maxPartsCount - 10000 // maxPartsCount - 10000
// minPartSize - 5MiB // minPartSize - 64MiB
// maxMultipartPutObjectSize - 5TiB // maxMultipartPutObjectSize - 5TiB
// //
func optimalPartInfo(objectSize int64) (totalPartsCount int, partSize int64, lastPartSize int64, err error) { func optimalPartInfo(objectSize int64) (totalPartsCount int, partSize int64, lastPartSize int64, err error) {
@@ -167,37 +166,64 @@ func hashCopyN(hashAlgorithms map[string]hash.Hash, hashSums map[string][]byte,
// getUploadID - fetch upload id if already present for an object name // getUploadID - fetch upload id if already present for an object name
// or initiate a new request to fetch a new upload id. // or initiate a new request to fetch a new upload id.
func (c Client) getUploadID(bucketName, objectName, contentType string) (uploadID string, isNew bool, err error) { func (c Client) newUploadID(bucketName, objectName string, metaData map[string][]string) (uploadID string, err error) {
// Input validation. // Input validation.
if err := isValidBucketName(bucketName); err != nil { if err := isValidBucketName(bucketName); err != nil {
return "", false, err return "", err
} }
if err := isValidObjectName(objectName); err != nil { if err := isValidObjectName(objectName); err != nil {
return "", false, err return "", err
} }
// Set content Type to default if empty string. // Initiate multipart upload for an object.
if contentType == "" { initMultipartUploadResult, err := c.initiateMultipartUpload(bucketName, objectName, metaData)
contentType = "application/octet-stream"
}
// Find upload id for previous upload for an object.
uploadID, err = c.findUploadID(bucketName, objectName)
if err != nil { if err != nil {
return "", false, err return "", err
} }
return initMultipartUploadResult.UploadID, nil
}
// getMpartUploadSession returns the upload id and the uploaded parts to continue a previous upload session
// or initiate a new multipart session if no current one found
func (c Client) getMpartUploadSession(bucketName, objectName string, metaData map[string][]string) (string, map[int]objectPart, error) {
// A map of all uploaded parts.
var partsInfo map[int]objectPart
var err error
uploadID, err := c.findUploadID(bucketName, objectName)
if err != nil {
return "", nil, err
}
if uploadID == "" { if uploadID == "" {
// Initiate multipart upload for an object. // Initiates a new multipart request
initMultipartUploadResult, err := c.initiateMultipartUpload(bucketName, objectName, contentType) uploadID, err = c.newUploadID(bucketName, objectName, metaData)
if err != nil { if err != nil {
return "", false, err return "", nil, err
}
} else {
// Fetch previously upload parts and maximum part size.
partsInfo, err = c.listObjectParts(bucketName, objectName, uploadID)
if err != nil {
// When the server returns NoSuchUpload even if its previouls acknowleged the existance of the upload id,
// initiate a new multipart upload
if respErr, ok := err.(ErrorResponse); ok && respErr.Code == "NoSuchUpload" {
uploadID, err = c.newUploadID(bucketName, objectName, metaData)
if err != nil {
return "", nil, err
}
} else {
return "", nil, err
}
} }
// Save the new upload id.
uploadID = initMultipartUploadResult.UploadID
// Indicate that this is a new upload id.
isNew = true
} }
return uploadID, isNew, nil
// Allocate partsInfo if not done yet
if partsInfo == nil {
partsInfo = make(map[int]objectPart)
}
return uploadID, partsInfo, nil
} }
// computeHash - Calculates hashes for an input read Seeker. // computeHash - Calculates hashes for an input read Seeker.

8
vendor/github.com/minio/minio-go/api-put-object-copy.go сгенерированный поставляемый
Просмотреть файл

@@ -16,7 +16,11 @@
package minio package minio
import "net/http" import (
"net/http"
"github.com/minio/minio-go/pkg/s3utils"
)
// CopyObject - copy a source object into a new object with the provided name in the provided bucket // CopyObject - copy a source object into a new object with the provided name in the provided bucket
func (c Client) CopyObject(bucketName string, objectName string, objectSource string, cpCond CopyConditions) error { func (c Client) CopyObject(bucketName string, objectName string, objectSource string, cpCond CopyConditions) error {
@@ -38,7 +42,7 @@ func (c Client) CopyObject(bucketName string, objectName string, objectSource st
} }
// Set copy source. // Set copy source.
customHeaders.Set("x-amz-copy-source", urlEncodePath(objectSource)) customHeaders.Set("x-amz-copy-source", s3utils.EncodePath(objectSource))
// Execute PUT on objectName. // Execute PUT on objectName.
resp, err := c.executeMethod("PUT", requestMetadata{ resp, err := c.executeMethod("PUT", requestMetadata{

121
vendor/github.com/minio/minio-go/api-put-object-file.go сгенерированный поставляемый
Просмотреть файл

@@ -28,6 +28,8 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"github.com/minio/minio-go/pkg/s3utils"
) )
// FPutObject - Create an object in a bucket, with contents from file at filePath. // FPutObject - Create an object in a bucket, with contents from file at filePath.
@@ -62,6 +64,8 @@ func (c Client) FPutObject(bucketName, objectName, filePath, contentType string)
return 0, ErrEntityTooLarge(fileSize, maxMultipartPutObjectSize, bucketName, objectName) return 0, ErrEntityTooLarge(fileSize, maxMultipartPutObjectSize, bucketName, objectName)
} }
objMetadata := make(map[string][]string)
// Set contentType based on filepath extension if not given or default // Set contentType based on filepath extension if not given or default
// value of "binary/octet-stream" if the extension has no associated type. // value of "binary/octet-stream" if the extension has no associated type.
if contentType == "" { if contentType == "" {
@@ -70,9 +74,11 @@ func (c Client) FPutObject(bucketName, objectName, filePath, contentType string)
} }
} }
objMetadata["Content-Type"] = []string{contentType}
// NOTE: Google Cloud Storage multipart Put is not compatible with Amazon S3 APIs. // NOTE: Google Cloud Storage multipart Put is not compatible with Amazon S3 APIs.
// Current implementation will only upload a maximum of 5GiB to Google Cloud Storage servers. // Current implementation will only upload a maximum of 5GiB to Google Cloud Storage servers.
if isGoogleEndpoint(c.endpointURL) { if s3utils.IsGoogleEndpoint(c.endpointURL) {
if fileSize > int64(maxSinglePutObjectSize) { if fileSize > int64(maxSinglePutObjectSize) {
return 0, ErrorResponse{ return 0, ErrorResponse{
Code: "NotImplemented", Code: "NotImplemented",
@@ -82,11 +88,11 @@ func (c Client) FPutObject(bucketName, objectName, filePath, contentType string)
} }
} }
// Do not compute MD5 for Google Cloud Storage. Uploads up to 5GiB in size. // Do not compute MD5 for Google Cloud Storage. Uploads up to 5GiB in size.
return c.putObjectNoChecksum(bucketName, objectName, fileReader, fileSize, contentType, nil) return c.putObjectNoChecksum(bucketName, objectName, fileReader, fileSize, objMetadata, nil)
} }
// NOTE: S3 doesn't allow anonymous multipart requests. // NOTE: S3 doesn't allow anonymous multipart requests.
if isAmazonEndpoint(c.endpointURL) && c.anonymous { if s3utils.IsAmazonEndpoint(c.endpointURL) && c.anonymous {
if fileSize > int64(maxSinglePutObjectSize) { if fileSize > int64(maxSinglePutObjectSize) {
return 0, ErrorResponse{ return 0, ErrorResponse{
Code: "NotImplemented", Code: "NotImplemented",
@@ -97,15 +103,15 @@ func (c Client) FPutObject(bucketName, objectName, filePath, contentType string)
} }
// Do not compute MD5 for anonymous requests to Amazon // Do not compute MD5 for anonymous requests to Amazon
// S3. Uploads up to 5GiB in size. // S3. Uploads up to 5GiB in size.
return c.putObjectNoChecksum(bucketName, objectName, fileReader, fileSize, contentType, nil) return c.putObjectNoChecksum(bucketName, objectName, fileReader, fileSize, objMetadata, nil)
} }
// Small object upload is initiated for uploads for input data size smaller than 5MiB. // Small object upload is initiated for uploads for input data size smaller than 5MiB.
if fileSize < minPartSize && fileSize >= 0 { if fileSize < minPartSize && fileSize >= 0 {
return c.putObjectSingle(bucketName, objectName, fileReader, fileSize, contentType, nil) return c.putObjectSingle(bucketName, objectName, fileReader, fileSize, objMetadata, nil)
} }
// Upload all large objects as multipart. // Upload all large objects as multipart.
n, err = c.putObjectMultipartFromFile(bucketName, objectName, fileReader, fileSize, contentType, nil) n, err = c.putObjectMultipartFromFile(bucketName, objectName, fileReader, fileSize, objMetadata, nil)
if err != nil { if err != nil {
errResp := ToErrorResponse(err) errResp := ToErrorResponse(err)
// Verify if multipart functionality is not available, if not // Verify if multipart functionality is not available, if not
@@ -116,7 +122,7 @@ func (c Client) FPutObject(bucketName, objectName, filePath, contentType string)
return 0, ErrEntityTooLarge(fileSize, maxSinglePutObjectSize, bucketName, objectName) return 0, ErrEntityTooLarge(fileSize, maxSinglePutObjectSize, bucketName, objectName)
} }
// Fall back to uploading as single PutObject operation. // Fall back to uploading as single PutObject operation.
return c.putObjectSingle(bucketName, objectName, fileReader, fileSize, contentType, nil) return c.putObjectSingle(bucketName, objectName, fileReader, fileSize, objMetadata, nil)
} }
return n, err return n, err
} }
@@ -131,7 +137,7 @@ func (c Client) FPutObject(bucketName, objectName, filePath, contentType string)
// against MD5SUM of each individual parts. This function also // against MD5SUM of each individual parts. This function also
// effectively utilizes file system capabilities of reading from // effectively utilizes file system capabilities of reading from
// specific sections and not having to create temporary files. // specific sections and not having to create temporary files.
func (c Client) putObjectMultipartFromFile(bucketName, objectName string, fileReader io.ReaderAt, fileSize int64, contentType string, progress io.Reader) (int64, error) { func (c Client) putObjectMultipartFromFile(bucketName, objectName string, fileReader io.ReaderAt, fileSize int64, metaData map[string][]string, progress io.Reader) (int64, error) {
// Input validation. // Input validation.
if err := isValidBucketName(bucketName); err != nil { if err := isValidBucketName(bucketName); err != nil {
return 0, err return 0, err
@@ -140,9 +146,8 @@ func (c Client) putObjectMultipartFromFile(bucketName, objectName string, fileRe
return 0, err return 0, err
} }
// Get upload id for an object, initiates a new multipart request // Get the upload id of a previously partially uploaded object or initiate a new multipart upload
// if it cannot find any previously partially uploaded object. uploadID, partsInfo, err := c.getMpartUploadSession(bucketName, objectName, metaData)
uploadID, isNew, err := c.getUploadID(bucketName, objectName, contentType)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -153,19 +158,6 @@ func (c Client) putObjectMultipartFromFile(bucketName, objectName string, fileRe
// Complete multipart upload. // Complete multipart upload.
var complMultipartUpload completeMultipartUpload var complMultipartUpload completeMultipartUpload
// A map of all uploaded parts.
var partsInfo = make(map[int]objectPart)
// If this session is a continuation of a previous session fetch all
// previously uploaded parts info.
if !isNew {
// Fetch previously upload parts and maximum part size.
partsInfo, err = c.listObjectParts(bucketName, objectName, uploadID)
if err != nil {
return 0, err
}
}
// Calculate the optimal parts info for a given size. // Calculate the optimal parts info for a given size.
totalPartsCount, partSize, lastPartSize, err := optimalPartInfo(fileSize) totalPartsCount, partSize, lastPartSize, err := optimalPartInfo(fileSize)
if err != nil { if err != nil {
@@ -178,14 +170,19 @@ func (c Client) putObjectMultipartFromFile(bucketName, objectName string, fileRe
// Create a channel to communicate which part to upload. // Create a channel to communicate which part to upload.
// Buffer this to 10000, the maximum number of parts allowed by S3. // Buffer this to 10000, the maximum number of parts allowed by S3.
uploadPartsCh := make(chan int, 10000) uploadPartsCh := make(chan uploadPartReq, 10000)
// Just for readability. // Just for readability.
lastPartNumber := totalPartsCount lastPartNumber := totalPartsCount
// Send each part through the partUploadCh to be uploaded. // Send each part through the partUploadCh to be uploaded.
for p := 1; p <= totalPartsCount; p++ { for p := 1; p <= totalPartsCount; p++ {
uploadPartsCh <- p part, ok := partsInfo[p]
if ok {
uploadPartsCh <- uploadPartReq{PartNum: p, Part: &part}
} else {
uploadPartsCh <- uploadPartReq{PartNum: p, Part: nil}
}
} }
close(uploadPartsCh) close(uploadPartsCh)
@@ -193,7 +190,7 @@ func (c Client) putObjectMultipartFromFile(bucketName, objectName string, fileRe
for w := 1; w <= 3; w++ { for w := 1; w <= 3; w++ {
go func() { go func() {
// Deal with each part as it comes through the channel. // Deal with each part as it comes through the channel.
for partNumber := range uploadPartsCh { for uploadReq := range uploadPartsCh {
// Add hash algorithms that need to be calculated by computeHash() // Add hash algorithms that need to be calculated by computeHash()
// In case of a non-v4 signature or https connection, sha256 is not needed. // In case of a non-v4 signature or https connection, sha256 is not needed.
hashAlgos := make(map[string]hash.Hash) hashAlgos := make(map[string]hash.Hash)
@@ -203,47 +200,50 @@ func (c Client) putObjectMultipartFromFile(bucketName, objectName string, fileRe
hashAlgos["sha256"] = sha256.New() hashAlgos["sha256"] = sha256.New()
} }
// If partNumber was not uploaded we calculate the missing
// part offset and size. For all other part numbers we
// calculate offset based on multiples of partSize.
readOffset := int64(uploadReq.PartNum-1) * partSize
missingPartSize := partSize
// As a special case if partNumber is lastPartNumber, we
// calculate the offset based on the last part size.
if uploadReq.PartNum == lastPartNumber {
readOffset = (fileSize - lastPartSize)
missingPartSize = lastPartSize
}
// Get a section reader on a particular offset.
sectionReader := io.NewSectionReader(fileReader, readOffset, missingPartSize)
var prtSize int64
var err error
prtSize, err = computeHash(hashAlgos, hashSums, sectionReader)
if err != nil {
uploadedPartsCh <- uploadedPartRes{
Error: err,
}
// Exit the goroutine.
return
}
// Create the part to be uploaded. // Create the part to be uploaded.
verifyObjPart := objectPart{ verifyObjPart := objectPart{
ETag: hex.EncodeToString(hashSums["md5"]), ETag: hex.EncodeToString(hashSums["md5"]),
PartNumber: partNumber, PartNumber: uploadReq.PartNum,
Size: partSize, Size: partSize,
} }
// If this is the last part do not give it the full part size. // If this is the last part do not give it the full part size.
if partNumber == lastPartNumber { if uploadReq.PartNum == lastPartNumber {
verifyObjPart.Size = lastPartSize verifyObjPart.Size = lastPartSize
} }
// Verify if part should be uploaded. // Verify if part should be uploaded.
if shouldUploadPart(verifyObjPart, partsInfo) { if shouldUploadPart(verifyObjPart, uploadReq) {
// If partNumber was not uploaded we calculate the missing
// part offset and size. For all other part numbers we
// calculate offset based on multiples of partSize.
readOffset := int64(partNumber-1) * partSize
missingPartSize := partSize
// As a special case if partNumber is lastPartNumber, we
// calculate the offset based on the last part size.
if partNumber == lastPartNumber {
readOffset = (fileSize - lastPartSize)
missingPartSize = lastPartSize
}
// Get a section reader on a particular offset.
sectionReader := io.NewSectionReader(fileReader, readOffset, missingPartSize)
var prtSize int64
prtSize, err = computeHash(hashAlgos, hashSums, sectionReader)
if err != nil {
uploadedPartsCh <- uploadedPartRes{
Error: err,
}
// Exit the goroutine.
return
}
// Proceed to upload the part. // Proceed to upload the part.
var objPart objectPart var objPart objectPart
objPart, err = c.uploadPart(bucketName, objectName, uploadID, sectionReader, partNumber, hashSums["md5"], hashSums["sha256"], prtSize) objPart, err = c.uploadPart(bucketName, objectName, uploadID, sectionReader, uploadReq.PartNum, hashSums["md5"], hashSums["sha256"], prtSize)
if err != nil { if err != nil {
uploadedPartsCh <- uploadedPartRes{ uploadedPartsCh <- uploadedPartRes{
Error: err, Error: err,
@@ -252,12 +252,13 @@ func (c Client) putObjectMultipartFromFile(bucketName, objectName string, fileRe
return return
} }
// Save successfully uploaded part metadata. // Save successfully uploaded part metadata.
partsInfo[partNumber] = objPart uploadReq.Part = &objPart
} }
// Return through the channel the part size. // Return through the channel the part size.
uploadedPartsCh <- uploadedPartRes{ uploadedPartsCh <- uploadedPartRes{
Size: verifyObjPart.Size, Size: verifyObjPart.Size,
PartNum: partNumber, PartNum: uploadReq.PartNum,
Part: uploadReq.Part,
Error: nil, Error: nil,
} }
} }
@@ -271,8 +272,8 @@ func (c Client) putObjectMultipartFromFile(bucketName, objectName string, fileRe
return totalUploadedSize, uploadRes.Error return totalUploadedSize, uploadRes.Error
} }
// Retrieve each uploaded part and store it to be completed. // Retrieve each uploaded part and store it to be completed.
part, ok := partsInfo[uploadRes.PartNum] part := uploadRes.Part
if !ok { if part == nil {
return totalUploadedSize, ErrInvalidArgument(fmt.Sprintf("Missing part number %d", uploadRes.PartNum)) return totalUploadedSize, ErrInvalidArgument(fmt.Sprintf("Missing part number %d", uploadRes.PartNum))
} }
// Update the total uploaded size. // Update the total uploaded size.

54
vendor/github.com/minio/minio-go/api-put-object-multipart.go сгенерированный поставляемый
Просмотреть файл

@@ -45,11 +45,11 @@ import (
// If we exhaust all the known types, code proceeds to use stream as // If we exhaust all the known types, code proceeds to use stream as
// is where each part is re-downloaded, checksummed and verified // is where each part is re-downloaded, checksummed and verified
// before upload. // before upload.
func (c Client) putObjectMultipart(bucketName, objectName string, reader io.Reader, size int64, contentType string, progress io.Reader) (n int64, err error) { func (c Client) putObjectMultipart(bucketName, objectName string, reader io.Reader, size int64, metaData map[string][]string, progress io.Reader) (n int64, err error) {
if size > 0 && size > minPartSize { if size > 0 && size > minPartSize {
// Verify if reader is *os.File, then use file system functionalities. // Verify if reader is *os.File, then use file system functionalities.
if isFile(reader) { if isFile(reader) {
return c.putObjectMultipartFromFile(bucketName, objectName, reader.(*os.File), size, contentType, progress) return c.putObjectMultipartFromFile(bucketName, objectName, reader.(*os.File), size, metaData, progress)
} }
// Verify if reader is *minio.Object or io.ReaderAt. // Verify if reader is *minio.Object or io.ReaderAt.
// NOTE: Verification of object is kept for a specific purpose // NOTE: Verification of object is kept for a specific purpose
@@ -58,17 +58,17 @@ func (c Client) putObjectMultipart(bucketName, objectName string, reader io.Read
// and such a functionality is used in the subsequent code // and such a functionality is used in the subsequent code
// path. // path.
if isObject(reader) || isReadAt(reader) { if isObject(reader) || isReadAt(reader) {
return c.putObjectMultipartFromReadAt(bucketName, objectName, reader.(io.ReaderAt), size, contentType, progress) return c.putObjectMultipartFromReadAt(bucketName, objectName, reader.(io.ReaderAt), size, metaData, progress)
} }
} }
// For any other data size and reader type we do generic multipart // For any other data size and reader type we do generic multipart
// approach by staging data in temporary files and uploading them. // approach by staging data in temporary files and uploading them.
return c.putObjectMultipartStream(bucketName, objectName, reader, size, contentType, progress) return c.putObjectMultipartStream(bucketName, objectName, reader, size, metaData, progress)
} }
// putObjectStream uploads files bigger than 5MiB, and also supports // putObjectStream uploads files bigger than 64MiB, and also supports
// special case where size is unknown i.e '-1'. // special case where size is unknown i.e '-1'.
func (c Client) putObjectMultipartStream(bucketName, objectName string, reader io.Reader, size int64, contentType string, progress io.Reader) (n int64, err error) { func (c Client) putObjectMultipartStream(bucketName, objectName string, reader io.Reader, size int64, metaData map[string][]string, progress io.Reader) (n int64, err error) {
// Input validation. // Input validation.
if err := isValidBucketName(bucketName); err != nil { if err := isValidBucketName(bucketName); err != nil {
return 0, err return 0, err
@@ -83,27 +83,12 @@ func (c Client) putObjectMultipartStream(bucketName, objectName string, reader i
// Complete multipart upload. // Complete multipart upload.
var complMultipartUpload completeMultipartUpload var complMultipartUpload completeMultipartUpload
// A map of all previously uploaded parts. // Get the upload id of a previously partially uploaded object or initiate a new multipart upload
var partsInfo = make(map[int]objectPart) uploadID, partsInfo, err := c.getMpartUploadSession(bucketName, objectName, metaData)
// getUploadID for an object, initiates a new multipart request
// if it cannot find any previously partially uploaded object.
uploadID, isNew, err := c.getUploadID(bucketName, objectName, contentType)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// If This session is a continuation of a previous session fetch all
// previously uploaded parts info and as a special case only fetch partsInfo
// for only known upload size.
if !isNew {
// Fetch previously uploaded parts and maximum part size.
partsInfo, err = c.listObjectParts(bucketName, objectName, uploadID)
if err != nil {
return 0, err
}
}
// Calculate the optimal parts info for a given size. // Calculate the optimal parts info for a given size.
totalPartsCount, partSize, _, err := optimalPartInfo(size) totalPartsCount, partSize, _, err := optimalPartInfo(size)
if err != nil { if err != nil {
@@ -139,12 +124,14 @@ func (c Client) putObjectMultipartStream(bucketName, objectName string, reader i
// as we read from the source. // as we read from the source.
reader = newHook(tmpBuffer, progress) reader = newHook(tmpBuffer, progress)
part, ok := partsInfo[partNumber]
// Verify if part should be uploaded. // Verify if part should be uploaded.
if shouldUploadPart(objectPart{ if !ok || shouldUploadPart(objectPart{
ETag: hex.EncodeToString(hashSums["md5"]), ETag: hex.EncodeToString(hashSums["md5"]),
PartNumber: partNumber, PartNumber: partNumber,
Size: prtSize, Size: prtSize,
}, partsInfo) { }, uploadPartReq{PartNum: partNumber, Part: &part}) {
// Proceed to upload the part. // Proceed to upload the part.
var objPart objectPart var objPart objectPart
objPart, err = c.uploadPart(bucketName, objectName, uploadID, reader, partNumber, hashSums["md5"], hashSums["sha256"], prtSize) objPart, err = c.uploadPart(bucketName, objectName, uploadID, reader, partNumber, hashSums["md5"], hashSums["sha256"], prtSize)
@@ -212,7 +199,7 @@ func (c Client) putObjectMultipartStream(bucketName, objectName string, reader i
} }
// initiateMultipartUpload - Initiates a multipart upload and returns an upload ID. // initiateMultipartUpload - Initiates a multipart upload and returns an upload ID.
func (c Client) initiateMultipartUpload(bucketName, objectName, contentType string) (initiateMultipartUploadResult, error) { func (c Client) initiateMultipartUpload(bucketName, objectName string, metaData map[string][]string) (initiateMultipartUploadResult, error) {
// Input validation. // Input validation.
if err := isValidBucketName(bucketName); err != nil { if err := isValidBucketName(bucketName); err != nil {
return initiateMultipartUploadResult{}, err return initiateMultipartUploadResult{}, err
@@ -225,13 +212,18 @@ func (c Client) initiateMultipartUpload(bucketName, objectName, contentType stri
urlValues := make(url.Values) urlValues := make(url.Values)
urlValues.Set("uploads", "") urlValues.Set("uploads", "")
if contentType == "" {
contentType = "application/octet-stream"
}
// Set ContentType header. // Set ContentType header.
customHeader := make(http.Header) customHeader := make(http.Header)
customHeader.Set("Content-Type", contentType) for k, v := range metaData {
if len(v) > 0 {
customHeader.Set(k, v[0])
}
}
// Set a default content-type header if the latter is not provided
if v, ok := metaData["Content-Type"]; !ok || len(v) == 0 {
customHeader.Set("Content-Type", "application/octet-stream")
}
reqMetadata := requestMetadata{ reqMetadata := requestMetadata{
bucketName: bucketName, bucketName: bucketName,

25
vendor/github.com/minio/minio-go/api-put-object-progress.go сгенерированный поставляемый
Просмотреть файл

@@ -19,10 +19,19 @@ package minio
import ( import (
"io" "io"
"strings" "strings"
"github.com/minio/minio-go/pkg/s3utils"
) )
// PutObjectWithProgress - With progress. // PutObjectWithProgress - with progress.
func (c Client) PutObjectWithProgress(bucketName, objectName string, reader io.Reader, contentType string, progress io.Reader) (n int64, err error) { func (c Client) PutObjectWithProgress(bucketName, objectName string, reader io.Reader, contentType string, progress io.Reader) (n int64, err error) {
metaData := make(map[string][]string)
metaData["Content-Type"] = []string{contentType}
return c.PutObjectWithMetadata(bucketName, objectName, reader, metaData, progress)
}
// PutObjectWithMetadata - with metadata.
func (c Client) PutObjectWithMetadata(bucketName, objectName string, reader io.Reader, metaData map[string][]string, progress io.Reader) (n int64, err error) {
// Input validation. // Input validation.
if err := isValidBucketName(bucketName); err != nil { if err := isValidBucketName(bucketName); err != nil {
return 0, err return 0, err
@@ -50,7 +59,7 @@ func (c Client) PutObjectWithProgress(bucketName, objectName string, reader io.R
// NOTE: Google Cloud Storage does not implement Amazon S3 Compatible multipart PUT. // NOTE: Google Cloud Storage does not implement Amazon S3 Compatible multipart PUT.
// So we fall back to single PUT operation with the maximum limit of 5GiB. // So we fall back to single PUT operation with the maximum limit of 5GiB.
if isGoogleEndpoint(c.endpointURL) { if s3utils.IsGoogleEndpoint(c.endpointURL) {
if size <= -1 { if size <= -1 {
return 0, ErrorResponse{ return 0, ErrorResponse{
Code: "NotImplemented", Code: "NotImplemented",
@@ -63,11 +72,11 @@ func (c Client) PutObjectWithProgress(bucketName, objectName string, reader io.R
return 0, ErrEntityTooLarge(size, maxSinglePutObjectSize, bucketName, objectName) return 0, ErrEntityTooLarge(size, maxSinglePutObjectSize, bucketName, objectName)
} }
// Do not compute MD5 for Google Cloud Storage. Uploads up to 5GiB in size. // Do not compute MD5 for Google Cloud Storage. Uploads up to 5GiB in size.
return c.putObjectNoChecksum(bucketName, objectName, reader, size, contentType, progress) return c.putObjectNoChecksum(bucketName, objectName, reader, size, metaData, progress)
} }
// NOTE: S3 doesn't allow anonymous multipart requests. // NOTE: S3 doesn't allow anonymous multipart requests.
if isAmazonEndpoint(c.endpointURL) && c.anonymous { if s3utils.IsAmazonEndpoint(c.endpointURL) && c.anonymous {
if size <= -1 { if size <= -1 {
return 0, ErrorResponse{ return 0, ErrorResponse{
Code: "NotImplemented", Code: "NotImplemented",
@@ -81,15 +90,15 @@ func (c Client) PutObjectWithProgress(bucketName, objectName string, reader io.R
} }
// Do not compute MD5 for anonymous requests to Amazon // Do not compute MD5 for anonymous requests to Amazon
// S3. Uploads up to 5GiB in size. // S3. Uploads up to 5GiB in size.
return c.putObjectNoChecksum(bucketName, objectName, reader, size, contentType, progress) return c.putObjectNoChecksum(bucketName, objectName, reader, size, metaData, progress)
} }
// putSmall object. // putSmall object.
if size < minPartSize && size >= 0 { if size < minPartSize && size >= 0 {
return c.putObjectSingle(bucketName, objectName, reader, size, contentType, progress) return c.putObjectSingle(bucketName, objectName, reader, size, metaData, progress)
} }
// For all sizes greater than 5MiB do multipart. // For all sizes greater than 5MiB do multipart.
n, err = c.putObjectMultipart(bucketName, objectName, reader, size, contentType, progress) n, err = c.putObjectMultipart(bucketName, objectName, reader, size, metaData, progress)
if err != nil { if err != nil {
errResp := ToErrorResponse(err) errResp := ToErrorResponse(err)
// Verify if multipart functionality is not available, if not // Verify if multipart functionality is not available, if not
@@ -100,7 +109,7 @@ func (c Client) PutObjectWithProgress(bucketName, objectName string, reader io.R
return 0, ErrEntityTooLarge(size, maxSinglePutObjectSize, bucketName, objectName) return 0, ErrEntityTooLarge(size, maxSinglePutObjectSize, bucketName, objectName)
} }
// Fall back to uploading as single PutObject operation. // Fall back to uploading as single PutObject operation.
return c.putObjectSingle(bucketName, objectName, reader, size, contentType, progress) return c.putObjectSingle(bucketName, objectName, reader, size, metaData, progress)
} }
return n, err return n, err
} }

135
vendor/github.com/minio/minio-go/api-put-object-readat.go сгенерированный поставляемый
Просмотреть файл

@@ -32,17 +32,22 @@ type uploadedPartRes struct {
Error error // Any error encountered while uploading the part. Error error // Any error encountered while uploading the part.
PartNum int // Number of the part uploaded. PartNum int // Number of the part uploaded.
Size int64 // Size of the part uploaded. Size int64 // Size of the part uploaded.
Part *objectPart
}
type uploadPartReq struct {
PartNum int // Number of the part uploaded.
Part *objectPart // Size of the part uploaded.
} }
// shouldUploadPartReadAt - verify if part should be uploaded. // shouldUploadPartReadAt - verify if part should be uploaded.
func shouldUploadPartReadAt(objPart objectPart, objectParts map[int]objectPart) bool { func shouldUploadPartReadAt(objPart objectPart, uploadReq uploadPartReq) bool {
// If part not found part should be uploaded. // If part not found part should be uploaded.
uploadedPart, found := objectParts[objPart.PartNumber] if uploadReq.Part == nil {
if !found {
return true return true
} }
// if size mismatches part should be uploaded. // if size mismatches part should be uploaded.
if uploadedPart.Size != objPart.Size { if uploadReq.Part.Size != objPart.Size {
return true return true
} }
return false return false
@@ -58,7 +63,7 @@ func shouldUploadPartReadAt(objPart objectPart, objectParts map[int]objectPart)
// temporary files for staging all the data, these temporary files are // temporary files for staging all the data, these temporary files are
// cleaned automatically when the caller i.e http client closes the // cleaned automatically when the caller i.e http client closes the
// stream after uploading all the contents successfully. // stream after uploading all the contents successfully.
func (c Client) putObjectMultipartFromReadAt(bucketName, objectName string, reader io.ReaderAt, size int64, contentType string, progress io.Reader) (n int64, err error) { func (c Client) putObjectMultipartFromReadAt(bucketName, objectName string, reader io.ReaderAt, size int64, metaData map[string][]string, progress io.Reader) (n int64, err error) {
// Input validation. // Input validation.
if err := isValidBucketName(bucketName); err != nil { if err := isValidBucketName(bucketName); err != nil {
return 0, err return 0, err
@@ -67,9 +72,8 @@ func (c Client) putObjectMultipartFromReadAt(bucketName, objectName string, read
return 0, err return 0, err
} }
// Get upload id for an object, initiates a new multipart request // Get the upload id of a previously partially uploaded object or initiate a new multipart upload
// if it cannot find any previously partially uploaded object. uploadID, partsInfo, err := c.getMpartUploadSession(bucketName, objectName, metaData)
uploadID, isNew, err := c.getUploadID(bucketName, objectName, contentType)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -80,17 +84,6 @@ func (c Client) putObjectMultipartFromReadAt(bucketName, objectName string, read
// Complete multipart upload. // Complete multipart upload.
var complMultipartUpload completeMultipartUpload var complMultipartUpload completeMultipartUpload
// A map of all uploaded parts.
var partsInfo = make(map[int]objectPart)
// Fetch all parts info previously uploaded.
if !isNew {
partsInfo, err = c.listObjectParts(bucketName, objectName, uploadID)
if err != nil {
return 0, err
}
}
// Calculate the optimal parts info for a given size. // Calculate the optimal parts info for a given size.
totalPartsCount, partSize, lastPartSize, err := optimalPartInfo(size) totalPartsCount, partSize, lastPartSize, err := optimalPartInfo(size)
if err != nil { if err != nil {
@@ -103,7 +96,7 @@ func (c Client) putObjectMultipartFromReadAt(bucketName, objectName string, read
// Declare a channel that sends the next part number to be uploaded. // Declare a channel that sends the next part number to be uploaded.
// Buffered to 10000 because thats the maximum number of parts allowed // Buffered to 10000 because thats the maximum number of parts allowed
// by S3. // by S3.
uploadPartsCh := make(chan int, 10000) uploadPartsCh := make(chan uploadPartReq, 10000)
// Declare a channel that sends back the response of a part upload. // Declare a channel that sends back the response of a part upload.
// Buffered to 10000 because thats the maximum number of parts allowed // Buffered to 10000 because thats the maximum number of parts allowed
@@ -112,7 +105,12 @@ func (c Client) putObjectMultipartFromReadAt(bucketName, objectName string, read
// Send each part number to the channel to be processed. // Send each part number to the channel to be processed.
for p := 1; p <= totalPartsCount; p++ { for p := 1; p <= totalPartsCount; p++ {
uploadPartsCh <- p part, ok := partsInfo[p]
if ok {
uploadPartsCh <- uploadPartReq{PartNum: p, Part: &part}
} else {
uploadPartsCh <- uploadPartReq{PartNum: p, Part: nil}
}
} }
close(uploadPartsCh) close(uploadPartsCh)
@@ -123,64 +121,65 @@ func (c Client) putObjectMultipartFromReadAt(bucketName, objectName string, read
readAtBuffer := make([]byte, optimalReadBufferSize) readAtBuffer := make([]byte, optimalReadBufferSize)
// Each worker will draw from the part channel and upload in parallel. // Each worker will draw from the part channel and upload in parallel.
for partNumber := range uploadPartsCh { for uploadReq := range uploadPartsCh {
// Declare a new tmpBuffer. // Declare a new tmpBuffer.
tmpBuffer := new(bytes.Buffer) tmpBuffer := new(bytes.Buffer)
// If partNumber was not uploaded we calculate the missing
// part offset and size. For all other part numbers we
// calculate offset based on multiples of partSize.
readOffset := int64(uploadReq.PartNum-1) * partSize
missingPartSize := partSize
// As a special case if partNumber is lastPartNumber, we
// calculate the offset based on the last part size.
if uploadReq.PartNum == lastPartNumber {
readOffset = (size - lastPartSize)
missingPartSize = lastPartSize
}
// Get a section reader on a particular offset.
sectionReader := io.NewSectionReader(reader, readOffset, missingPartSize)
// Choose the needed hash algorithms to be calculated by hashCopyBuffer.
// Sha256 is avoided in non-v4 signature requests or HTTPS connections
hashSums := make(map[string][]byte)
hashAlgos := make(map[string]hash.Hash)
hashAlgos["md5"] = md5.New()
if c.signature.isV4() && !c.secure {
hashAlgos["sha256"] = sha256.New()
}
var prtSize int64
var err error
prtSize, err = hashCopyBuffer(hashAlgos, hashSums, tmpBuffer, sectionReader, readAtBuffer)
if err != nil {
// Send the error back through the channel.
uploadedPartsCh <- uploadedPartRes{
Size: 0,
Error: err,
}
// Exit the goroutine.
return
}
// Verify object if its uploaded. // Verify object if its uploaded.
verifyObjPart := objectPart{ verifyObjPart := objectPart{
PartNumber: partNumber, PartNumber: uploadReq.PartNum,
Size: partSize, Size: partSize,
} }
// Special case if we see a last part number, save last part // Special case if we see a last part number, save last part
// size as the proper part size. // size as the proper part size.
if partNumber == lastPartNumber { if uploadReq.PartNum == lastPartNumber {
verifyObjPart.Size = lastPartSize verifyObjPart.Size = lastPartSize
} }
// Only upload the necessary parts. Otherwise return size through channel // Only upload the necessary parts. Otherwise return size through channel
// to update any progress bar. // to update any progress bar.
if shouldUploadPartReadAt(verifyObjPart, partsInfo) { if shouldUploadPartReadAt(verifyObjPart, uploadReq) {
// If partNumber was not uploaded we calculate the missing
// part offset and size. For all other part numbers we
// calculate offset based on multiples of partSize.
readOffset := int64(partNumber-1) * partSize
missingPartSize := partSize
// As a special case if partNumber is lastPartNumber, we
// calculate the offset based on the last part size.
if partNumber == lastPartNumber {
readOffset = (size - lastPartSize)
missingPartSize = lastPartSize
}
// Get a section reader on a particular offset.
sectionReader := io.NewSectionReader(reader, readOffset, missingPartSize)
// Choose the needed hash algorithms to be calculated by hashCopyBuffer.
// Sha256 is avoided in non-v4 signature requests or HTTPS connections
hashSums := make(map[string][]byte)
hashAlgos := make(map[string]hash.Hash)
hashAlgos["md5"] = md5.New()
if c.signature.isV4() && !c.secure {
hashAlgos["sha256"] = sha256.New()
}
var prtSize int64
prtSize, err = hashCopyBuffer(hashAlgos, hashSums, tmpBuffer, sectionReader, readAtBuffer)
if err != nil {
// Send the error back through the channel.
uploadedPartsCh <- uploadedPartRes{
Size: 0,
Error: err,
}
// Exit the goroutine.
return
}
// Proceed to upload the part. // Proceed to upload the part.
var objPart objectPart var objPart objectPart
objPart, err = c.uploadPart(bucketName, objectName, uploadID, tmpBuffer, partNumber, hashSums["md5"], hashSums["sha256"], prtSize) objPart, err = c.uploadPart(bucketName, objectName, uploadID, tmpBuffer, uploadReq.PartNum, hashSums["md5"], hashSums["sha256"], prtSize)
if err != nil { if err != nil {
uploadedPartsCh <- uploadedPartRes{ uploadedPartsCh <- uploadedPartRes{
Size: 0, Size: 0,
@@ -190,12 +189,13 @@ func (c Client) putObjectMultipartFromReadAt(bucketName, objectName string, read
return return
} }
// Save successfully uploaded part metadata. // Save successfully uploaded part metadata.
partsInfo[partNumber] = objPart uploadReq.Part = &objPart
} }
// Send successful part info through the channel. // Send successful part info through the channel.
uploadedPartsCh <- uploadedPartRes{ uploadedPartsCh <- uploadedPartRes{
Size: verifyObjPart.Size, Size: verifyObjPart.Size,
PartNum: partNumber, PartNum: uploadReq.PartNum,
Part: uploadReq.Part,
Error: nil, Error: nil,
} }
} }
@@ -210,8 +210,9 @@ func (c Client) putObjectMultipartFromReadAt(bucketName, objectName string, read
return totalUploadedSize, uploadRes.Error return totalUploadedSize, uploadRes.Error
} }
// Retrieve each uploaded part and store it to be completed. // Retrieve each uploaded part and store it to be completed.
part, ok := partsInfo[uploadRes.PartNum] // part, ok := partsInfo[uploadRes.PartNum]
if !ok { part := uploadRes.Part
if part == nil {
return 0, ErrInvalidArgument(fmt.Sprintf("Missing part number %d", uploadRes.PartNum)) return 0, ErrInvalidArgument(fmt.Sprintf("Missing part number %d", uploadRes.PartNum))
} }
// Update the totalUploadedSize. // Update the totalUploadedSize.

46
vendor/github.com/minio/minio-go/api-put-object.go сгенерированный поставляемый
Просмотреть файл

@@ -103,11 +103,10 @@ func getReaderSize(reader io.Reader) (size int64, err error) {
// implement Seekable calls. Ignore them and treat // implement Seekable calls. Ignore them and treat
// them like a stream with unknown length. // them like a stream with unknown length.
switch st.Name() { switch st.Name() {
case "stdin": case "stdin", "stdout", "stderr":
fallthrough return
case "stdout": // Ignore read/write stream of os.Pipe() which have unknown length too.
fallthrough case "|0", "|1":
case "stderr":
return return
} }
size = st.Size() size = st.Size()
@@ -151,7 +150,7 @@ func (c Client) PutObject(bucketName, objectName string, reader io.Reader, conte
// putObjectNoChecksum special function used Google Cloud Storage. This special function // putObjectNoChecksum special function used Google Cloud Storage. This special function
// is used for Google Cloud Storage since Google's multipart API is not S3 compatible. // is used for Google Cloud Storage since Google's multipart API is not S3 compatible.
func (c Client) putObjectNoChecksum(bucketName, objectName string, reader io.Reader, size int64, contentType string, progress io.Reader) (n int64, err error) { func (c Client) putObjectNoChecksum(bucketName, objectName string, reader io.Reader, size int64, metaData map[string][]string, progress io.Reader) (n int64, err error) {
// Input validation. // Input validation.
if err := isValidBucketName(bucketName); err != nil { if err := isValidBucketName(bucketName); err != nil {
return 0, err return 0, err
@@ -169,7 +168,7 @@ func (c Client) putObjectNoChecksum(bucketName, objectName string, reader io.Rea
// This function does not calculate sha256 and md5sum for payload. // This function does not calculate sha256 and md5sum for payload.
// Execute put object. // Execute put object.
st, err := c.putObjectDo(bucketName, objectName, readSeeker, nil, nil, size, contentType) st, err := c.putObjectDo(bucketName, objectName, readSeeker, nil, nil, size, metaData)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -181,7 +180,7 @@ func (c Client) putObjectNoChecksum(bucketName, objectName string, reader io.Rea
// putObjectSingle is a special function for uploading single put object request. // putObjectSingle is a special function for uploading single put object request.
// This special function is used as a fallback when multipart upload fails. // This special function is used as a fallback when multipart upload fails.
func (c Client) putObjectSingle(bucketName, objectName string, reader io.Reader, size int64, contentType string, progress io.Reader) (n int64, err error) { func (c Client) putObjectSingle(bucketName, objectName string, reader io.Reader, size int64, metaData map[string][]string, progress io.Reader) (n int64, err error) {
// Input validation. // Input validation.
if err := isValidBucketName(bucketName); err != nil { if err := isValidBucketName(bucketName); err != nil {
return 0, err return 0, err
@@ -237,7 +236,7 @@ func (c Client) putObjectSingle(bucketName, objectName string, reader io.Reader,
} }
} }
// Execute put object. // Execute put object.
st, err := c.putObjectDo(bucketName, objectName, reader, hashSums["md5"], hashSums["sha256"], size, contentType) st, err := c.putObjectDo(bucketName, objectName, reader, hashSums["md5"], hashSums["sha256"], size, metaData)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -255,7 +254,7 @@ func (c Client) putObjectSingle(bucketName, objectName string, reader io.Reader,
// putObjectDo - executes the put object http operation. // putObjectDo - executes the put object http operation.
// NOTE: You must have WRITE permissions on a bucket to add an object to it. // NOTE: You must have WRITE permissions on a bucket to add an object to it.
func (c Client) putObjectDo(bucketName, objectName string, reader io.Reader, md5Sum []byte, sha256Sum []byte, size int64, contentType string) (ObjectInfo, error) { func (c Client) putObjectDo(bucketName, objectName string, reader io.Reader, md5Sum []byte, sha256Sum []byte, size int64, metaData map[string][]string) (ObjectInfo, error) {
// Input validation. // Input validation.
if err := isValidBucketName(bucketName); err != nil { if err := isValidBucketName(bucketName); err != nil {
return ObjectInfo{}, err return ObjectInfo{}, err
@@ -272,13 +271,20 @@ func (c Client) putObjectDo(bucketName, objectName string, reader io.Reader, md5
return ObjectInfo{}, ErrEntityTooLarge(size, maxSinglePutObjectSize, bucketName, objectName) return ObjectInfo{}, ErrEntityTooLarge(size, maxSinglePutObjectSize, bucketName, objectName)
} }
if strings.TrimSpace(contentType) == "" {
contentType = "application/octet-stream"
}
// Set headers. // Set headers.
customHeader := make(http.Header) customHeader := make(http.Header)
customHeader.Set("Content-Type", contentType)
// Set metadata to headers
for k, v := range metaData {
if len(v) > 0 {
customHeader.Set(k, v[0])
}
}
// If Content-Type is not provided, set the default application/octet-stream one
if v, ok := metaData["Content-Type"]; !ok || len(v) == 0 {
customHeader.Set("Content-Type", "application/octet-stream")
}
// Populate request metadata. // Populate request metadata.
reqMetadata := requestMetadata{ reqMetadata := requestMetadata{
@@ -303,13 +309,13 @@ func (c Client) putObjectDo(bucketName, objectName string, reader io.Reader, md5
} }
} }
var metadata ObjectInfo var objInfo ObjectInfo
// Trim off the odd double quotes from ETag in the beginning and end. // Trim off the odd double quotes from ETag in the beginning and end.
metadata.ETag = strings.TrimPrefix(resp.Header.Get("ETag"), "\"") objInfo.ETag = strings.TrimPrefix(resp.Header.Get("ETag"), "\"")
metadata.ETag = strings.TrimSuffix(metadata.ETag, "\"") objInfo.ETag = strings.TrimSuffix(objInfo.ETag, "\"")
// A success here means data was written to server successfully. // A success here means data was written to server successfully.
metadata.Size = size objInfo.Size = size
// Return here. // Return here.
return metadata, nil return objInfo, nil
} }

11
vendor/github.com/minio/minio-go/api-remove.go сгенерированный поставляемый
Просмотреть файл

@@ -71,6 +71,13 @@ func (c Client) RemoveObject(bucketName, objectName string) error {
if err != nil { if err != nil {
return err return err
} }
if resp != nil {
// if some unexpected error happened and max retry is reached, we want to let client know
if resp.StatusCode != http.StatusNoContent {
return httpRespToErrorResponse(resp, bucketName, objectName)
}
}
// DeleteObject always responds with http '204' even for // DeleteObject always responds with http '204' even for
// objects which do not exist. So no need to handle them // objects which do not exist. So no need to handle them
// specifically. // specifically.
@@ -164,6 +171,10 @@ func (c Client) RemoveObjects(bucketName string, objectsCh <-chan string) <-chan
break break
} }
} }
if count == 0 {
// Multi Objects Delete API doesn't accept empty object list, quit immediatly
break
}
if count < maxEntries { if count < maxEntries {
// We didn't have 1000 entries, so this is the last batch // We didn't have 1000 entries, so this is the last batch
finish = true finish = true

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше