mirror of
https://github.com/gomods/athens
synced 2026-02-03 11:00:32 +00:00
update redigo (#266)
* dep ensure -v -update github.com/gocraft/work * rm -rf vendor/github.com/garyburd * sed /garyburd/redigo / gomodule/redigo * dont constrain redigo version * update redigo ver * rm deps * update deps * add vendor
This commit is contained in:
committed by
Rob j Loranger
parent
1aaacc7b59
commit
94bbcd1996
Generated
+426
-161
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -94,8 +94,8 @@
|
||||
name = "github.com/gobuffalo/gocraft-work-adapter"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/garyburd/redigo"
|
||||
version = "1.6.0"
|
||||
name = "github.com/gomodule/redigo"
|
||||
version = "2.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "cloud.google.com/go"
|
||||
|
||||
+1
-1
@@ -7,6 +7,6 @@ In no particular order:
|
||||
- [Buffalo](https://gobuffalo.io)
|
||||
- [mgo](https://github.com/globalsign/mgo)
|
||||
- [Testify](https://github.com/stretchr/testify)
|
||||
- [redigo](https://github.com/garyburd/redigo)
|
||||
- [redigo](https://github.com/gomodule/redigo)
|
||||
- [Protocol Buffers](https://github.com/golang/protobuf)
|
||||
- [Gocraft](https://github.com/gocraft/work)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"github.com/gobuffalo/buffalo"
|
||||
"github.com/gobuffalo/buffalo/middleware"
|
||||
"github.com/gobuffalo/buffalo/middleware/csrf"
|
||||
@@ -17,6 +16,7 @@ import (
|
||||
"github.com/gomods/athens/pkg/eventlog"
|
||||
"github.com/gomods/athens/pkg/log"
|
||||
"github.com/gomods/athens/pkg/storage"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/rs/cors"
|
||||
"github.com/unrolled/secure"
|
||||
)
|
||||
|
||||
@@ -3,7 +3,6 @@ package actions
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"github.com/gobuffalo/buffalo"
|
||||
"github.com/gobuffalo/buffalo/middleware"
|
||||
"github.com/gobuffalo/buffalo/middleware/csrf"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
"github.com/gomods/athens/pkg/module"
|
||||
"github.com/gomods/athens/pkg/storage"
|
||||
"github.com/gomods/athens/pkg/user"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/rs/cors"
|
||||
"github.com/unrolled/secure"
|
||||
)
|
||||
|
||||
Generated
Vendored
+67
-67
@@ -1,67 +1,67 @@
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPAccessConditions identifies standard HTTP access conditions which you optionally set.
|
||||
type HTTPAccessConditions struct {
|
||||
IfModifiedSince time.Time
|
||||
IfUnmodifiedSince time.Time
|
||||
IfMatch ETag
|
||||
IfNoneMatch ETag
|
||||
}
|
||||
|
||||
// pointers is for internal infrastructure. It returns the fields as pointers.
|
||||
func (ac HTTPAccessConditions) pointers() (ims *time.Time, ius *time.Time, ime *ETag, inme *ETag) {
|
||||
if !ac.IfModifiedSince.IsZero() {
|
||||
ims = &ac.IfModifiedSince
|
||||
}
|
||||
if !ac.IfUnmodifiedSince.IsZero() {
|
||||
ius = &ac.IfUnmodifiedSince
|
||||
}
|
||||
if ac.IfMatch != ETagNone {
|
||||
ime = &ac.IfMatch
|
||||
}
|
||||
if ac.IfNoneMatch != ETagNone {
|
||||
inme = &ac.IfNoneMatch
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ContainerAccessConditions identifies container-specific access conditions which you optionally set.
|
||||
type ContainerAccessConditions struct {
|
||||
HTTPAccessConditions
|
||||
LeaseAccessConditions
|
||||
}
|
||||
|
||||
// BlobAccessConditions identifies blob-specific access conditions which you optionally set.
|
||||
type BlobAccessConditions struct {
|
||||
HTTPAccessConditions
|
||||
LeaseAccessConditions
|
||||
AppendBlobAccessConditions
|
||||
PageBlobAccessConditions
|
||||
}
|
||||
|
||||
// LeaseAccessConditions identifies lease access conditions for a container or blob which you optionally set.
|
||||
type LeaseAccessConditions struct {
|
||||
LeaseID string
|
||||
}
|
||||
|
||||
// pointers is for internal infrastructure. It returns the fields as pointers.
|
||||
func (ac LeaseAccessConditions) pointers() (leaseID *string) {
|
||||
if ac.LeaseID != "" {
|
||||
leaseID = &ac.LeaseID
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
// getInt32 is for internal infrastructure. It is used with access condition values where
|
||||
// 0 (the default setting) is meaningful. The library interprets 0 as do not send the header
|
||||
// and the privately-storage field in the access condition object is stored as +1 higher than desired.
|
||||
// THis method returns true, if the value is > 0 (explicitly set) and the stored value - 1 (the set desired value).
|
||||
func getInt32(value int32) (bool, int32) {
|
||||
return value > 0, value - 1
|
||||
}
|
||||
*/
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPAccessConditions identifies standard HTTP access conditions which you optionally set.
|
||||
type HTTPAccessConditions struct {
|
||||
IfModifiedSince time.Time
|
||||
IfUnmodifiedSince time.Time
|
||||
IfMatch ETag
|
||||
IfNoneMatch ETag
|
||||
}
|
||||
|
||||
// pointers is for internal infrastructure. It returns the fields as pointers.
|
||||
func (ac HTTPAccessConditions) pointers() (ims *time.Time, ius *time.Time, ime *ETag, inme *ETag) {
|
||||
if !ac.IfModifiedSince.IsZero() {
|
||||
ims = &ac.IfModifiedSince
|
||||
}
|
||||
if !ac.IfUnmodifiedSince.IsZero() {
|
||||
ius = &ac.IfUnmodifiedSince
|
||||
}
|
||||
if ac.IfMatch != ETagNone {
|
||||
ime = &ac.IfMatch
|
||||
}
|
||||
if ac.IfNoneMatch != ETagNone {
|
||||
inme = &ac.IfNoneMatch
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ContainerAccessConditions identifies container-specific access conditions which you optionally set.
|
||||
type ContainerAccessConditions struct {
|
||||
HTTPAccessConditions
|
||||
LeaseAccessConditions
|
||||
}
|
||||
|
||||
// BlobAccessConditions identifies blob-specific access conditions which you optionally set.
|
||||
type BlobAccessConditions struct {
|
||||
HTTPAccessConditions
|
||||
LeaseAccessConditions
|
||||
AppendBlobAccessConditions
|
||||
PageBlobAccessConditions
|
||||
}
|
||||
|
||||
// LeaseAccessConditions identifies lease access conditions for a container or blob which you optionally set.
|
||||
type LeaseAccessConditions struct {
|
||||
LeaseID string
|
||||
}
|
||||
|
||||
// pointers is for internal infrastructure. It returns the fields as pointers.
|
||||
func (ac LeaseAccessConditions) pointers() (leaseID *string) {
|
||||
if ac.LeaseID != "" {
|
||||
leaseID = &ac.LeaseID
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
// getInt32 is for internal infrastructure. It is used with access condition values where
|
||||
// 0 (the default setting) is meaningful. The library interprets 0 as do not send the header
|
||||
// and the privately-storage field in the access condition object is stored as +1 higher than desired.
|
||||
// THis method returns true, if the value is > 0 (explicitly set) and the stored value - 1 (the set desired value).
|
||||
func getInt32(value int32) (bool, int32) {
|
||||
return value > 0, value - 1
|
||||
}
|
||||
*/
|
||||
|
||||
+206
-206
@@ -1,206 +1,206 @@
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BlobSASSignatureValues is used to generate a Shared Access Signature (SAS) for an Azure Storage container or blob.
|
||||
type BlobSASSignatureValues struct {
|
||||
Version string `param:"sv"` // If not specified, this defaults to SASVersion
|
||||
Protocol SASProtocol `param:"spr"` // See the SASProtocol* constants
|
||||
StartTime time.Time `param:"st"` // Not specified if IsZero
|
||||
ExpiryTime time.Time `param:"se"` // Not specified if IsZero
|
||||
Permissions string `param:"sp"` // Create by initializing a ContainerSASPermissions or BlobSASPermissions and then call String()
|
||||
IPRange IPRange `param:"sip"`
|
||||
Identifier string `param:"si"`
|
||||
ContainerName string
|
||||
BlobName string // Use "" to create a Container SAS
|
||||
CacheControl string // rscc
|
||||
ContentDisposition string // rscd
|
||||
ContentEncoding string // rsce
|
||||
ContentLanguage string // rscl
|
||||
ContentType string // rsct
|
||||
}
|
||||
|
||||
// NewSASQueryParameters uses an account's shared key credential to sign this signature values to produce
|
||||
// the proper SAS query parameters.
|
||||
func (v BlobSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *SharedKeyCredential) SASQueryParameters {
|
||||
if sharedKeyCredential == nil {
|
||||
panic("sharedKeyCredential can't be nil")
|
||||
}
|
||||
|
||||
resource := "c"
|
||||
if v.BlobName == "" {
|
||||
// Make sure the permission characters are in the correct order
|
||||
perms := &ContainerSASPermissions{}
|
||||
if err := perms.Parse(v.Permissions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
v.Permissions = perms.String()
|
||||
} else {
|
||||
resource = "b"
|
||||
// Make sure the permission characters are in the correct order
|
||||
perms := &BlobSASPermissions{}
|
||||
if err := perms.Parse(v.Permissions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
v.Permissions = perms.String()
|
||||
}
|
||||
if v.Version == "" {
|
||||
v.Version = SASVersion
|
||||
}
|
||||
startTime, expiryTime := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime)
|
||||
|
||||
// String to sign: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
|
||||
stringToSign := strings.Join([]string{
|
||||
v.Permissions,
|
||||
startTime,
|
||||
expiryTime,
|
||||
getCanonicalName(sharedKeyCredential.AccountName(), v.ContainerName, v.BlobName),
|
||||
v.Identifier,
|
||||
v.IPRange.String(),
|
||||
string(v.Protocol),
|
||||
v.Version,
|
||||
v.CacheControl, // rscc
|
||||
v.ContentDisposition, // rscd
|
||||
v.ContentEncoding, // rsce
|
||||
v.ContentLanguage, // rscl
|
||||
v.ContentType}, // rsct
|
||||
"\n")
|
||||
signature := sharedKeyCredential.ComputeHMACSHA256(stringToSign)
|
||||
|
||||
p := SASQueryParameters{
|
||||
// Common SAS parameters
|
||||
version: v.Version,
|
||||
protocol: v.Protocol,
|
||||
startTime: v.StartTime,
|
||||
expiryTime: v.ExpiryTime,
|
||||
permissions: v.Permissions,
|
||||
ipRange: v.IPRange,
|
||||
|
||||
// Container/Blob-specific SAS parameters
|
||||
resource: resource,
|
||||
identifier: v.Identifier,
|
||||
|
||||
// Calculated SAS signature
|
||||
signature: signature,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// getCanonicalName computes the canonical name for a container or blob resource for SAS signing.
|
||||
func getCanonicalName(account string, containerName string, blobName string) string {
|
||||
// Container: "/blob/account/containername"
|
||||
// Blob: "/blob/account/containername/blobname"
|
||||
elements := []string{"/blob/", account, "/", containerName}
|
||||
if blobName != "" {
|
||||
elements = append(elements, "/", strings.Replace(blobName, "\\", "/", -1))
|
||||
}
|
||||
return strings.Join(elements, "")
|
||||
}
|
||||
|
||||
// The ContainerSASPermissions type simplifies creating the permissions string for an Azure Storage container SAS.
|
||||
// Initialize an instance of this type and then call its String method to set BlobSASSignatureValues's Permissions field.
|
||||
type ContainerSASPermissions struct {
|
||||
Read, Add, Create, Write, Delete, List bool
|
||||
}
|
||||
|
||||
// String produces the SAS permissions string for an Azure Storage container.
|
||||
// Call this method to set BlobSASSignatureValues's Permissions field.
|
||||
func (p ContainerSASPermissions) String() string {
|
||||
var b bytes.Buffer
|
||||
if p.Read {
|
||||
b.WriteRune('r')
|
||||
}
|
||||
if p.Add {
|
||||
b.WriteRune('a')
|
||||
}
|
||||
if p.Create {
|
||||
b.WriteRune('c')
|
||||
}
|
||||
if p.Write {
|
||||
b.WriteRune('w')
|
||||
}
|
||||
if p.Delete {
|
||||
b.WriteRune('d')
|
||||
}
|
||||
if p.List {
|
||||
b.WriteRune('l')
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Parse initializes the ContainerSASPermissions's fields from a string.
|
||||
func (p *ContainerSASPermissions) Parse(s string) error {
|
||||
*p = ContainerSASPermissions{} // Clear the flags
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case 'r':
|
||||
p.Read = true
|
||||
case 'a':
|
||||
p.Add = true
|
||||
case 'c':
|
||||
p.Create = true
|
||||
case 'w':
|
||||
p.Write = true
|
||||
case 'd':
|
||||
p.Delete = true
|
||||
case 'l':
|
||||
p.List = true
|
||||
default:
|
||||
return fmt.Errorf("Invalid permission: '%v'", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The BlobSASPermissions type simplifies creating the permissions string for an Azure Storage blob SAS.
|
||||
// Initialize an instance of this type and then call its String method to set BlobSASSignatureValues's Permissions field.
|
||||
type BlobSASPermissions struct{ Read, Add, Create, Write, Delete bool }
|
||||
|
||||
// String produces the SAS permissions string for an Azure Storage blob.
|
||||
// Call this method to set BlobSASSignatureValues's Permissions field.
|
||||
func (p BlobSASPermissions) String() string {
|
||||
var b bytes.Buffer
|
||||
if p.Read {
|
||||
b.WriteRune('r')
|
||||
}
|
||||
if p.Add {
|
||||
b.WriteRune('a')
|
||||
}
|
||||
if p.Create {
|
||||
b.WriteRune('c')
|
||||
}
|
||||
if p.Write {
|
||||
b.WriteRune('w')
|
||||
}
|
||||
if p.Delete {
|
||||
b.WriteRune('d')
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Parse initializes the BlobSASPermissions's fields from a string.
|
||||
func (p *BlobSASPermissions) Parse(s string) error {
|
||||
*p = BlobSASPermissions{} // Clear the flags
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case 'r':
|
||||
p.Read = true
|
||||
case 'a':
|
||||
p.Add = true
|
||||
case 'c':
|
||||
p.Create = true
|
||||
case 'w':
|
||||
p.Write = true
|
||||
case 'd':
|
||||
p.Delete = true
|
||||
default:
|
||||
return fmt.Errorf("Invalid permission: '%v'", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BlobSASSignatureValues is used to generate a Shared Access Signature (SAS) for an Azure Storage container or blob.
|
||||
type BlobSASSignatureValues struct {
|
||||
Version string `param:"sv"` // If not specified, this defaults to SASVersion
|
||||
Protocol SASProtocol `param:"spr"` // See the SASProtocol* constants
|
||||
StartTime time.Time `param:"st"` // Not specified if IsZero
|
||||
ExpiryTime time.Time `param:"se"` // Not specified if IsZero
|
||||
Permissions string `param:"sp"` // Create by initializing a ContainerSASPermissions or BlobSASPermissions and then call String()
|
||||
IPRange IPRange `param:"sip"`
|
||||
Identifier string `param:"si"`
|
||||
ContainerName string
|
||||
BlobName string // Use "" to create a Container SAS
|
||||
CacheControl string // rscc
|
||||
ContentDisposition string // rscd
|
||||
ContentEncoding string // rsce
|
||||
ContentLanguage string // rscl
|
||||
ContentType string // rsct
|
||||
}
|
||||
|
||||
// NewSASQueryParameters uses an account's shared key credential to sign this signature values to produce
|
||||
// the proper SAS query parameters.
|
||||
func (v BlobSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *SharedKeyCredential) SASQueryParameters {
|
||||
if sharedKeyCredential == nil {
|
||||
panic("sharedKeyCredential can't be nil")
|
||||
}
|
||||
|
||||
resource := "c"
|
||||
if v.BlobName == "" {
|
||||
// Make sure the permission characters are in the correct order
|
||||
perms := &ContainerSASPermissions{}
|
||||
if err := perms.Parse(v.Permissions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
v.Permissions = perms.String()
|
||||
} else {
|
||||
resource = "b"
|
||||
// Make sure the permission characters are in the correct order
|
||||
perms := &BlobSASPermissions{}
|
||||
if err := perms.Parse(v.Permissions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
v.Permissions = perms.String()
|
||||
}
|
||||
if v.Version == "" {
|
||||
v.Version = SASVersion
|
||||
}
|
||||
startTime, expiryTime := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime)
|
||||
|
||||
// String to sign: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
|
||||
stringToSign := strings.Join([]string{
|
||||
v.Permissions,
|
||||
startTime,
|
||||
expiryTime,
|
||||
getCanonicalName(sharedKeyCredential.AccountName(), v.ContainerName, v.BlobName),
|
||||
v.Identifier,
|
||||
v.IPRange.String(),
|
||||
string(v.Protocol),
|
||||
v.Version,
|
||||
v.CacheControl, // rscc
|
||||
v.ContentDisposition, // rscd
|
||||
v.ContentEncoding, // rsce
|
||||
v.ContentLanguage, // rscl
|
||||
v.ContentType}, // rsct
|
||||
"\n")
|
||||
signature := sharedKeyCredential.ComputeHMACSHA256(stringToSign)
|
||||
|
||||
p := SASQueryParameters{
|
||||
// Common SAS parameters
|
||||
version: v.Version,
|
||||
protocol: v.Protocol,
|
||||
startTime: v.StartTime,
|
||||
expiryTime: v.ExpiryTime,
|
||||
permissions: v.Permissions,
|
||||
ipRange: v.IPRange,
|
||||
|
||||
// Container/Blob-specific SAS parameters
|
||||
resource: resource,
|
||||
identifier: v.Identifier,
|
||||
|
||||
// Calculated SAS signature
|
||||
signature: signature,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// getCanonicalName computes the canonical name for a container or blob resource for SAS signing.
|
||||
func getCanonicalName(account string, containerName string, blobName string) string {
|
||||
// Container: "/blob/account/containername"
|
||||
// Blob: "/blob/account/containername/blobname"
|
||||
elements := []string{"/blob/", account, "/", containerName}
|
||||
if blobName != "" {
|
||||
elements = append(elements, "/", strings.Replace(blobName, "\\", "/", -1))
|
||||
}
|
||||
return strings.Join(elements, "")
|
||||
}
|
||||
|
||||
// The ContainerSASPermissions type simplifies creating the permissions string for an Azure Storage container SAS.
|
||||
// Initialize an instance of this type and then call its String method to set BlobSASSignatureValues's Permissions field.
|
||||
type ContainerSASPermissions struct {
|
||||
Read, Add, Create, Write, Delete, List bool
|
||||
}
|
||||
|
||||
// String produces the SAS permissions string for an Azure Storage container.
|
||||
// Call this method to set BlobSASSignatureValues's Permissions field.
|
||||
func (p ContainerSASPermissions) String() string {
|
||||
var b bytes.Buffer
|
||||
if p.Read {
|
||||
b.WriteRune('r')
|
||||
}
|
||||
if p.Add {
|
||||
b.WriteRune('a')
|
||||
}
|
||||
if p.Create {
|
||||
b.WriteRune('c')
|
||||
}
|
||||
if p.Write {
|
||||
b.WriteRune('w')
|
||||
}
|
||||
if p.Delete {
|
||||
b.WriteRune('d')
|
||||
}
|
||||
if p.List {
|
||||
b.WriteRune('l')
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Parse initializes the ContainerSASPermissions's fields from a string.
|
||||
func (p *ContainerSASPermissions) Parse(s string) error {
|
||||
*p = ContainerSASPermissions{} // Clear the flags
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case 'r':
|
||||
p.Read = true
|
||||
case 'a':
|
||||
p.Add = true
|
||||
case 'c':
|
||||
p.Create = true
|
||||
case 'w':
|
||||
p.Write = true
|
||||
case 'd':
|
||||
p.Delete = true
|
||||
case 'l':
|
||||
p.List = true
|
||||
default:
|
||||
return fmt.Errorf("Invalid permission: '%v'", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The BlobSASPermissions type simplifies creating the permissions string for an Azure Storage blob SAS.
|
||||
// Initialize an instance of this type and then call its String method to set BlobSASSignatureValues's Permissions field.
|
||||
type BlobSASPermissions struct{ Read, Add, Create, Write, Delete bool }
|
||||
|
||||
// String produces the SAS permissions string for an Azure Storage blob.
|
||||
// Call this method to set BlobSASSignatureValues's Permissions field.
|
||||
func (p BlobSASPermissions) String() string {
|
||||
var b bytes.Buffer
|
||||
if p.Read {
|
||||
b.WriteRune('r')
|
||||
}
|
||||
if p.Add {
|
||||
b.WriteRune('a')
|
||||
}
|
||||
if p.Create {
|
||||
b.WriteRune('c')
|
||||
}
|
||||
if p.Write {
|
||||
b.WriteRune('w')
|
||||
}
|
||||
if p.Delete {
|
||||
b.WriteRune('d')
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Parse initializes the BlobSASPermissions's fields from a string.
|
||||
func (p *BlobSASPermissions) Parse(s string) error {
|
||||
*p = BlobSASPermissions{} // Clear the flags
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case 'r':
|
||||
p.Read = true
|
||||
case 'a':
|
||||
p.Add = true
|
||||
case 'c':
|
||||
p.Create = true
|
||||
case 'w':
|
||||
p.Write = true
|
||||
case 'd':
|
||||
p.Delete = true
|
||||
default:
|
||||
return fmt.Errorf("Invalid permission: '%v'", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Generated
Vendored
+195
-195
@@ -1,195 +1,195 @@
|
||||
package azblob
|
||||
|
||||
// https://docs.microsoft.com/en-us/rest/api/storageservices/blob-service-error-codes
|
||||
|
||||
// ServiceCode values indicate a service failure.
|
||||
const (
|
||||
// ServiceCodeAppendPositionConditionNotMet means the append position condition specified was not met.
|
||||
ServiceCodeAppendPositionConditionNotMet ServiceCodeType = "AppendPositionConditionNotMet"
|
||||
|
||||
// ServiceCodeBlobAlreadyExists means the specified blob already exists.
|
||||
ServiceCodeBlobAlreadyExists ServiceCodeType = "BlobAlreadyExists"
|
||||
|
||||
// ServiceCodeBlobNotFound means the specified blob does not exist.
|
||||
ServiceCodeBlobNotFound ServiceCodeType = "BlobNotFound"
|
||||
|
||||
// ServiceCodeBlobOverwritten means the blob has been recreated since the previous snapshot was taken.
|
||||
ServiceCodeBlobOverwritten ServiceCodeType = "BlobOverwritten"
|
||||
|
||||
// ServiceCodeBlobTierInadequateForContentLength means the specified blob tier size limit cannot be less than content length.
|
||||
ServiceCodeBlobTierInadequateForContentLength ServiceCodeType = "BlobTierInadequateForContentLength"
|
||||
|
||||
// ServiceCodeBlockCountExceedsLimit means the committed block count cannot exceed the maximum limit of 50,000 blocks
|
||||
// or that the uncommitted block count cannot exceed the maximum limit of 100,000 blocks.
|
||||
ServiceCodeBlockCountExceedsLimit ServiceCodeType = "BlockCountExceedsLimit"
|
||||
|
||||
// ServiceCodeBlockListTooLong means the block list may not contain more than 50,000 blocks.
|
||||
ServiceCodeBlockListTooLong ServiceCodeType = "BlockListTooLong"
|
||||
|
||||
// ServiceCodeCannotChangeToLowerTier means that a higher blob tier has already been explicitly set.
|
||||
ServiceCodeCannotChangeToLowerTier ServiceCodeType = "CannotChangeToLowerTier"
|
||||
|
||||
// ServiceCodeCannotVerifyCopySource means that the service could not verify the copy source within the specified time.
|
||||
// Examine the HTTP status code and message for more information about the failure.
|
||||
ServiceCodeCannotVerifyCopySource ServiceCodeType = "CannotVerifyCopySource"
|
||||
|
||||
// ServiceCodeContainerAlreadyExists means the specified container already exists.
|
||||
ServiceCodeContainerAlreadyExists ServiceCodeType = "ContainerAlreadyExists"
|
||||
|
||||
// ServiceCodeContainerBeingDeleted means the specified container is being deleted.
|
||||
ServiceCodeContainerBeingDeleted ServiceCodeType = "ContainerBeingDeleted"
|
||||
|
||||
// ServiceCodeContainerDisabled means the specified container has been disabled by the administrator.
|
||||
ServiceCodeContainerDisabled ServiceCodeType = "ContainerDisabled"
|
||||
|
||||
// ServiceCodeContainerNotFound means the specified container does not exist.
|
||||
ServiceCodeContainerNotFound ServiceCodeType = "ContainerNotFound"
|
||||
|
||||
// ServiceCodeContentLengthLargerThanTierLimit means the blob's content length cannot exceed its tier limit.
|
||||
ServiceCodeContentLengthLargerThanTierLimit ServiceCodeType = "ContentLengthLargerThanTierLimit"
|
||||
|
||||
// ServiceCodeCopyAcrossAccountsNotSupported means the copy source account and destination account must be the same.
|
||||
ServiceCodeCopyAcrossAccountsNotSupported ServiceCodeType = "CopyAcrossAccountsNotSupported"
|
||||
|
||||
// ServiceCodeCopyIDMismatch means the specified copy ID did not match the copy ID for the pending copy operation.
|
||||
ServiceCodeCopyIDMismatch ServiceCodeType = "CopyIdMismatch"
|
||||
|
||||
// ServiceCodeFeatureVersionMismatch means the type of blob in the container is unrecognized by this version or
|
||||
// that the operation for AppendBlob requires at least version 2015-02-21.
|
||||
ServiceCodeFeatureVersionMismatch ServiceCodeType = "FeatureVersionMismatch"
|
||||
|
||||
// ServiceCodeIncrementalCopyBlobMismatch means the specified source blob is different than the copy source of the existing incremental copy blob.
|
||||
ServiceCodeIncrementalCopyBlobMismatch ServiceCodeType = "IncrementalCopyBlobMismatch"
|
||||
|
||||
// ServiceCodeIncrementalCopyOfEralierVersionSnapshotNotAllowed means the specified snapshot is earlier than the last snapshot copied into the incremental copy blob.
|
||||
ServiceCodeIncrementalCopyOfEralierVersionSnapshotNotAllowed ServiceCodeType = "IncrementalCopyOfEralierVersionSnapshotNotAllowed"
|
||||
|
||||
// ServiceCodeIncrementalCopySourceMustBeSnapshot means the source for incremental copy request must be a snapshot.
|
||||
ServiceCodeIncrementalCopySourceMustBeSnapshot ServiceCodeType = "IncrementalCopySourceMustBeSnapshot"
|
||||
|
||||
// ServiceCodeInfiniteLeaseDurationRequired means the lease ID matched, but the specified lease must be an infinite-duration lease.
|
||||
ServiceCodeInfiniteLeaseDurationRequired ServiceCodeType = "InfiniteLeaseDurationRequired"
|
||||
|
||||
// ServiceCodeInvalidBlobOrBlock means the specified blob or block content is invalid.
|
||||
ServiceCodeInvalidBlobOrBlock ServiceCodeType = "InvalidBlobOrBlock"
|
||||
|
||||
// ServiceCodeInvalidBlobType means the blob type is invalid for this operation.
|
||||
ServiceCodeInvalidBlobType ServiceCodeType = "InvalidBlobType"
|
||||
|
||||
// ServiceCodeInvalidBlockID means the specified block ID is invalid. The block ID must be Base64-encoded.
|
||||
ServiceCodeInvalidBlockID ServiceCodeType = "InvalidBlockId"
|
||||
|
||||
// ServiceCodeInvalidBlockList means the specified block list is invalid.
|
||||
ServiceCodeInvalidBlockList ServiceCodeType = "InvalidBlockList"
|
||||
|
||||
// ServiceCodeInvalidOperation means an invalid operation against a blob snapshot.
|
||||
ServiceCodeInvalidOperation ServiceCodeType = "InvalidOperation"
|
||||
|
||||
// ServiceCodeInvalidPageRange means the page range specified is invalid.
|
||||
ServiceCodeInvalidPageRange ServiceCodeType = "InvalidPageRange"
|
||||
|
||||
// ServiceCodeInvalidSourceBlobType means the copy source blob type is invalid for this operation.
|
||||
ServiceCodeInvalidSourceBlobType ServiceCodeType = "InvalidSourceBlobType"
|
||||
|
||||
// ServiceCodeInvalidSourceBlobURL means the source URL for incremental copy request must be valid Azure Storage blob URL.
|
||||
ServiceCodeInvalidSourceBlobURL ServiceCodeType = "InvalidSourceBlobUrl"
|
||||
|
||||
// ServiceCodeInvalidVersionForPageBlobOperation means that all operations on page blobs require at least version 2009-09-19.
|
||||
ServiceCodeInvalidVersionForPageBlobOperation ServiceCodeType = "InvalidVersionForPageBlobOperation"
|
||||
|
||||
// ServiceCodeLeaseAlreadyPresent means there is already a lease present.
|
||||
ServiceCodeLeaseAlreadyPresent ServiceCodeType = "LeaseAlreadyPresent"
|
||||
|
||||
// ServiceCodeLeaseAlreadyBroken means the lease has already been broken and cannot be broken again.
|
||||
ServiceCodeLeaseAlreadyBroken ServiceCodeType = "LeaseAlreadyBroken"
|
||||
|
||||
// ServiceCodeLeaseIDMismatchWithBlobOperation means the lease ID specified did not match the lease ID for the blob.
|
||||
ServiceCodeLeaseIDMismatchWithBlobOperation ServiceCodeType = "LeaseIdMismatchWithBlobOperation"
|
||||
|
||||
// ServiceCodeLeaseIDMismatchWithContainerOperation means the lease ID specified did not match the lease ID for the container.
|
||||
ServiceCodeLeaseIDMismatchWithContainerOperation ServiceCodeType = "LeaseIdMismatchWithContainerOperation"
|
||||
|
||||
// ServiceCodeLeaseIDMismatchWithLeaseOperation means the lease ID specified did not match the lease ID for the blob/container.
|
||||
ServiceCodeLeaseIDMismatchWithLeaseOperation ServiceCodeType = "LeaseIdMismatchWithLeaseOperation"
|
||||
|
||||
// ServiceCodeLeaseIDMissing means there is currently a lease on the blob/container and no lease ID was specified in the request.
|
||||
ServiceCodeLeaseIDMissing ServiceCodeType = "LeaseIdMissing"
|
||||
|
||||
// ServiceCodeLeaseIsBreakingAndCannotBeAcquired means the lease ID matched, but the lease is currently in breaking state and cannot be acquired until it is broken.
|
||||
ServiceCodeLeaseIsBreakingAndCannotBeAcquired ServiceCodeType = "LeaseIsBreakingAndCannotBeAcquired"
|
||||
|
||||
// ServiceCodeLeaseIsBreakingAndCannotBeChanged means the lease ID matched, but the lease is currently in breaking state and cannot be changed.
|
||||
ServiceCodeLeaseIsBreakingAndCannotBeChanged ServiceCodeType = "LeaseIsBreakingAndCannotBeChanged"
|
||||
|
||||
// ServiceCodeLeaseIsBrokenAndCannotBeRenewed means the lease ID matched, but the lease has been broken explicitly and cannot be renewed.
|
||||
ServiceCodeLeaseIsBrokenAndCannotBeRenewed ServiceCodeType = "LeaseIsBrokenAndCannotBeRenewed"
|
||||
|
||||
// ServiceCodeLeaseLost means a lease ID was specified, but the lease for the blob/container has expired.
|
||||
ServiceCodeLeaseLost ServiceCodeType = "LeaseLost"
|
||||
|
||||
// ServiceCodeLeaseNotPresentWithBlobOperation means there is currently no lease on the blob.
|
||||
ServiceCodeLeaseNotPresentWithBlobOperation ServiceCodeType = "LeaseNotPresentWithBlobOperation"
|
||||
|
||||
// ServiceCodeLeaseNotPresentWithContainerOperation means there is currently no lease on the container.
|
||||
ServiceCodeLeaseNotPresentWithContainerOperation ServiceCodeType = "LeaseNotPresentWithContainerOperation"
|
||||
|
||||
// ServiceCodeLeaseNotPresentWithLeaseOperation means there is currently no lease on the blob/container.
|
||||
ServiceCodeLeaseNotPresentWithLeaseOperation ServiceCodeType = "LeaseNotPresentWithLeaseOperation"
|
||||
|
||||
// ServiceCodeMaxBlobSizeConditionNotMet means the max blob size condition specified was not met.
|
||||
ServiceCodeMaxBlobSizeConditionNotMet ServiceCodeType = "MaxBlobSizeConditionNotMet"
|
||||
|
||||
// ServiceCodeNoPendingCopyOperation means there is currently no pending copy operation.
|
||||
ServiceCodeNoPendingCopyOperation ServiceCodeType = "NoPendingCopyOperation"
|
||||
|
||||
// ServiceCodeOperationNotAllowedOnIncrementalCopyBlob means the specified operation is not allowed on an incremental copy blob.
|
||||
ServiceCodeOperationNotAllowedOnIncrementalCopyBlob ServiceCodeType = "OperationNotAllowedOnIncrementalCopyBlob"
|
||||
|
||||
// ServiceCodePendingCopyOperation means there is currently a pending copy operation.
|
||||
ServiceCodePendingCopyOperation ServiceCodeType = "PendingCopyOperation"
|
||||
|
||||
// ServiceCodePreviousSnapshotCannotBeNewer means the prevsnapshot query parameter value cannot be newer than snapshot query parameter value.
|
||||
ServiceCodePreviousSnapshotCannotBeNewer ServiceCodeType = "PreviousSnapshotCannotBeNewer"
|
||||
|
||||
// ServiceCodePreviousSnapshotNotFound means the previous snapshot is not found.
|
||||
ServiceCodePreviousSnapshotNotFound ServiceCodeType = "PreviousSnapshotNotFound"
|
||||
|
||||
// ServiceCodePreviousSnapshotOperationNotSupported means that differential Get Page Ranges is not supported on the previous snapshot.
|
||||
ServiceCodePreviousSnapshotOperationNotSupported ServiceCodeType = "PreviousSnapshotOperationNotSupported"
|
||||
|
||||
// ServiceCodeSequenceNumberConditionNotMet means the sequence number condition specified was not met.
|
||||
ServiceCodeSequenceNumberConditionNotMet ServiceCodeType = "SequenceNumberConditionNotMet"
|
||||
|
||||
// ServiceCodeSequenceNumberIncrementTooLarge means the sequence number increment cannot be performed because it would result in overflow of the sequence number.
|
||||
ServiceCodeSequenceNumberIncrementTooLarge ServiceCodeType = "SequenceNumberIncrementTooLarge"
|
||||
|
||||
// ServiceCodeSnapshotCountExceeded means the snapshot count against this blob has been exceeded.
|
||||
ServiceCodeSnapshotCountExceeded ServiceCodeType = "SnapshotCountExceeded"
|
||||
|
||||
// ServiceCodeSnaphotOperationRateExceeded means the rate of snapshot operations against this blob has been exceeded.
|
||||
ServiceCodeSnaphotOperationRateExceeded ServiceCodeType = "SnaphotOperationRateExceeded"
|
||||
|
||||
// ServiceCodeSnapshotsPresent means this operation is not permitted while the blob has snapshots.
|
||||
ServiceCodeSnapshotsPresent ServiceCodeType = "SnapshotsPresent"
|
||||
|
||||
// ServiceCodeSourceConditionNotMet means the source condition specified using HTTP conditional header(s) is not met.
|
||||
ServiceCodeSourceConditionNotMet ServiceCodeType = "SourceConditionNotMet"
|
||||
|
||||
// ServiceCodeSystemInUse means this blob is in use by the system.
|
||||
ServiceCodeSystemInUse ServiceCodeType = "SystemInUse"
|
||||
|
||||
// ServiceCodeTargetConditionNotMet means the target condition specified using HTTP conditional header(s) is not met.
|
||||
ServiceCodeTargetConditionNotMet ServiceCodeType = "TargetConditionNotMet"
|
||||
|
||||
// ServiceCodeUnauthorizedBlobOverwrite means this request is not authorized to perform blob overwrites.
|
||||
ServiceCodeUnauthorizedBlobOverwrite ServiceCodeType = "UnauthorizedBlobOverwrite"
|
||||
|
||||
// ServiceCodeBlobBeingRehydrated means this operation is not permitted because the blob is being rehydrated.
|
||||
ServiceCodeBlobBeingRehydrated ServiceCodeType = "BlobBeingRehydrated"
|
||||
|
||||
// ServiceCodeBlobArchived means this operation is not permitted on an archived blob.
|
||||
ServiceCodeBlobArchived ServiceCodeType = "BlobArchived"
|
||||
|
||||
// ServiceCodeBlobNotArchived means this blob is currently not in the archived state.
|
||||
ServiceCodeBlobNotArchived ServiceCodeType = "BlobNotArchived"
|
||||
)
|
||||
package azblob
|
||||
|
||||
// https://docs.microsoft.com/en-us/rest/api/storageservices/blob-service-error-codes
|
||||
|
||||
// ServiceCode values indicate a service failure.
|
||||
const (
|
||||
// ServiceCodeAppendPositionConditionNotMet means the append position condition specified was not met.
|
||||
ServiceCodeAppendPositionConditionNotMet ServiceCodeType = "AppendPositionConditionNotMet"
|
||||
|
||||
// ServiceCodeBlobAlreadyExists means the specified blob already exists.
|
||||
ServiceCodeBlobAlreadyExists ServiceCodeType = "BlobAlreadyExists"
|
||||
|
||||
// ServiceCodeBlobNotFound means the specified blob does not exist.
|
||||
ServiceCodeBlobNotFound ServiceCodeType = "BlobNotFound"
|
||||
|
||||
// ServiceCodeBlobOverwritten means the blob has been recreated since the previous snapshot was taken.
|
||||
ServiceCodeBlobOverwritten ServiceCodeType = "BlobOverwritten"
|
||||
|
||||
// ServiceCodeBlobTierInadequateForContentLength means the specified blob tier size limit cannot be less than content length.
|
||||
ServiceCodeBlobTierInadequateForContentLength ServiceCodeType = "BlobTierInadequateForContentLength"
|
||||
|
||||
// ServiceCodeBlockCountExceedsLimit means the committed block count cannot exceed the maximum limit of 50,000 blocks
|
||||
// or that the uncommitted block count cannot exceed the maximum limit of 100,000 blocks.
|
||||
ServiceCodeBlockCountExceedsLimit ServiceCodeType = "BlockCountExceedsLimit"
|
||||
|
||||
// ServiceCodeBlockListTooLong means the block list may not contain more than 50,000 blocks.
|
||||
ServiceCodeBlockListTooLong ServiceCodeType = "BlockListTooLong"
|
||||
|
||||
// ServiceCodeCannotChangeToLowerTier means that a higher blob tier has already been explicitly set.
|
||||
ServiceCodeCannotChangeToLowerTier ServiceCodeType = "CannotChangeToLowerTier"
|
||||
|
||||
// ServiceCodeCannotVerifyCopySource means that the service could not verify the copy source within the specified time.
|
||||
// Examine the HTTP status code and message for more information about the failure.
|
||||
ServiceCodeCannotVerifyCopySource ServiceCodeType = "CannotVerifyCopySource"
|
||||
|
||||
// ServiceCodeContainerAlreadyExists means the specified container already exists.
|
||||
ServiceCodeContainerAlreadyExists ServiceCodeType = "ContainerAlreadyExists"
|
||||
|
||||
// ServiceCodeContainerBeingDeleted means the specified container is being deleted.
|
||||
ServiceCodeContainerBeingDeleted ServiceCodeType = "ContainerBeingDeleted"
|
||||
|
||||
// ServiceCodeContainerDisabled means the specified container has been disabled by the administrator.
|
||||
ServiceCodeContainerDisabled ServiceCodeType = "ContainerDisabled"
|
||||
|
||||
// ServiceCodeContainerNotFound means the specified container does not exist.
|
||||
ServiceCodeContainerNotFound ServiceCodeType = "ContainerNotFound"
|
||||
|
||||
// ServiceCodeContentLengthLargerThanTierLimit means the blob's content length cannot exceed its tier limit.
|
||||
ServiceCodeContentLengthLargerThanTierLimit ServiceCodeType = "ContentLengthLargerThanTierLimit"
|
||||
|
||||
// ServiceCodeCopyAcrossAccountsNotSupported means the copy source account and destination account must be the same.
|
||||
ServiceCodeCopyAcrossAccountsNotSupported ServiceCodeType = "CopyAcrossAccountsNotSupported"
|
||||
|
||||
// ServiceCodeCopyIDMismatch means the specified copy ID did not match the copy ID for the pending copy operation.
|
||||
ServiceCodeCopyIDMismatch ServiceCodeType = "CopyIdMismatch"
|
||||
|
||||
// ServiceCodeFeatureVersionMismatch means the type of blob in the container is unrecognized by this version or
|
||||
// that the operation for AppendBlob requires at least version 2015-02-21.
|
||||
ServiceCodeFeatureVersionMismatch ServiceCodeType = "FeatureVersionMismatch"
|
||||
|
||||
// ServiceCodeIncrementalCopyBlobMismatch means the specified source blob is different than the copy source of the existing incremental copy blob.
|
||||
ServiceCodeIncrementalCopyBlobMismatch ServiceCodeType = "IncrementalCopyBlobMismatch"
|
||||
|
||||
// ServiceCodeIncrementalCopyOfEralierVersionSnapshotNotAllowed means the specified snapshot is earlier than the last snapshot copied into the incremental copy blob.
|
||||
ServiceCodeIncrementalCopyOfEralierVersionSnapshotNotAllowed ServiceCodeType = "IncrementalCopyOfEralierVersionSnapshotNotAllowed"
|
||||
|
||||
// ServiceCodeIncrementalCopySourceMustBeSnapshot means the source for incremental copy request must be a snapshot.
|
||||
ServiceCodeIncrementalCopySourceMustBeSnapshot ServiceCodeType = "IncrementalCopySourceMustBeSnapshot"
|
||||
|
||||
// ServiceCodeInfiniteLeaseDurationRequired means the lease ID matched, but the specified lease must be an infinite-duration lease.
|
||||
ServiceCodeInfiniteLeaseDurationRequired ServiceCodeType = "InfiniteLeaseDurationRequired"
|
||||
|
||||
// ServiceCodeInvalidBlobOrBlock means the specified blob or block content is invalid.
|
||||
ServiceCodeInvalidBlobOrBlock ServiceCodeType = "InvalidBlobOrBlock"
|
||||
|
||||
// ServiceCodeInvalidBlobType means the blob type is invalid for this operation.
|
||||
ServiceCodeInvalidBlobType ServiceCodeType = "InvalidBlobType"
|
||||
|
||||
// ServiceCodeInvalidBlockID means the specified block ID is invalid. The block ID must be Base64-encoded.
|
||||
ServiceCodeInvalidBlockID ServiceCodeType = "InvalidBlockId"
|
||||
|
||||
// ServiceCodeInvalidBlockList means the specified block list is invalid.
|
||||
ServiceCodeInvalidBlockList ServiceCodeType = "InvalidBlockList"
|
||||
|
||||
// ServiceCodeInvalidOperation means an invalid operation against a blob snapshot.
|
||||
ServiceCodeInvalidOperation ServiceCodeType = "InvalidOperation"
|
||||
|
||||
// ServiceCodeInvalidPageRange means the page range specified is invalid.
|
||||
ServiceCodeInvalidPageRange ServiceCodeType = "InvalidPageRange"
|
||||
|
||||
// ServiceCodeInvalidSourceBlobType means the copy source blob type is invalid for this operation.
|
||||
ServiceCodeInvalidSourceBlobType ServiceCodeType = "InvalidSourceBlobType"
|
||||
|
||||
// ServiceCodeInvalidSourceBlobURL means the source URL for incremental copy request must be valid Azure Storage blob URL.
|
||||
ServiceCodeInvalidSourceBlobURL ServiceCodeType = "InvalidSourceBlobUrl"
|
||||
|
||||
// ServiceCodeInvalidVersionForPageBlobOperation means that all operations on page blobs require at least version 2009-09-19.
|
||||
ServiceCodeInvalidVersionForPageBlobOperation ServiceCodeType = "InvalidVersionForPageBlobOperation"
|
||||
|
||||
// ServiceCodeLeaseAlreadyPresent means there is already a lease present.
|
||||
ServiceCodeLeaseAlreadyPresent ServiceCodeType = "LeaseAlreadyPresent"
|
||||
|
||||
// ServiceCodeLeaseAlreadyBroken means the lease has already been broken and cannot be broken again.
|
||||
ServiceCodeLeaseAlreadyBroken ServiceCodeType = "LeaseAlreadyBroken"
|
||||
|
||||
// ServiceCodeLeaseIDMismatchWithBlobOperation means the lease ID specified did not match the lease ID for the blob.
|
||||
ServiceCodeLeaseIDMismatchWithBlobOperation ServiceCodeType = "LeaseIdMismatchWithBlobOperation"
|
||||
|
||||
// ServiceCodeLeaseIDMismatchWithContainerOperation means the lease ID specified did not match the lease ID for the container.
|
||||
ServiceCodeLeaseIDMismatchWithContainerOperation ServiceCodeType = "LeaseIdMismatchWithContainerOperation"
|
||||
|
||||
// ServiceCodeLeaseIDMismatchWithLeaseOperation means the lease ID specified did not match the lease ID for the blob/container.
|
||||
ServiceCodeLeaseIDMismatchWithLeaseOperation ServiceCodeType = "LeaseIdMismatchWithLeaseOperation"
|
||||
|
||||
// ServiceCodeLeaseIDMissing means there is currently a lease on the blob/container and no lease ID was specified in the request.
|
||||
ServiceCodeLeaseIDMissing ServiceCodeType = "LeaseIdMissing"
|
||||
|
||||
// ServiceCodeLeaseIsBreakingAndCannotBeAcquired means the lease ID matched, but the lease is currently in breaking state and cannot be acquired until it is broken.
|
||||
ServiceCodeLeaseIsBreakingAndCannotBeAcquired ServiceCodeType = "LeaseIsBreakingAndCannotBeAcquired"
|
||||
|
||||
// ServiceCodeLeaseIsBreakingAndCannotBeChanged means the lease ID matched, but the lease is currently in breaking state and cannot be changed.
|
||||
ServiceCodeLeaseIsBreakingAndCannotBeChanged ServiceCodeType = "LeaseIsBreakingAndCannotBeChanged"
|
||||
|
||||
// ServiceCodeLeaseIsBrokenAndCannotBeRenewed means the lease ID matched, but the lease has been broken explicitly and cannot be renewed.
|
||||
ServiceCodeLeaseIsBrokenAndCannotBeRenewed ServiceCodeType = "LeaseIsBrokenAndCannotBeRenewed"
|
||||
|
||||
// ServiceCodeLeaseLost means a lease ID was specified, but the lease for the blob/container has expired.
|
||||
ServiceCodeLeaseLost ServiceCodeType = "LeaseLost"
|
||||
|
||||
// ServiceCodeLeaseNotPresentWithBlobOperation means there is currently no lease on the blob.
|
||||
ServiceCodeLeaseNotPresentWithBlobOperation ServiceCodeType = "LeaseNotPresentWithBlobOperation"
|
||||
|
||||
// ServiceCodeLeaseNotPresentWithContainerOperation means there is currently no lease on the container.
|
||||
ServiceCodeLeaseNotPresentWithContainerOperation ServiceCodeType = "LeaseNotPresentWithContainerOperation"
|
||||
|
||||
// ServiceCodeLeaseNotPresentWithLeaseOperation means there is currently no lease on the blob/container.
|
||||
ServiceCodeLeaseNotPresentWithLeaseOperation ServiceCodeType = "LeaseNotPresentWithLeaseOperation"
|
||||
|
||||
// ServiceCodeMaxBlobSizeConditionNotMet means the max blob size condition specified was not met.
|
||||
ServiceCodeMaxBlobSizeConditionNotMet ServiceCodeType = "MaxBlobSizeConditionNotMet"
|
||||
|
||||
// ServiceCodeNoPendingCopyOperation means there is currently no pending copy operation.
|
||||
ServiceCodeNoPendingCopyOperation ServiceCodeType = "NoPendingCopyOperation"
|
||||
|
||||
// ServiceCodeOperationNotAllowedOnIncrementalCopyBlob means the specified operation is not allowed on an incremental copy blob.
|
||||
ServiceCodeOperationNotAllowedOnIncrementalCopyBlob ServiceCodeType = "OperationNotAllowedOnIncrementalCopyBlob"
|
||||
|
||||
// ServiceCodePendingCopyOperation means there is currently a pending copy operation.
|
||||
ServiceCodePendingCopyOperation ServiceCodeType = "PendingCopyOperation"
|
||||
|
||||
// ServiceCodePreviousSnapshotCannotBeNewer means the prevsnapshot query parameter value cannot be newer than snapshot query parameter value.
|
||||
ServiceCodePreviousSnapshotCannotBeNewer ServiceCodeType = "PreviousSnapshotCannotBeNewer"
|
||||
|
||||
// ServiceCodePreviousSnapshotNotFound means the previous snapshot is not found.
|
||||
ServiceCodePreviousSnapshotNotFound ServiceCodeType = "PreviousSnapshotNotFound"
|
||||
|
||||
// ServiceCodePreviousSnapshotOperationNotSupported means that differential Get Page Ranges is not supported on the previous snapshot.
|
||||
ServiceCodePreviousSnapshotOperationNotSupported ServiceCodeType = "PreviousSnapshotOperationNotSupported"
|
||||
|
||||
// ServiceCodeSequenceNumberConditionNotMet means the sequence number condition specified was not met.
|
||||
ServiceCodeSequenceNumberConditionNotMet ServiceCodeType = "SequenceNumberConditionNotMet"
|
||||
|
||||
// ServiceCodeSequenceNumberIncrementTooLarge means the sequence number increment cannot be performed because it would result in overflow of the sequence number.
|
||||
ServiceCodeSequenceNumberIncrementTooLarge ServiceCodeType = "SequenceNumberIncrementTooLarge"
|
||||
|
||||
// ServiceCodeSnapshotCountExceeded means the snapshot count against this blob has been exceeded.
|
||||
ServiceCodeSnapshotCountExceeded ServiceCodeType = "SnapshotCountExceeded"
|
||||
|
||||
// ServiceCodeSnaphotOperationRateExceeded means the rate of snapshot operations against this blob has been exceeded.
|
||||
ServiceCodeSnaphotOperationRateExceeded ServiceCodeType = "SnaphotOperationRateExceeded"
|
||||
|
||||
// ServiceCodeSnapshotsPresent means this operation is not permitted while the blob has snapshots.
|
||||
ServiceCodeSnapshotsPresent ServiceCodeType = "SnapshotsPresent"
|
||||
|
||||
// ServiceCodeSourceConditionNotMet means the source condition specified using HTTP conditional header(s) is not met.
|
||||
ServiceCodeSourceConditionNotMet ServiceCodeType = "SourceConditionNotMet"
|
||||
|
||||
// ServiceCodeSystemInUse means this blob is in use by the system.
|
||||
ServiceCodeSystemInUse ServiceCodeType = "SystemInUse"
|
||||
|
||||
// ServiceCodeTargetConditionNotMet means the target condition specified using HTTP conditional header(s) is not met.
|
||||
ServiceCodeTargetConditionNotMet ServiceCodeType = "TargetConditionNotMet"
|
||||
|
||||
// ServiceCodeUnauthorizedBlobOverwrite means this request is not authorized to perform blob overwrites.
|
||||
ServiceCodeUnauthorizedBlobOverwrite ServiceCodeType = "UnauthorizedBlobOverwrite"
|
||||
|
||||
// ServiceCodeBlobBeingRehydrated means this operation is not permitted because the blob is being rehydrated.
|
||||
ServiceCodeBlobBeingRehydrated ServiceCodeType = "BlobBeingRehydrated"
|
||||
|
||||
// ServiceCodeBlobArchived means this operation is not permitted on an archived blob.
|
||||
ServiceCodeBlobArchived ServiceCodeType = "BlobArchived"
|
||||
|
||||
// ServiceCodeBlobNotArchived means this blob is currently not in the archived state.
|
||||
ServiceCodeBlobNotArchived ServiceCodeType = "BlobNotArchived"
|
||||
)
|
||||
|
||||
Generated
Vendored
+55
-55
@@ -1,55 +1,55 @@
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Azure/azure-pipeline-go/pipeline"
|
||||
)
|
||||
|
||||
// Credential represent any credential type; it is used to create a credential policy Factory.
|
||||
type Credential interface {
|
||||
pipeline.Factory
|
||||
credentialMarker()
|
||||
}
|
||||
|
||||
type credentialFunc pipeline.FactoryFunc
|
||||
|
||||
func (f credentialFunc) New(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.Policy {
|
||||
return f(next, po)
|
||||
}
|
||||
|
||||
// credentialMarker is a package-internal method that exists just to satisfy the Credential interface.
|
||||
func (credentialFunc) credentialMarker() {}
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
// NewAnonymousCredential creates an anonymous credential for use with HTTP(S) requests that read public resource
|
||||
// or for use with Shared Access Signatures (SAS).
|
||||
func NewAnonymousCredential() Credential {
|
||||
return anonymousCredentialFactory
|
||||
}
|
||||
|
||||
var anonymousCredentialFactory Credential = &anonymousCredentialPolicyFactory{} // Singleton
|
||||
|
||||
// anonymousCredentialPolicyFactory is the credential's policy factory.
|
||||
type anonymousCredentialPolicyFactory struct {
|
||||
}
|
||||
|
||||
// New creates a credential policy object.
|
||||
func (f *anonymousCredentialPolicyFactory) New(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.Policy {
|
||||
return &anonymousCredentialPolicy{next: next}
|
||||
}
|
||||
|
||||
// credentialMarker is a package-internal method that exists just to satisfy the Credential interface.
|
||||
func (*anonymousCredentialPolicyFactory) credentialMarker() {}
|
||||
|
||||
// anonymousCredentialPolicy is the credential's policy object.
|
||||
type anonymousCredentialPolicy struct {
|
||||
next pipeline.Policy
|
||||
}
|
||||
|
||||
// Do implements the credential's policy interface.
|
||||
func (p anonymousCredentialPolicy) Do(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
|
||||
// For anonymous credentials, this is effectively a no-op
|
||||
return p.next.Do(ctx, request)
|
||||
}
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Azure/azure-pipeline-go/pipeline"
|
||||
)
|
||||
|
||||
// Credential represent any credential type; it is used to create a credential policy Factory.
|
||||
type Credential interface {
|
||||
pipeline.Factory
|
||||
credentialMarker()
|
||||
}
|
||||
|
||||
type credentialFunc pipeline.FactoryFunc
|
||||
|
||||
func (f credentialFunc) New(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.Policy {
|
||||
return f(next, po)
|
||||
}
|
||||
|
||||
// credentialMarker is a package-internal method that exists just to satisfy the Credential interface.
|
||||
func (credentialFunc) credentialMarker() {}
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
// NewAnonymousCredential creates an anonymous credential for use with HTTP(S) requests that read public resource
|
||||
// or for use with Shared Access Signatures (SAS).
|
||||
func NewAnonymousCredential() Credential {
|
||||
return anonymousCredentialFactory
|
||||
}
|
||||
|
||||
var anonymousCredentialFactory Credential = &anonymousCredentialPolicyFactory{} // Singleton
|
||||
|
||||
// anonymousCredentialPolicyFactory is the credential's policy factory.
|
||||
type anonymousCredentialPolicyFactory struct {
|
||||
}
|
||||
|
||||
// New creates a credential policy object.
|
||||
func (f *anonymousCredentialPolicyFactory) New(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.Policy {
|
||||
return &anonymousCredentialPolicy{next: next}
|
||||
}
|
||||
|
||||
// credentialMarker is a package-internal method that exists just to satisfy the Credential interface.
|
||||
func (*anonymousCredentialPolicyFactory) credentialMarker() {}
|
||||
|
||||
// anonymousCredentialPolicy is the credential's policy object.
|
||||
type anonymousCredentialPolicy struct {
|
||||
next pipeline.Policy
|
||||
}
|
||||
|
||||
// Do implements the credential's policy interface.
|
||||
func (p anonymousCredentialPolicy) Do(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
|
||||
// For anonymous credentials, this is effectively a no-op
|
||||
return p.next.Do(ctx, request)
|
||||
}
|
||||
|
||||
Generated
Vendored
+39
-39
@@ -1,39 +1,39 @@
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/Azure/azure-pipeline-go/pipeline"
|
||||
)
|
||||
|
||||
// NewTokenCredential creates a token credential for use with role-based
|
||||
// access control (RBAC) access to Azure Storage resources.
|
||||
func NewTokenCredential(token string) *TokenCredential {
|
||||
f := &TokenCredential{}
|
||||
f.SetToken(token)
|
||||
return f
|
||||
}
|
||||
|
||||
// TokenCredential is a pipeline.Factory is the credential's policy factory.
|
||||
type TokenCredential struct{ token atomic.Value }
|
||||
|
||||
// Token returns the current token value
|
||||
func (f *TokenCredential) Token() string { return f.token.Load().(string) }
|
||||
|
||||
// SetToken changes the current token value
|
||||
func (f *TokenCredential) SetToken(token string) { f.token.Store(token) }
|
||||
|
||||
// credentialMarker is a package-internal method that exists just to satisfy the Credential interface.
|
||||
func (*TokenCredential) credentialMarker() {}
|
||||
|
||||
// New creates a credential policy object.
|
||||
func (f *TokenCredential) New(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.Policy {
|
||||
return pipeline.PolicyFunc(func(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
|
||||
if request.URL.Scheme != "https" {
|
||||
panic("Token credentials require a URL using the https protocol scheme.")
|
||||
}
|
||||
request.Header[headerAuthorization] = []string{"Bearer " + f.Token()}
|
||||
return next.Do(ctx, request)
|
||||
})
|
||||
}
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/Azure/azure-pipeline-go/pipeline"
|
||||
)
|
||||
|
||||
// NewTokenCredential creates a token credential for use with role-based
|
||||
// access control (RBAC) access to Azure Storage resources.
|
||||
func NewTokenCredential(token string) *TokenCredential {
|
||||
f := &TokenCredential{}
|
||||
f.SetToken(token)
|
||||
return f
|
||||
}
|
||||
|
||||
// TokenCredential is a pipeline.Factory is the credential's policy factory.
|
||||
type TokenCredential struct{ token atomic.Value }
|
||||
|
||||
// Token returns the current token value
|
||||
func (f *TokenCredential) Token() string { return f.token.Load().(string) }
|
||||
|
||||
// SetToken changes the current token value
|
||||
func (f *TokenCredential) SetToken(token string) { f.token.Store(token) }
|
||||
|
||||
// credentialMarker is a package-internal method that exists just to satisfy the Credential interface.
|
||||
func (*TokenCredential) credentialMarker() {}
|
||||
|
||||
// New creates a credential policy object.
|
||||
func (f *TokenCredential) New(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.Policy {
|
||||
return pipeline.PolicyFunc(func(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
|
||||
if request.URL.Scheme != "https" {
|
||||
panic("Token credentials require a URL using the https protocol scheme.")
|
||||
}
|
||||
request.Header[headerAuthorization] = []string{"Bearer " + f.Token()}
|
||||
return next.Do(ctx, request)
|
||||
})
|
||||
}
|
||||
|
||||
+27
-27
@@ -1,27 +1,27 @@
|
||||
// +build linux darwin
|
||||
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type mmf []byte
|
||||
|
||||
func newMMF(file *os.File, writable bool, offset int64, length int) (mmf, error) {
|
||||
prot, flags := syscall.PROT_READ, syscall.MAP_SHARED // Assume read-only
|
||||
if writable {
|
||||
prot, flags = syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED
|
||||
}
|
||||
addr, err := syscall.Mmap(int(file.Fd()), offset, length, prot, flags)
|
||||
return mmf(addr), err
|
||||
}
|
||||
|
||||
func (m *mmf) unmap() {
|
||||
err := syscall.Munmap(*m)
|
||||
*m = nil
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// +build linux darwin
|
||||
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type mmf []byte
|
||||
|
||||
func newMMF(file *os.File, writable bool, offset int64, length int) (mmf, error) {
|
||||
prot, flags := syscall.PROT_READ, syscall.MAP_SHARED // Assume read-only
|
||||
if writable {
|
||||
prot, flags = syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED
|
||||
}
|
||||
addr, err := syscall.Mmap(int(file.Fd()), offset, length, prot, flags)
|
||||
return mmf(addr), err
|
||||
}
|
||||
|
||||
func (m *mmf) unmap() {
|
||||
err := syscall.Munmap(*m)
|
||||
*m = nil
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
Generated
Vendored
+38
-38
@@ -1,38 +1,38 @@
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type mmf []byte
|
||||
|
||||
func newMMF(file *os.File, writable bool, offset int64, length int) (mmf, error) {
|
||||
prot, access := uint32(syscall.PAGE_READONLY), uint32(syscall.FILE_MAP_READ) // Assume read-only
|
||||
if writable {
|
||||
prot, access = uint32(syscall.PAGE_READWRITE), uint32(syscall.FILE_MAP_WRITE)
|
||||
}
|
||||
hMMF, errno := syscall.CreateFileMapping(syscall.Handle(file.Fd()), nil, prot, uint32(int64(length)>>32), uint32(int64(length)&0xffffffff), nil)
|
||||
if hMMF == 0 {
|
||||
return nil, os.NewSyscallError("CreateFileMapping", errno)
|
||||
}
|
||||
defer syscall.CloseHandle(hMMF)
|
||||
addr, errno := syscall.MapViewOfFile(hMMF, access, uint32(offset>>32), uint32(offset&0xffffffff), uintptr(length))
|
||||
m := mmf{}
|
||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&m))
|
||||
h.Data = addr
|
||||
h.Len = length
|
||||
h.Cap = h.Len
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *mmf) unmap() {
|
||||
addr := uintptr(unsafe.Pointer(&(([]byte)(*m)[0])))
|
||||
*m = mmf{}
|
||||
err := syscall.UnmapViewOfFile(addr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type mmf []byte
|
||||
|
||||
func newMMF(file *os.File, writable bool, offset int64, length int) (mmf, error) {
|
||||
prot, access := uint32(syscall.PAGE_READONLY), uint32(syscall.FILE_MAP_READ) // Assume read-only
|
||||
if writable {
|
||||
prot, access = uint32(syscall.PAGE_READWRITE), uint32(syscall.FILE_MAP_WRITE)
|
||||
}
|
||||
hMMF, errno := syscall.CreateFileMapping(syscall.Handle(file.Fd()), nil, prot, uint32(int64(length)>>32), uint32(int64(length)&0xffffffff), nil)
|
||||
if hMMF == 0 {
|
||||
return nil, os.NewSyscallError("CreateFileMapping", errno)
|
||||
}
|
||||
defer syscall.CloseHandle(hMMF)
|
||||
addr, errno := syscall.MapViewOfFile(hMMF, access, uint32(offset>>32), uint32(offset&0xffffffff), uintptr(length))
|
||||
m := mmf{}
|
||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&m))
|
||||
h.Data = addr
|
||||
h.Len = length
|
||||
h.Cap = h.Len
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *mmf) unmap() {
|
||||
addr := uintptr(unsafe.Pointer(&(([]byte)(*m)[0])))
|
||||
*m = mmf{}
|
||||
err := syscall.UnmapViewOfFile(addr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
Generated
Vendored
+51
-51
@@ -1,51 +1,51 @@
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/Azure/azure-pipeline-go/pipeline"
|
||||
)
|
||||
|
||||
// TelemetryOptions configures the telemetry policy's behavior.
|
||||
type TelemetryOptions struct {
|
||||
// Value is a string prepended to each request's User-Agent and sent to the service.
|
||||
// The service records the user-agent in logs for diagnostics and tracking of client requests.
|
||||
Value string
|
||||
}
|
||||
|
||||
// NewTelemetryPolicyFactory creates a factory that can create telemetry policy objects
|
||||
// which add telemetry information to outgoing HTTP requests.
|
||||
func NewTelemetryPolicyFactory(o TelemetryOptions) pipeline.Factory {
|
||||
b := &bytes.Buffer{}
|
||||
b.WriteString(o.Value)
|
||||
if b.Len() > 0 {
|
||||
b.WriteRune(' ')
|
||||
}
|
||||
fmt.Fprintf(b, "Azure-Storage/%s %s", serviceLibVersion, platformInfo)
|
||||
telemetryValue := b.String()
|
||||
|
||||
return pipeline.FactoryFunc(func(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.PolicyFunc {
|
||||
return func(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
|
||||
request.Header.Set("User-Agent", telemetryValue)
|
||||
return next.Do(ctx, request)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// NOTE: the ONLY function that should write to this variable is this func
|
||||
var platformInfo = func() string {
|
||||
// Azure-Storage/version (runtime; os type and version)”
|
||||
// Azure-Storage/1.4.0 (NODE-VERSION v4.5.0; Windows_NT 10.0.14393)'
|
||||
operatingSystem := runtime.GOOS // Default OS string
|
||||
switch operatingSystem {
|
||||
case "windows":
|
||||
operatingSystem = os.Getenv("OS") // Get more specific OS information
|
||||
case "linux": // accept default OS info
|
||||
case "freebsd": // accept default OS info
|
||||
}
|
||||
return fmt.Sprintf("(%s; %s)", runtime.Version(), operatingSystem)
|
||||
}()
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/Azure/azure-pipeline-go/pipeline"
|
||||
)
|
||||
|
||||
// TelemetryOptions configures the telemetry policy's behavior.
|
||||
type TelemetryOptions struct {
|
||||
// Value is a string prepended to each request's User-Agent and sent to the service.
|
||||
// The service records the user-agent in logs for diagnostics and tracking of client requests.
|
||||
Value string
|
||||
}
|
||||
|
||||
// NewTelemetryPolicyFactory creates a factory that can create telemetry policy objects
|
||||
// which add telemetry information to outgoing HTTP requests.
|
||||
func NewTelemetryPolicyFactory(o TelemetryOptions) pipeline.Factory {
|
||||
b := &bytes.Buffer{}
|
||||
b.WriteString(o.Value)
|
||||
if b.Len() > 0 {
|
||||
b.WriteRune(' ')
|
||||
}
|
||||
fmt.Fprintf(b, "Azure-Storage/%s %s", serviceLibVersion, platformInfo)
|
||||
telemetryValue := b.String()
|
||||
|
||||
return pipeline.FactoryFunc(func(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.PolicyFunc {
|
||||
return func(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
|
||||
request.Header.Set("User-Agent", telemetryValue)
|
||||
return next.Do(ctx, request)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// NOTE: the ONLY function that should write to this variable is this func
|
||||
var platformInfo = func() string {
|
||||
// Azure-Storage/version (runtime; os type and version)”
|
||||
// Azure-Storage/1.4.0 (NODE-VERSION v4.5.0; Windows_NT 10.0.14393)'
|
||||
operatingSystem := runtime.GOOS // Default OS string
|
||||
switch operatingSystem {
|
||||
case "windows":
|
||||
operatingSystem = os.Getenv("OS") // Get more specific OS information
|
||||
case "linux": // accept default OS info
|
||||
case "freebsd": // accept default OS info
|
||||
}
|
||||
return fmt.Sprintf("(%s; %s)", runtime.Version(), operatingSystem)
|
||||
}()
|
||||
|
||||
Generated
Vendored
+24
-24
@@ -1,24 +1,24 @@
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Azure/azure-pipeline-go/pipeline"
|
||||
)
|
||||
|
||||
// NewUniqueRequestIDPolicyFactory creates a UniqueRequestIDPolicyFactory object
|
||||
// that sets the request's x-ms-client-request-id header if it doesn't already exist.
|
||||
func NewUniqueRequestIDPolicyFactory() pipeline.Factory {
|
||||
return pipeline.FactoryFunc(func(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.PolicyFunc {
|
||||
// This is Policy's Do method:
|
||||
return func(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
|
||||
id := request.Header.Get(xMsClientRequestID)
|
||||
if id == "" { // Add a unique request ID if the caller didn't specify one already
|
||||
request.Header.Set(xMsClientRequestID, newUUID().String())
|
||||
}
|
||||
return next.Do(ctx, request)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const xMsClientRequestID = "x-ms-client-request-id"
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Azure/azure-pipeline-go/pipeline"
|
||||
)
|
||||
|
||||
// NewUniqueRequestIDPolicyFactory creates a UniqueRequestIDPolicyFactory object
|
||||
// that sets the request's x-ms-client-request-id header if it doesn't already exist.
|
||||
func NewUniqueRequestIDPolicyFactory() pipeline.Factory {
|
||||
return pipeline.FactoryFunc(func(next pipeline.Policy, po *pipeline.PolicyOptions) pipeline.PolicyFunc {
|
||||
// This is Policy's Do method:
|
||||
return func(ctx context.Context, request pipeline.Request) (pipeline.Response, error) {
|
||||
id := request.Header.Get(xMsClientRequestID)
|
||||
if id == "" { // Add a unique request ID if the caller didn't specify one already
|
||||
request.Header.Set(xMsClientRequestID, newUUID().String())
|
||||
}
|
||||
return next.Do(ctx, request)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const xMsClientRequestID = "x-ms-client-request-id"
|
||||
|
||||
Generated
Vendored
+217
-217
@@ -1,217 +1,217 @@
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AccountSASSignatureValues is used to generate a Shared Access Signature (SAS) for an Azure Storage account.
|
||||
// For more information, see https://docs.microsoft.com/rest/api/storageservices/constructing-an-account-sas
|
||||
type AccountSASSignatureValues struct {
|
||||
Version string `param:"sv"` // If not specified, this defaults to SASVersion
|
||||
Protocol SASProtocol `param:"spr"` // See the SASProtocol* constants
|
||||
StartTime time.Time `param:"st"` // Not specified if IsZero
|
||||
ExpiryTime time.Time `param:"se"` // Not specified if IsZero
|
||||
Permissions string `param:"sp"` // Create by initializing a AccountSASPermissions and then call String()
|
||||
IPRange IPRange `param:"sip"`
|
||||
Services string `param:"ss"` // Create by initializing AccountSASServices and then call String()
|
||||
ResourceTypes string `param:"srt"` // Create by initializing AccountSASResourceTypes and then call String()
|
||||
}
|
||||
|
||||
// NewSASQueryParameters uses an account's shared key credential to sign this signature values to produce
|
||||
// the proper SAS query parameters.
|
||||
func (v AccountSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *SharedKeyCredential) SASQueryParameters {
|
||||
// https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS
|
||||
if v.ExpiryTime.IsZero() || v.Permissions == "" || v.ResourceTypes == "" || v.Services == "" {
|
||||
panic("Account SAS is missing at least one of these: ExpiryTime, Permissions, Service, or ResourceType")
|
||||
}
|
||||
if v.Version == "" {
|
||||
v.Version = SASVersion
|
||||
}
|
||||
perms := &AccountSASPermissions{}
|
||||
if err := perms.Parse(v.Permissions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
v.Permissions = perms.String()
|
||||
|
||||
startTime, expiryTime := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime)
|
||||
|
||||
stringToSign := strings.Join([]string{
|
||||
sharedKeyCredential.AccountName(),
|
||||
v.Permissions,
|
||||
v.Services,
|
||||
v.ResourceTypes,
|
||||
startTime,
|
||||
expiryTime,
|
||||
v.IPRange.String(),
|
||||
string(v.Protocol),
|
||||
v.Version,
|
||||
""}, // That right, the account SAS requires a terminating extra newline
|
||||
"\n")
|
||||
|
||||
signature := sharedKeyCredential.ComputeHMACSHA256(stringToSign)
|
||||
p := SASQueryParameters{
|
||||
// Common SAS parameters
|
||||
version: v.Version,
|
||||
protocol: v.Protocol,
|
||||
startTime: v.StartTime,
|
||||
expiryTime: v.ExpiryTime,
|
||||
permissions: v.Permissions,
|
||||
ipRange: v.IPRange,
|
||||
|
||||
// Account-specific SAS parameters
|
||||
services: v.Services,
|
||||
resourceTypes: v.ResourceTypes,
|
||||
|
||||
// Calculated SAS signature
|
||||
signature: signature,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// The AccountSASPermissions type simplifies creating the permissions string for an Azure Storage Account SAS.
|
||||
// Initialize an instance of this type and then call its String method to set AccountSASSignatureValues's Permissions field.
|
||||
type AccountSASPermissions struct {
|
||||
Read, Write, Delete, List, Add, Create, Update, Process bool
|
||||
}
|
||||
|
||||
// String produces the SAS permissions string for an Azure Storage account.
|
||||
// Call this method to set AccountSASSignatureValues's Permissions field.
|
||||
func (p AccountSASPermissions) String() string {
|
||||
var buffer bytes.Buffer
|
||||
if p.Read {
|
||||
buffer.WriteRune('r')
|
||||
}
|
||||
if p.Write {
|
||||
buffer.WriteRune('w')
|
||||
}
|
||||
if p.Delete {
|
||||
buffer.WriteRune('d')
|
||||
}
|
||||
if p.List {
|
||||
buffer.WriteRune('l')
|
||||
}
|
||||
if p.Add {
|
||||
buffer.WriteRune('a')
|
||||
}
|
||||
if p.Create {
|
||||
buffer.WriteRune('c')
|
||||
}
|
||||
if p.Update {
|
||||
buffer.WriteRune('u')
|
||||
}
|
||||
if p.Process {
|
||||
buffer.WriteRune('p')
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Parse initializes the AccountSASPermissions's fields from a string.
|
||||
func (p *AccountSASPermissions) Parse(s string) error {
|
||||
*p = AccountSASPermissions{} // Clear out the flags
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case 'r':
|
||||
p.Read = true
|
||||
case 'w':
|
||||
p.Write = true
|
||||
case 'd':
|
||||
p.Delete = true
|
||||
case 'l':
|
||||
p.List = true
|
||||
case 'a':
|
||||
p.Add = true
|
||||
case 'c':
|
||||
p.Create = true
|
||||
case 'u':
|
||||
p.Update = true
|
||||
case 'p':
|
||||
p.Process = true
|
||||
default:
|
||||
return fmt.Errorf("Invalid permission character: '%v'", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The AccountSASServices type simplifies creating the services string for an Azure Storage Account SAS.
|
||||
// Initialize an instance of this type and then call its String method to set AccountSASSignatureValues's Services field.
|
||||
type AccountSASServices struct {
|
||||
Blob, Queue, File bool
|
||||
}
|
||||
|
||||
// String produces the SAS services string for an Azure Storage account.
|
||||
// Call this method to set AccountSASSignatureValues's Services field.
|
||||
func (s AccountSASServices) String() string {
|
||||
var buffer bytes.Buffer
|
||||
if s.Blob {
|
||||
buffer.WriteRune('b')
|
||||
}
|
||||
if s.Queue {
|
||||
buffer.WriteRune('q')
|
||||
}
|
||||
if s.File {
|
||||
buffer.WriteRune('f')
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Parse initializes the AccountSASServices' fields from a string.
|
||||
func (a *AccountSASServices) Parse(s string) error {
|
||||
*a = AccountSASServices{} // Clear out the flags
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case 'b':
|
||||
a.Blob = true
|
||||
case 'q':
|
||||
a.Queue = true
|
||||
case 'f':
|
||||
a.File = true
|
||||
default:
|
||||
return fmt.Errorf("Invalid service character: '%v'", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The AccountSASResourceTypes type simplifies creating the resource types string for an Azure Storage Account SAS.
|
||||
// Initialize an instance of this type and then call its String method to set AccountSASSignatureValues's ResourceTypes field.
|
||||
type AccountSASResourceTypes struct {
|
||||
Service, Container, Object bool
|
||||
}
|
||||
|
||||
// String produces the SAS resource types string for an Azure Storage account.
|
||||
// Call this method to set AccountSASSignatureValues's ResourceTypes field.
|
||||
func (rt AccountSASResourceTypes) String() string {
|
||||
var buffer bytes.Buffer
|
||||
if rt.Service {
|
||||
buffer.WriteRune('s')
|
||||
}
|
||||
if rt.Container {
|
||||
buffer.WriteRune('c')
|
||||
}
|
||||
if rt.Object {
|
||||
buffer.WriteRune('o')
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Parse initializes the AccountSASResourceType's fields from a string.
|
||||
func (rt *AccountSASResourceTypes) Parse(s string) error {
|
||||
*rt = AccountSASResourceTypes{} // Clear out the flags
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case 's':
|
||||
rt.Service = true
|
||||
case 'q':
|
||||
rt.Container = true
|
||||
case 'o':
|
||||
rt.Object = true
|
||||
default:
|
||||
return fmt.Errorf("Invalid resource type: '%v'", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AccountSASSignatureValues is used to generate a Shared Access Signature (SAS) for an Azure Storage account.
|
||||
// For more information, see https://docs.microsoft.com/rest/api/storageservices/constructing-an-account-sas
|
||||
type AccountSASSignatureValues struct {
|
||||
Version string `param:"sv"` // If not specified, this defaults to SASVersion
|
||||
Protocol SASProtocol `param:"spr"` // See the SASProtocol* constants
|
||||
StartTime time.Time `param:"st"` // Not specified if IsZero
|
||||
ExpiryTime time.Time `param:"se"` // Not specified if IsZero
|
||||
Permissions string `param:"sp"` // Create by initializing a AccountSASPermissions and then call String()
|
||||
IPRange IPRange `param:"sip"`
|
||||
Services string `param:"ss"` // Create by initializing AccountSASServices and then call String()
|
||||
ResourceTypes string `param:"srt"` // Create by initializing AccountSASResourceTypes and then call String()
|
||||
}
|
||||
|
||||
// NewSASQueryParameters uses an account's shared key credential to sign this signature values to produce
|
||||
// the proper SAS query parameters.
|
||||
func (v AccountSASSignatureValues) NewSASQueryParameters(sharedKeyCredential *SharedKeyCredential) SASQueryParameters {
|
||||
// https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS
|
||||
if v.ExpiryTime.IsZero() || v.Permissions == "" || v.ResourceTypes == "" || v.Services == "" {
|
||||
panic("Account SAS is missing at least one of these: ExpiryTime, Permissions, Service, or ResourceType")
|
||||
}
|
||||
if v.Version == "" {
|
||||
v.Version = SASVersion
|
||||
}
|
||||
perms := &AccountSASPermissions{}
|
||||
if err := perms.Parse(v.Permissions); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
v.Permissions = perms.String()
|
||||
|
||||
startTime, expiryTime := FormatTimesForSASSigning(v.StartTime, v.ExpiryTime)
|
||||
|
||||
stringToSign := strings.Join([]string{
|
||||
sharedKeyCredential.AccountName(),
|
||||
v.Permissions,
|
||||
v.Services,
|
||||
v.ResourceTypes,
|
||||
startTime,
|
||||
expiryTime,
|
||||
v.IPRange.String(),
|
||||
string(v.Protocol),
|
||||
v.Version,
|
||||
""}, // That right, the account SAS requires a terminating extra newline
|
||||
"\n")
|
||||
|
||||
signature := sharedKeyCredential.ComputeHMACSHA256(stringToSign)
|
||||
p := SASQueryParameters{
|
||||
// Common SAS parameters
|
||||
version: v.Version,
|
||||
protocol: v.Protocol,
|
||||
startTime: v.StartTime,
|
||||
expiryTime: v.ExpiryTime,
|
||||
permissions: v.Permissions,
|
||||
ipRange: v.IPRange,
|
||||
|
||||
// Account-specific SAS parameters
|
||||
services: v.Services,
|
||||
resourceTypes: v.ResourceTypes,
|
||||
|
||||
// Calculated SAS signature
|
||||
signature: signature,
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// The AccountSASPermissions type simplifies creating the permissions string for an Azure Storage Account SAS.
|
||||
// Initialize an instance of this type and then call its String method to set AccountSASSignatureValues's Permissions field.
|
||||
type AccountSASPermissions struct {
|
||||
Read, Write, Delete, List, Add, Create, Update, Process bool
|
||||
}
|
||||
|
||||
// String produces the SAS permissions string for an Azure Storage account.
|
||||
// Call this method to set AccountSASSignatureValues's Permissions field.
|
||||
func (p AccountSASPermissions) String() string {
|
||||
var buffer bytes.Buffer
|
||||
if p.Read {
|
||||
buffer.WriteRune('r')
|
||||
}
|
||||
if p.Write {
|
||||
buffer.WriteRune('w')
|
||||
}
|
||||
if p.Delete {
|
||||
buffer.WriteRune('d')
|
||||
}
|
||||
if p.List {
|
||||
buffer.WriteRune('l')
|
||||
}
|
||||
if p.Add {
|
||||
buffer.WriteRune('a')
|
||||
}
|
||||
if p.Create {
|
||||
buffer.WriteRune('c')
|
||||
}
|
||||
if p.Update {
|
||||
buffer.WriteRune('u')
|
||||
}
|
||||
if p.Process {
|
||||
buffer.WriteRune('p')
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Parse initializes the AccountSASPermissions's fields from a string.
|
||||
func (p *AccountSASPermissions) Parse(s string) error {
|
||||
*p = AccountSASPermissions{} // Clear out the flags
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case 'r':
|
||||
p.Read = true
|
||||
case 'w':
|
||||
p.Write = true
|
||||
case 'd':
|
||||
p.Delete = true
|
||||
case 'l':
|
||||
p.List = true
|
||||
case 'a':
|
||||
p.Add = true
|
||||
case 'c':
|
||||
p.Create = true
|
||||
case 'u':
|
||||
p.Update = true
|
||||
case 'p':
|
||||
p.Process = true
|
||||
default:
|
||||
return fmt.Errorf("Invalid permission character: '%v'", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The AccountSASServices type simplifies creating the services string for an Azure Storage Account SAS.
|
||||
// Initialize an instance of this type and then call its String method to set AccountSASSignatureValues's Services field.
|
||||
type AccountSASServices struct {
|
||||
Blob, Queue, File bool
|
||||
}
|
||||
|
||||
// String produces the SAS services string for an Azure Storage account.
|
||||
// Call this method to set AccountSASSignatureValues's Services field.
|
||||
func (s AccountSASServices) String() string {
|
||||
var buffer bytes.Buffer
|
||||
if s.Blob {
|
||||
buffer.WriteRune('b')
|
||||
}
|
||||
if s.Queue {
|
||||
buffer.WriteRune('q')
|
||||
}
|
||||
if s.File {
|
||||
buffer.WriteRune('f')
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Parse initializes the AccountSASServices' fields from a string.
|
||||
func (a *AccountSASServices) Parse(s string) error {
|
||||
*a = AccountSASServices{} // Clear out the flags
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case 'b':
|
||||
a.Blob = true
|
||||
case 'q':
|
||||
a.Queue = true
|
||||
case 'f':
|
||||
a.File = true
|
||||
default:
|
||||
return fmt.Errorf("Invalid service character: '%v'", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The AccountSASResourceTypes type simplifies creating the resource types string for an Azure Storage Account SAS.
|
||||
// Initialize an instance of this type and then call its String method to set AccountSASSignatureValues's ResourceTypes field.
|
||||
type AccountSASResourceTypes struct {
|
||||
Service, Container, Object bool
|
||||
}
|
||||
|
||||
// String produces the SAS resource types string for an Azure Storage account.
|
||||
// Call this method to set AccountSASSignatureValues's ResourceTypes field.
|
||||
func (rt AccountSASResourceTypes) String() string {
|
||||
var buffer bytes.Buffer
|
||||
if rt.Service {
|
||||
buffer.WriteRune('s')
|
||||
}
|
||||
if rt.Container {
|
||||
buffer.WriteRune('c')
|
||||
}
|
||||
if rt.Object {
|
||||
buffer.WriteRune('o')
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Parse initializes the AccountSASResourceType's fields from a string.
|
||||
func (rt *AccountSASResourceTypes) Parse(s string) error {
|
||||
*rt = AccountSASResourceTypes{} // Clear out the flags
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case 's':
|
||||
rt.Service = true
|
||||
case 'q':
|
||||
rt.Container = true
|
||||
case 'o':
|
||||
rt.Object = true
|
||||
default:
|
||||
return fmt.Errorf("Invalid resource type: '%v'", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Generated
Vendored
+131
-131
@@ -1,131 +1,131 @@
|
||||
package azblob
|
||||
|
||||
// https://docs.microsoft.com/en-us/rest/api/storageservices/common-rest-api-error-codes
|
||||
|
||||
const (
|
||||
// ServiceCodeNone is the default value. It indicates that the error was related to the service or that the service didn't return a code.
|
||||
ServiceCodeNone ServiceCodeType = ""
|
||||
|
||||
// ServiceCodeAccountAlreadyExists means the specified account already exists.
|
||||
ServiceCodeAccountAlreadyExists ServiceCodeType = "AccountAlreadyExists"
|
||||
|
||||
// ServiceCodeAccountBeingCreated means the specified account is in the process of being created (403).
|
||||
ServiceCodeAccountBeingCreated ServiceCodeType = "AccountBeingCreated"
|
||||
|
||||
// ServiceCodeAccountIsDisabled means the specified account is disabled (403).
|
||||
ServiceCodeAccountIsDisabled ServiceCodeType = "AccountIsDisabled"
|
||||
|
||||
// ServiceCodeAuthenticationFailed means the server failed to authenticate the request. Make sure the value of the Authorization header is formed correctly including the signature (403).
|
||||
ServiceCodeAuthenticationFailed ServiceCodeType = "AuthenticationFailed"
|
||||
|
||||
// ServiceCodeConditionHeadersNotSupported means the condition headers are not supported (400).
|
||||
ServiceCodeConditionHeadersNotSupported ServiceCodeType = "ConditionHeadersNotSupported"
|
||||
|
||||
// ServiceCodeConditionNotMet means the condition specified in the conditional header(s) was not met for a read/write operation (304/412).
|
||||
ServiceCodeConditionNotMet ServiceCodeType = "ConditionNotMet"
|
||||
|
||||
// ServiceCodeEmptyMetadataKey means the key for one of the metadata key-value pairs is empty (400).
|
||||
ServiceCodeEmptyMetadataKey ServiceCodeType = "EmptyMetadataKey"
|
||||
|
||||
// ServiceCodeInsufficientAccountPermissions means read operations are currently disabled or Write operations are not allowed or The account being accessed does not have sufficient permissions to execute this operation (403).
|
||||
ServiceCodeInsufficientAccountPermissions ServiceCodeType = "InsufficientAccountPermissions"
|
||||
|
||||
// ServiceCodeInternalError means the server encountered an internal error. Please retry the request (500).
|
||||
ServiceCodeInternalError ServiceCodeType = "InternalError"
|
||||
|
||||
// ServiceCodeInvalidAuthenticationInfo means the authentication information was not provided in the correct format. Verify the value of Authorization header (400).
|
||||
ServiceCodeInvalidAuthenticationInfo ServiceCodeType = "InvalidAuthenticationInfo"
|
||||
|
||||
// ServiceCodeInvalidHeaderValue means the value provided for one of the HTTP headers was not in the correct format (400).
|
||||
ServiceCodeInvalidHeaderValue ServiceCodeType = "InvalidHeaderValue"
|
||||
|
||||
// ServiceCodeInvalidHTTPVerb means the HTTP verb specified was not recognized by the server (400).
|
||||
ServiceCodeInvalidHTTPVerb ServiceCodeType = "InvalidHttpVerb"
|
||||
|
||||
// ServiceCodeInvalidInput means one of the request inputs is not valid (400).
|
||||
ServiceCodeInvalidInput ServiceCodeType = "InvalidInput"
|
||||
|
||||
// ServiceCodeInvalidMd5 means the MD5 value specified in the request is invalid. The MD5 value must be 128 bits and Base64-encoded (400).
|
||||
ServiceCodeInvalidMd5 ServiceCodeType = "InvalidMd5"
|
||||
|
||||
// ServiceCodeInvalidMetadata means the specified metadata is invalid. It includes characters that are not permitted (400).
|
||||
ServiceCodeInvalidMetadata ServiceCodeType = "InvalidMetadata"
|
||||
|
||||
// ServiceCodeInvalidQueryParameterValue means an invalid value was specified for one of the query parameters in the request URI (400).
|
||||
ServiceCodeInvalidQueryParameterValue ServiceCodeType = "InvalidQueryParameterValue"
|
||||
|
||||
// ServiceCodeInvalidRange means the range specified is invalid for the current size of the resource (416).
|
||||
ServiceCodeInvalidRange ServiceCodeType = "InvalidRange"
|
||||
|
||||
// ServiceCodeInvalidResourceName means the specified resource name contains invalid characters (400).
|
||||
ServiceCodeInvalidResourceName ServiceCodeType = "InvalidResourceName"
|
||||
|
||||
// ServiceCodeInvalidURI means the requested URI does not represent any resource on the server (400).
|
||||
ServiceCodeInvalidURI ServiceCodeType = "InvalidUri"
|
||||
|
||||
// ServiceCodeInvalidXMLDocument means the specified XML is not syntactically valid (400).
|
||||
ServiceCodeInvalidXMLDocument ServiceCodeType = "InvalidXmlDocument"
|
||||
|
||||
// ServiceCodeInvalidXMLNodeValue means the value provided for one of the XML nodes in the request body was not in the correct format (400).
|
||||
ServiceCodeInvalidXMLNodeValue ServiceCodeType = "InvalidXmlNodeValue"
|
||||
|
||||
// ServiceCodeMd5Mismatch means the MD5 value specified in the request did not match the MD5 value calculated by the server (400).
|
||||
ServiceCodeMd5Mismatch ServiceCodeType = "Md5Mismatch"
|
||||
|
||||
// ServiceCodeMetadataTooLarge means the size of the specified metadata exceeds the maximum size permitted (400).
|
||||
ServiceCodeMetadataTooLarge ServiceCodeType = "MetadataTooLarge"
|
||||
|
||||
// ServiceCodeMissingContentLengthHeader means the Content-Length header was not specified (411).
|
||||
ServiceCodeMissingContentLengthHeader ServiceCodeType = "MissingContentLengthHeader"
|
||||
|
||||
// ServiceCodeMissingRequiredQueryParameter means a required query parameter was not specified for this request (400).
|
||||
ServiceCodeMissingRequiredQueryParameter ServiceCodeType = "MissingRequiredQueryParameter"
|
||||
|
||||
// ServiceCodeMissingRequiredHeader means a required HTTP header was not specified (400).
|
||||
ServiceCodeMissingRequiredHeader ServiceCodeType = "MissingRequiredHeader"
|
||||
|
||||
// ServiceCodeMissingRequiredXMLNode means a required XML node was not specified in the request body (400).
|
||||
ServiceCodeMissingRequiredXMLNode ServiceCodeType = "MissingRequiredXmlNode"
|
||||
|
||||
// ServiceCodeMultipleConditionHeadersNotSupported means multiple condition headers are not supported (400).
|
||||
ServiceCodeMultipleConditionHeadersNotSupported ServiceCodeType = "MultipleConditionHeadersNotSupported"
|
||||
|
||||
// ServiceCodeOperationTimedOut means the operation could not be completed within the permitted time (500).
|
||||
ServiceCodeOperationTimedOut ServiceCodeType = "OperationTimedOut"
|
||||
|
||||
// ServiceCodeOutOfRangeInput means one of the request inputs is out of range (400).
|
||||
ServiceCodeOutOfRangeInput ServiceCodeType = "OutOfRangeInput"
|
||||
|
||||
// ServiceCodeOutOfRangeQueryParameterValue means a query parameter specified in the request URI is outside the permissible range (400).
|
||||
ServiceCodeOutOfRangeQueryParameterValue ServiceCodeType = "OutOfRangeQueryParameterValue"
|
||||
|
||||
// ServiceCodeRequestBodyTooLarge means the size of the request body exceeds the maximum size permitted (413).
|
||||
ServiceCodeRequestBodyTooLarge ServiceCodeType = "RequestBodyTooLarge"
|
||||
|
||||
// ServiceCodeResourceTypeMismatch means the specified resource type does not match the type of the existing resource (409).
|
||||
ServiceCodeResourceTypeMismatch ServiceCodeType = "ResourceTypeMismatch"
|
||||
|
||||
// ServiceCodeRequestURLFailedToParse means the url in the request could not be parsed (400).
|
||||
ServiceCodeRequestURLFailedToParse ServiceCodeType = "RequestUrlFailedToParse"
|
||||
|
||||
// ServiceCodeResourceAlreadyExists means the specified resource already exists (409).
|
||||
ServiceCodeResourceAlreadyExists ServiceCodeType = "ResourceAlreadyExists"
|
||||
|
||||
// ServiceCodeResourceNotFound means the specified resource does not exist (404).
|
||||
ServiceCodeResourceNotFound ServiceCodeType = "ResourceNotFound"
|
||||
|
||||
// ServiceCodeServerBusy means the server is currently unable to receive requests. Please retry your request or Ingress/egress is over the account limit or operations per second is over the account limit (503).
|
||||
ServiceCodeServerBusy ServiceCodeType = "ServerBusy"
|
||||
|
||||
// ServiceCodeUnsupportedHeader means one of the HTTP headers specified in the request is not supported (400).
|
||||
ServiceCodeUnsupportedHeader ServiceCodeType = "UnsupportedHeader"
|
||||
|
||||
// ServiceCodeUnsupportedXMLNode means one of the XML nodes specified in the request body is not supported (400).
|
||||
ServiceCodeUnsupportedXMLNode ServiceCodeType = "UnsupportedXmlNode"
|
||||
|
||||
// ServiceCodeUnsupportedQueryParameter means one of the query parameters specified in the request URI is not supported (400).
|
||||
ServiceCodeUnsupportedQueryParameter ServiceCodeType = "UnsupportedQueryParameter"
|
||||
|
||||
// ServiceCodeUnsupportedHTTPVerb means the resource doesn't support the specified HTTP verb (405).
|
||||
ServiceCodeUnsupportedHTTPVerb ServiceCodeType = "UnsupportedHttpVerb"
|
||||
)
|
||||
package azblob
|
||||
|
||||
// https://docs.microsoft.com/en-us/rest/api/storageservices/common-rest-api-error-codes
|
||||
|
||||
const (
|
||||
// ServiceCodeNone is the default value. It indicates that the error was related to the service or that the service didn't return a code.
|
||||
ServiceCodeNone ServiceCodeType = ""
|
||||
|
||||
// ServiceCodeAccountAlreadyExists means the specified account already exists.
|
||||
ServiceCodeAccountAlreadyExists ServiceCodeType = "AccountAlreadyExists"
|
||||
|
||||
// ServiceCodeAccountBeingCreated means the specified account is in the process of being created (403).
|
||||
ServiceCodeAccountBeingCreated ServiceCodeType = "AccountBeingCreated"
|
||||
|
||||
// ServiceCodeAccountIsDisabled means the specified account is disabled (403).
|
||||
ServiceCodeAccountIsDisabled ServiceCodeType = "AccountIsDisabled"
|
||||
|
||||
// ServiceCodeAuthenticationFailed means the server failed to authenticate the request. Make sure the value of the Authorization header is formed correctly including the signature (403).
|
||||
ServiceCodeAuthenticationFailed ServiceCodeType = "AuthenticationFailed"
|
||||
|
||||
// ServiceCodeConditionHeadersNotSupported means the condition headers are not supported (400).
|
||||
ServiceCodeConditionHeadersNotSupported ServiceCodeType = "ConditionHeadersNotSupported"
|
||||
|
||||
// ServiceCodeConditionNotMet means the condition specified in the conditional header(s) was not met for a read/write operation (304/412).
|
||||
ServiceCodeConditionNotMet ServiceCodeType = "ConditionNotMet"
|
||||
|
||||
// ServiceCodeEmptyMetadataKey means the key for one of the metadata key-value pairs is empty (400).
|
||||
ServiceCodeEmptyMetadataKey ServiceCodeType = "EmptyMetadataKey"
|
||||
|
||||
// ServiceCodeInsufficientAccountPermissions means read operations are currently disabled or Write operations are not allowed or The account being accessed does not have sufficient permissions to execute this operation (403).
|
||||
ServiceCodeInsufficientAccountPermissions ServiceCodeType = "InsufficientAccountPermissions"
|
||||
|
||||
// ServiceCodeInternalError means the server encountered an internal error. Please retry the request (500).
|
||||
ServiceCodeInternalError ServiceCodeType = "InternalError"
|
||||
|
||||
// ServiceCodeInvalidAuthenticationInfo means the authentication information was not provided in the correct format. Verify the value of Authorization header (400).
|
||||
ServiceCodeInvalidAuthenticationInfo ServiceCodeType = "InvalidAuthenticationInfo"
|
||||
|
||||
// ServiceCodeInvalidHeaderValue means the value provided for one of the HTTP headers was not in the correct format (400).
|
||||
ServiceCodeInvalidHeaderValue ServiceCodeType = "InvalidHeaderValue"
|
||||
|
||||
// ServiceCodeInvalidHTTPVerb means the HTTP verb specified was not recognized by the server (400).
|
||||
ServiceCodeInvalidHTTPVerb ServiceCodeType = "InvalidHttpVerb"
|
||||
|
||||
// ServiceCodeInvalidInput means one of the request inputs is not valid (400).
|
||||
ServiceCodeInvalidInput ServiceCodeType = "InvalidInput"
|
||||
|
||||
// ServiceCodeInvalidMd5 means the MD5 value specified in the request is invalid. The MD5 value must be 128 bits and Base64-encoded (400).
|
||||
ServiceCodeInvalidMd5 ServiceCodeType = "InvalidMd5"
|
||||
|
||||
// ServiceCodeInvalidMetadata means the specified metadata is invalid. It includes characters that are not permitted (400).
|
||||
ServiceCodeInvalidMetadata ServiceCodeType = "InvalidMetadata"
|
||||
|
||||
// ServiceCodeInvalidQueryParameterValue means an invalid value was specified for one of the query parameters in the request URI (400).
|
||||
ServiceCodeInvalidQueryParameterValue ServiceCodeType = "InvalidQueryParameterValue"
|
||||
|
||||
// ServiceCodeInvalidRange means the range specified is invalid for the current size of the resource (416).
|
||||
ServiceCodeInvalidRange ServiceCodeType = "InvalidRange"
|
||||
|
||||
// ServiceCodeInvalidResourceName means the specified resource name contains invalid characters (400).
|
||||
ServiceCodeInvalidResourceName ServiceCodeType = "InvalidResourceName"
|
||||
|
||||
// ServiceCodeInvalidURI means the requested URI does not represent any resource on the server (400).
|
||||
ServiceCodeInvalidURI ServiceCodeType = "InvalidUri"
|
||||
|
||||
// ServiceCodeInvalidXMLDocument means the specified XML is not syntactically valid (400).
|
||||
ServiceCodeInvalidXMLDocument ServiceCodeType = "InvalidXmlDocument"
|
||||
|
||||
// ServiceCodeInvalidXMLNodeValue means the value provided for one of the XML nodes in the request body was not in the correct format (400).
|
||||
ServiceCodeInvalidXMLNodeValue ServiceCodeType = "InvalidXmlNodeValue"
|
||||
|
||||
// ServiceCodeMd5Mismatch means the MD5 value specified in the request did not match the MD5 value calculated by the server (400).
|
||||
ServiceCodeMd5Mismatch ServiceCodeType = "Md5Mismatch"
|
||||
|
||||
// ServiceCodeMetadataTooLarge means the size of the specified metadata exceeds the maximum size permitted (400).
|
||||
ServiceCodeMetadataTooLarge ServiceCodeType = "MetadataTooLarge"
|
||||
|
||||
// ServiceCodeMissingContentLengthHeader means the Content-Length header was not specified (411).
|
||||
ServiceCodeMissingContentLengthHeader ServiceCodeType = "MissingContentLengthHeader"
|
||||
|
||||
// ServiceCodeMissingRequiredQueryParameter means a required query parameter was not specified for this request (400).
|
||||
ServiceCodeMissingRequiredQueryParameter ServiceCodeType = "MissingRequiredQueryParameter"
|
||||
|
||||
// ServiceCodeMissingRequiredHeader means a required HTTP header was not specified (400).
|
||||
ServiceCodeMissingRequiredHeader ServiceCodeType = "MissingRequiredHeader"
|
||||
|
||||
// ServiceCodeMissingRequiredXMLNode means a required XML node was not specified in the request body (400).
|
||||
ServiceCodeMissingRequiredXMLNode ServiceCodeType = "MissingRequiredXmlNode"
|
||||
|
||||
// ServiceCodeMultipleConditionHeadersNotSupported means multiple condition headers are not supported (400).
|
||||
ServiceCodeMultipleConditionHeadersNotSupported ServiceCodeType = "MultipleConditionHeadersNotSupported"
|
||||
|
||||
// ServiceCodeOperationTimedOut means the operation could not be completed within the permitted time (500).
|
||||
ServiceCodeOperationTimedOut ServiceCodeType = "OperationTimedOut"
|
||||
|
||||
// ServiceCodeOutOfRangeInput means one of the request inputs is out of range (400).
|
||||
ServiceCodeOutOfRangeInput ServiceCodeType = "OutOfRangeInput"
|
||||
|
||||
// ServiceCodeOutOfRangeQueryParameterValue means a query parameter specified in the request URI is outside the permissible range (400).
|
||||
ServiceCodeOutOfRangeQueryParameterValue ServiceCodeType = "OutOfRangeQueryParameterValue"
|
||||
|
||||
// ServiceCodeRequestBodyTooLarge means the size of the request body exceeds the maximum size permitted (413).
|
||||
ServiceCodeRequestBodyTooLarge ServiceCodeType = "RequestBodyTooLarge"
|
||||
|
||||
// ServiceCodeResourceTypeMismatch means the specified resource type does not match the type of the existing resource (409).
|
||||
ServiceCodeResourceTypeMismatch ServiceCodeType = "ResourceTypeMismatch"
|
||||
|
||||
// ServiceCodeRequestURLFailedToParse means the url in the request could not be parsed (400).
|
||||
ServiceCodeRequestURLFailedToParse ServiceCodeType = "RequestUrlFailedToParse"
|
||||
|
||||
// ServiceCodeResourceAlreadyExists means the specified resource already exists (409).
|
||||
ServiceCodeResourceAlreadyExists ServiceCodeType = "ResourceAlreadyExists"
|
||||
|
||||
// ServiceCodeResourceNotFound means the specified resource does not exist (404).
|
||||
ServiceCodeResourceNotFound ServiceCodeType = "ResourceNotFound"
|
||||
|
||||
// ServiceCodeServerBusy means the server is currently unable to receive requests. Please retry your request or Ingress/egress is over the account limit or operations per second is over the account limit (503).
|
||||
ServiceCodeServerBusy ServiceCodeType = "ServerBusy"
|
||||
|
||||
// ServiceCodeUnsupportedHeader means one of the HTTP headers specified in the request is not supported (400).
|
||||
ServiceCodeUnsupportedHeader ServiceCodeType = "UnsupportedHeader"
|
||||
|
||||
// ServiceCodeUnsupportedXMLNode means one of the XML nodes specified in the request body is not supported (400).
|
||||
ServiceCodeUnsupportedXMLNode ServiceCodeType = "UnsupportedXmlNode"
|
||||
|
||||
// ServiceCodeUnsupportedQueryParameter means one of the query parameters specified in the request URI is not supported (400).
|
||||
ServiceCodeUnsupportedQueryParameter ServiceCodeType = "UnsupportedQueryParameter"
|
||||
|
||||
// ServiceCodeUnsupportedHTTPVerb means the resource doesn't support the specified HTTP verb (405).
|
||||
ServiceCodeUnsupportedHTTPVerb ServiceCodeType = "UnsupportedHttpVerb"
|
||||
)
|
||||
|
||||
Generated
Vendored
+110
-110
@@ -1,110 +1,110 @@
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/Azure/azure-pipeline-go/pipeline"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// wire up our custom error handling constructor
|
||||
responseErrorFactory = newStorageError
|
||||
}
|
||||
|
||||
// ServiceCodeType is a string identifying a storage service error.
|
||||
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/status-and-error-codes2
|
||||
type ServiceCodeType string
|
||||
|
||||
// StorageError identifies a responder-generated network or response parsing error.
|
||||
type StorageError interface {
|
||||
// ResponseError implements error's Error(), net.Error's Temporary() and Timeout() methods & Response().
|
||||
ResponseError
|
||||
|
||||
// ServiceCode returns a service error code. Your code can use this to make error recovery decisions.
|
||||
ServiceCode() ServiceCodeType
|
||||
}
|
||||
|
||||
// storageError is the internal struct that implements the public StorageError interface.
|
||||
type storageError struct {
|
||||
responseError
|
||||
serviceCode ServiceCodeType
|
||||
details map[string]string
|
||||
}
|
||||
|
||||
// newStorageError creates an error object that implements the error interface.
|
||||
func newStorageError(cause error, response *http.Response, description string) error {
|
||||
return &storageError{
|
||||
responseError: responseError{
|
||||
ErrorNode: pipeline.ErrorNode{}.Initialize(cause, 3),
|
||||
response: response,
|
||||
description: description,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceCode returns service-error information. The caller may examine these values but should not modify any of them.
|
||||
func (e *storageError) ServiceCode() ServiceCodeType { return e.serviceCode }
|
||||
|
||||
// Error implements the error interface's Error method to return a string representation of the error.
|
||||
func (e *storageError) Error() string {
|
||||
b := &bytes.Buffer{}
|
||||
fmt.Fprintf(b, "===== RESPONSE ERROR (ServiceCode=%s) =====\n", e.serviceCode)
|
||||
fmt.Fprintf(b, "Description=%s, Details: ", e.description)
|
||||
if len(e.details) == 0 {
|
||||
b.WriteString("(none)\n")
|
||||
} else {
|
||||
b.WriteRune('\n')
|
||||
keys := make([]string, 0, len(e.details))
|
||||
// Alphabetize the details
|
||||
for k := range e.details {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
fmt.Fprintf(b, " %s: %+v\n", k, e.details[k])
|
||||
}
|
||||
}
|
||||
req := pipeline.Request{Request: e.response.Request}.Copy() // Make a copy of the response's request
|
||||
pipeline.WriteRequestWithResponse(b, prepareRequestForLogging(req), e.response, nil)
|
||||
return e.ErrorNode.Error(b.String())
|
||||
}
|
||||
|
||||
// Temporary returns true if the error occurred due to a temporary condition (including an HTTP status of 500 or 503).
|
||||
func (e *storageError) Temporary() bool {
|
||||
if e.response != nil {
|
||||
if (e.response.StatusCode == http.StatusInternalServerError) || (e.response.StatusCode == http.StatusServiceUnavailable) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return e.ErrorNode.Temporary()
|
||||
}
|
||||
|
||||
// UnmarshalXML performs custom unmarshalling of XML-formatted Azure storage request errors.
|
||||
func (e *storageError) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) {
|
||||
tokName := ""
|
||||
var t xml.Token
|
||||
for t, err = d.Token(); err == nil; t, err = d.Token() {
|
||||
switch tt := t.(type) {
|
||||
case xml.StartElement:
|
||||
tokName = tt.Name.Local
|
||||
break
|
||||
case xml.CharData:
|
||||
switch tokName {
|
||||
case "Code":
|
||||
e.serviceCode = ServiceCodeType(tt)
|
||||
case "Message":
|
||||
e.description = string(tt)
|
||||
default:
|
||||
if e.details == nil {
|
||||
e.details = map[string]string{}
|
||||
}
|
||||
e.details[tokName] = string(tt)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/Azure/azure-pipeline-go/pipeline"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// wire up our custom error handling constructor
|
||||
responseErrorFactory = newStorageError
|
||||
}
|
||||
|
||||
// ServiceCodeType is a string identifying a storage service error.
|
||||
// For more information, see https://docs.microsoft.com/en-us/rest/api/storageservices/status-and-error-codes2
|
||||
type ServiceCodeType string
|
||||
|
||||
// StorageError identifies a responder-generated network or response parsing error.
|
||||
type StorageError interface {
|
||||
// ResponseError implements error's Error(), net.Error's Temporary() and Timeout() methods & Response().
|
||||
ResponseError
|
||||
|
||||
// ServiceCode returns a service error code. Your code can use this to make error recovery decisions.
|
||||
ServiceCode() ServiceCodeType
|
||||
}
|
||||
|
||||
// storageError is the internal struct that implements the public StorageError interface.
|
||||
type storageError struct {
|
||||
responseError
|
||||
serviceCode ServiceCodeType
|
||||
details map[string]string
|
||||
}
|
||||
|
||||
// newStorageError creates an error object that implements the error interface.
|
||||
func newStorageError(cause error, response *http.Response, description string) error {
|
||||
return &storageError{
|
||||
responseError: responseError{
|
||||
ErrorNode: pipeline.ErrorNode{}.Initialize(cause, 3),
|
||||
response: response,
|
||||
description: description,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceCode returns service-error information. The caller may examine these values but should not modify any of them.
|
||||
func (e *storageError) ServiceCode() ServiceCodeType { return e.serviceCode }
|
||||
|
||||
// Error implements the error interface's Error method to return a string representation of the error.
|
||||
func (e *storageError) Error() string {
|
||||
b := &bytes.Buffer{}
|
||||
fmt.Fprintf(b, "===== RESPONSE ERROR (ServiceCode=%s) =====\n", e.serviceCode)
|
||||
fmt.Fprintf(b, "Description=%s, Details: ", e.description)
|
||||
if len(e.details) == 0 {
|
||||
b.WriteString("(none)\n")
|
||||
} else {
|
||||
b.WriteRune('\n')
|
||||
keys := make([]string, 0, len(e.details))
|
||||
// Alphabetize the details
|
||||
for k := range e.details {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
fmt.Fprintf(b, " %s: %+v\n", k, e.details[k])
|
||||
}
|
||||
}
|
||||
req := pipeline.Request{Request: e.response.Request}.Copy() // Make a copy of the response's request
|
||||
pipeline.WriteRequestWithResponse(b, prepareRequestForLogging(req), e.response, nil)
|
||||
return e.ErrorNode.Error(b.String())
|
||||
}
|
||||
|
||||
// Temporary returns true if the error occurred due to a temporary condition (including an HTTP status of 500 or 503).
|
||||
func (e *storageError) Temporary() bool {
|
||||
if e.response != nil {
|
||||
if (e.response.StatusCode == http.StatusInternalServerError) || (e.response.StatusCode == http.StatusServiceUnavailable) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return e.ErrorNode.Temporary()
|
||||
}
|
||||
|
||||
// UnmarshalXML performs custom unmarshalling of XML-formatted Azure storage request errors.
|
||||
func (e *storageError) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) {
|
||||
tokName := ""
|
||||
var t xml.Token
|
||||
for t, err = d.Token(); err == nil; t, err = d.Token() {
|
||||
switch tt := t.(type) {
|
||||
case xml.StartElement:
|
||||
tokName = tt.Name.Local
|
||||
break
|
||||
case xml.CharData:
|
||||
switch tokName {
|
||||
case "Code":
|
||||
e.serviceCode = ServiceCodeType(tt)
|
||||
case "Message":
|
||||
e.description = string(tt)
|
||||
default:
|
||||
if e.details == nil {
|
||||
e.details = map[string]string{}
|
||||
}
|
||||
e.details[tokName] = string(tt)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+80
-80
@@ -1,80 +1,80 @@
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// The UUID reserved variants.
|
||||
const (
|
||||
reservedNCS byte = 0x80
|
||||
reservedRFC4122 byte = 0x40
|
||||
reservedMicrosoft byte = 0x20
|
||||
reservedFuture byte = 0x00
|
||||
)
|
||||
|
||||
// A UUID representation compliant with specification in RFC 4122 document.
|
||||
type uuid [16]byte
|
||||
|
||||
// NewUUID returns a new uuid using RFC 4122 algorithm.
|
||||
func newUUID() (u uuid) {
|
||||
u = uuid{}
|
||||
// Set all bits to randomly (or pseudo-randomly) chosen values.
|
||||
_, err := rand.Read(u[:])
|
||||
if err != nil {
|
||||
panic("ran.Read failed")
|
||||
}
|
||||
u[8] = (u[8] | reservedRFC4122) & 0x7F // u.setVariant(ReservedRFC4122)
|
||||
|
||||
var version byte = 4
|
||||
u[6] = (u[6] & 0xF) | (version << 4) // u.setVersion(4)
|
||||
return
|
||||
}
|
||||
|
||||
// String returns an unparsed version of the generated UUID sequence.
|
||||
func (u uuid) String() string {
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:4], u[4:6], u[6:8], u[8:10], u[10:])
|
||||
}
|
||||
|
||||
// ParseUUID parses a string formatted as "003020100-0504-0706-0809-0a0b0c0d0e0f"
|
||||
// or "{03020100-0504-0706-0809-0a0b0c0d0e0f}" into a UUID.
|
||||
func parseUUID(uuidStr string) uuid {
|
||||
char := func(hexString string) byte {
|
||||
i, _ := strconv.ParseUint(hexString, 16, 8)
|
||||
return byte(i)
|
||||
}
|
||||
if uuidStr[0] == '{' {
|
||||
uuidStr = uuidStr[1:] // Skip over the '{'
|
||||
}
|
||||
// 03020100 - 05 04 - 07 06 - 08 09 - 0a 0b 0c 0d 0e 0f
|
||||
// 1 11 1 11 11 1 12 22 2 22 22 22 33 33 33
|
||||
// 01234567 8 90 12 3 45 67 8 90 12 3 45 67 89 01 23 45
|
||||
uuidVal := uuid{
|
||||
char(uuidStr[0:2]),
|
||||
char(uuidStr[2:4]),
|
||||
char(uuidStr[4:6]),
|
||||
char(uuidStr[6:8]),
|
||||
|
||||
char(uuidStr[9:11]),
|
||||
char(uuidStr[11:13]),
|
||||
|
||||
char(uuidStr[14:16]),
|
||||
char(uuidStr[16:18]),
|
||||
|
||||
char(uuidStr[19:21]),
|
||||
char(uuidStr[21:23]),
|
||||
|
||||
char(uuidStr[24:26]),
|
||||
char(uuidStr[26:28]),
|
||||
char(uuidStr[28:30]),
|
||||
char(uuidStr[30:32]),
|
||||
char(uuidStr[32:34]),
|
||||
char(uuidStr[34:36]),
|
||||
}
|
||||
return uuidVal
|
||||
}
|
||||
|
||||
func (u uuid) bytes() []byte {
|
||||
return u[:]
|
||||
}
|
||||
package azblob
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// The UUID reserved variants.
|
||||
const (
|
||||
reservedNCS byte = 0x80
|
||||
reservedRFC4122 byte = 0x40
|
||||
reservedMicrosoft byte = 0x20
|
||||
reservedFuture byte = 0x00
|
||||
)
|
||||
|
||||
// A UUID representation compliant with specification in RFC 4122 document.
|
||||
type uuid [16]byte
|
||||
|
||||
// NewUUID returns a new uuid using RFC 4122 algorithm.
|
||||
func newUUID() (u uuid) {
|
||||
u = uuid{}
|
||||
// Set all bits to randomly (or pseudo-randomly) chosen values.
|
||||
_, err := rand.Read(u[:])
|
||||
if err != nil {
|
||||
panic("ran.Read failed")
|
||||
}
|
||||
u[8] = (u[8] | reservedRFC4122) & 0x7F // u.setVariant(ReservedRFC4122)
|
||||
|
||||
var version byte = 4
|
||||
u[6] = (u[6] & 0xF) | (version << 4) // u.setVersion(4)
|
||||
return
|
||||
}
|
||||
|
||||
// String returns an unparsed version of the generated UUID sequence.
|
||||
func (u uuid) String() string {
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:4], u[4:6], u[6:8], u[8:10], u[10:])
|
||||
}
|
||||
|
||||
// ParseUUID parses a string formatted as "003020100-0504-0706-0809-0a0b0c0d0e0f"
|
||||
// or "{03020100-0504-0706-0809-0a0b0c0d0e0f}" into a UUID.
|
||||
func parseUUID(uuidStr string) uuid {
|
||||
char := func(hexString string) byte {
|
||||
i, _ := strconv.ParseUint(hexString, 16, 8)
|
||||
return byte(i)
|
||||
}
|
||||
if uuidStr[0] == '{' {
|
||||
uuidStr = uuidStr[1:] // Skip over the '{'
|
||||
}
|
||||
// 03020100 - 05 04 - 07 06 - 08 09 - 0a 0b 0c 0d 0e 0f
|
||||
// 1 11 1 11 11 1 12 22 2 22 22 22 33 33 33
|
||||
// 01234567 8 90 12 3 45 67 8 90 12 3 45 67 89 01 23 45
|
||||
uuidVal := uuid{
|
||||
char(uuidStr[0:2]),
|
||||
char(uuidStr[2:4]),
|
||||
char(uuidStr[4:6]),
|
||||
char(uuidStr[6:8]),
|
||||
|
||||
char(uuidStr[9:11]),
|
||||
char(uuidStr[11:13]),
|
||||
|
||||
char(uuidStr[14:16]),
|
||||
char(uuidStr[16:18]),
|
||||
|
||||
char(uuidStr[19:21]),
|
||||
char(uuidStr[21:23]),
|
||||
|
||||
char(uuidStr[24:26]),
|
||||
char(uuidStr[26:28]),
|
||||
char(uuidStr[28:30]),
|
||||
char(uuidStr[30:32]),
|
||||
char(uuidStr[32:34]),
|
||||
char(uuidStr[34:36]),
|
||||
}
|
||||
return uuidVal
|
||||
}
|
||||
|
||||
func (u uuid) bytes() []byte {
|
||||
return u[:]
|
||||
}
|
||||
|
||||
+89
-89
@@ -1,89 +1,89 @@
|
||||
// Copyright 2017 Microsoft Corporation. All rights reserved.
|
||||
// Use of this source code is governed by an MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package azblob allows you to manipulate Azure Storage containers and blobs objects.
|
||||
|
||||
URL Types
|
||||
|
||||
The most common types you'll work with are the XxxURL types. The methods of these types make requests
|
||||
against the Azure Storage Service.
|
||||
|
||||
- ServiceURL's methods perform operations on a storage account.
|
||||
- ContainerURL's methods perform operations on an account's container.
|
||||
- BlockBlobURL's methods perform operations on a container's block blob.
|
||||
- AppendBlobURL's methods perform operations on a container's append blob.
|
||||
- PageBlobURL's methods perform operations on a container's page blob.
|
||||
- BlobURL's methods perform operations on a container's blob regardless of the blob's type.
|
||||
|
||||
Internally, each XxxURL object contains a URL and a request pipeline. The URL indicates the endpoint where each HTTP
|
||||
request is sent and the pipeline indicates how the outgoing HTTP request and incoming HTTP response is processed.
|
||||
The pipeline specifies things like retry policies, logging, deserialization of HTTP response payloads, and more.
|
||||
|
||||
Pipelines are threadsafe and may be shared by multiple XxxURL objects. When you create a ServiceURL, you pass
|
||||
an initial pipeline. When you call ServiceURL's NewContainerURL method, the new ContainerURL object has its own
|
||||
URL but it shares the same pipeline as the parent ServiceURL object.
|
||||
|
||||
To work with a blob, call one of ContainerURL's 4 NewXxxBlobURL methods depending on how you want to treat the blob.
|
||||
To treat the blob as a block blob, append blob, or page blob, call NewBlockBlobURL, NewAppendBlobURL, or NewPageBlobURL
|
||||
respectively. These three types are all identical except for the methods they expose; each type exposes the methods
|
||||
relevant to the type of blob represented. If you're not sure how you want to treat a blob, you can call NewBlobURL;
|
||||
this returns an object whose methods are relevant to any kind of blob. When you call ContainerURL's NewXxxBlobURL,
|
||||
the new XxxBlobURL object has its own URL but it shares the same pipeline as the parent ContainerURL object. You
|
||||
can easily switch between blob types (method sets) by calling a ToXxxBlobURL method.
|
||||
|
||||
If you'd like to use a different pipeline with a ServiceURL, ContainerURL, or XxxBlobURL object, then call the XxxURL
|
||||
object's WithPipeline method passing in the desired pipeline. The WithPipeline methods create a new XxxURL object
|
||||
with the same URL as the original but with the specified pipeline.
|
||||
|
||||
Note that XxxURL objects use little memory, are goroutine-safe, and many objects share the same pipeline. This means that
|
||||
XxxURL objects share a lot of system resources making them very efficient.
|
||||
|
||||
All of XxxURL's methods that make HTTP requests return rich error handling information so you can discern network failures,
|
||||
transient failures, timeout failures, service failures, etc. See the StorageError interface for more information and an
|
||||
example of how to do deal with errors.
|
||||
|
||||
URL and Shared Access Signature Manipulation
|
||||
|
||||
The library includes a BlobURLParts type for deconstructing and reconstructing URLs. And you can use the following types
|
||||
for generating and parsing Shared Access Signature (SAS)
|
||||
- Use the AccountSASSignatureValues type to create a SAS for a storage account.
|
||||
- Use the BlobSASSignatureValues type to create a SAS for a container or blob.
|
||||
- Use the SASQueryParameters type to turn signature values in to query parameres or to parse query parameters.
|
||||
|
||||
To generate a SAS, you must use the SharedKeyCredential type.
|
||||
|
||||
Credentials
|
||||
|
||||
When creating a request pipeline, you must specify one of this package's credential types.
|
||||
- Call the NewAnonymousCredential function for requests that contain a Shared Access Signature (SAS).
|
||||
- Call the NewSharedKeyCredential function (with an account name & key) to access any account resources. You must also use this
|
||||
to generate Shared Access Signatures.
|
||||
|
||||
HTTP Request Policy Factories
|
||||
|
||||
This package defines several request policy factories for use with the pipeline package.
|
||||
Most applications will not use these factories directly; instead, the NewPipeline
|
||||
function creates these factories, initializes them (via the PipelineOptions type)
|
||||
and returns a pipeline object for use by the XxxURL objects.
|
||||
|
||||
However, for advanced scenarios, developers can access these policy factories directly
|
||||
and even create their own and then construct their own pipeline in order to affect HTTP
|
||||
requests and responses performed by the XxxURL objects. For example, developers can
|
||||
introduce their own logging, random failures, request recording & playback for fast
|
||||
testing, HTTP request pacing, alternate retry mechanisms, metering, metrics, etc. The
|
||||
possibilities are endless!
|
||||
|
||||
Below are the request pipeline policy factory functions that are provided with this
|
||||
package:
|
||||
- NewRetryPolicyFactory Enables rich retry semantics for failed HTTP requests.
|
||||
- NewRequestLogPolicyFactory Enables rich logging support for HTTP requests/responses & failures.
|
||||
- NewTelemetryPolicyFactory Enables simple modification of the HTTP request's User-Agent header so each request reports the SDK version & language/runtime making the requests.
|
||||
- NewUniqueRequestIDPolicyFactory Adds a x-ms-client-request-id header with a unique UUID value to an HTTP request to help with diagnosing failures.
|
||||
|
||||
Also, note that all the NewXxxCredential functions return request policy factory objects which get injected into the pipeline.
|
||||
*/
|
||||
package azblob
|
||||
|
||||
// TokenCredential Use this to access resources using Role-Based Access Control (RBAC).
|
||||
// Copyright 2017 Microsoft Corporation. All rights reserved.
|
||||
// Use of this source code is governed by an MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package azblob allows you to manipulate Azure Storage containers and blobs objects.
|
||||
|
||||
URL Types
|
||||
|
||||
The most common types you'll work with are the XxxURL types. The methods of these types make requests
|
||||
against the Azure Storage Service.
|
||||
|
||||
- ServiceURL's methods perform operations on a storage account.
|
||||
- ContainerURL's methods perform operations on an account's container.
|
||||
- BlockBlobURL's methods perform operations on a container's block blob.
|
||||
- AppendBlobURL's methods perform operations on a container's append blob.
|
||||
- PageBlobURL's methods perform operations on a container's page blob.
|
||||
- BlobURL's methods perform operations on a container's blob regardless of the blob's type.
|
||||
|
||||
Internally, each XxxURL object contains a URL and a request pipeline. The URL indicates the endpoint where each HTTP
|
||||
request is sent and the pipeline indicates how the outgoing HTTP request and incoming HTTP response is processed.
|
||||
The pipeline specifies things like retry policies, logging, deserialization of HTTP response payloads, and more.
|
||||
|
||||
Pipelines are threadsafe and may be shared by multiple XxxURL objects. When you create a ServiceURL, you pass
|
||||
an initial pipeline. When you call ServiceURL's NewContainerURL method, the new ContainerURL object has its own
|
||||
URL but it shares the same pipeline as the parent ServiceURL object.
|
||||
|
||||
To work with a blob, call one of ContainerURL's 4 NewXxxBlobURL methods depending on how you want to treat the blob.
|
||||
To treat the blob as a block blob, append blob, or page blob, call NewBlockBlobURL, NewAppendBlobURL, or NewPageBlobURL
|
||||
respectively. These three types are all identical except for the methods they expose; each type exposes the methods
|
||||
relevant to the type of blob represented. If you're not sure how you want to treat a blob, you can call NewBlobURL;
|
||||
this returns an object whose methods are relevant to any kind of blob. When you call ContainerURL's NewXxxBlobURL,
|
||||
the new XxxBlobURL object has its own URL but it shares the same pipeline as the parent ContainerURL object. You
|
||||
can easily switch between blob types (method sets) by calling a ToXxxBlobURL method.
|
||||
|
||||
If you'd like to use a different pipeline with a ServiceURL, ContainerURL, or XxxBlobURL object, then call the XxxURL
|
||||
object's WithPipeline method passing in the desired pipeline. The WithPipeline methods create a new XxxURL object
|
||||
with the same URL as the original but with the specified pipeline.
|
||||
|
||||
Note that XxxURL objects use little memory, are goroutine-safe, and many objects share the same pipeline. This means that
|
||||
XxxURL objects share a lot of system resources making them very efficient.
|
||||
|
||||
All of XxxURL's methods that make HTTP requests return rich error handling information so you can discern network failures,
|
||||
transient failures, timeout failures, service failures, etc. See the StorageError interface for more information and an
|
||||
example of how to do deal with errors.
|
||||
|
||||
URL and Shared Access Signature Manipulation
|
||||
|
||||
The library includes a BlobURLParts type for deconstructing and reconstructing URLs. And you can use the following types
|
||||
for generating and parsing Shared Access Signature (SAS)
|
||||
- Use the AccountSASSignatureValues type to create a SAS for a storage account.
|
||||
- Use the BlobSASSignatureValues type to create a SAS for a container or blob.
|
||||
- Use the SASQueryParameters type to turn signature values in to query parameres or to parse query parameters.
|
||||
|
||||
To generate a SAS, you must use the SharedKeyCredential type.
|
||||
|
||||
Credentials
|
||||
|
||||
When creating a request pipeline, you must specify one of this package's credential types.
|
||||
- Call the NewAnonymousCredential function for requests that contain a Shared Access Signature (SAS).
|
||||
- Call the NewSharedKeyCredential function (with an account name & key) to access any account resources. You must also use this
|
||||
to generate Shared Access Signatures.
|
||||
|
||||
HTTP Request Policy Factories
|
||||
|
||||
This package defines several request policy factories for use with the pipeline package.
|
||||
Most applications will not use these factories directly; instead, the NewPipeline
|
||||
function creates these factories, initializes them (via the PipelineOptions type)
|
||||
and returns a pipeline object for use by the XxxURL objects.
|
||||
|
||||
However, for advanced scenarios, developers can access these policy factories directly
|
||||
and even create their own and then construct their own pipeline in order to affect HTTP
|
||||
requests and responses performed by the XxxURL objects. For example, developers can
|
||||
introduce their own logging, random failures, request recording & playback for fast
|
||||
testing, HTTP request pacing, alternate retry mechanisms, metering, metrics, etc. The
|
||||
possibilities are endless!
|
||||
|
||||
Below are the request pipeline policy factory functions that are provided with this
|
||||
package:
|
||||
- NewRetryPolicyFactory Enables rich retry semantics for failed HTTP requests.
|
||||
- NewRequestLogPolicyFactory Enables rich logging support for HTTP requests/responses & failures.
|
||||
- NewTelemetryPolicyFactory Enables simple modification of the HTTP request's User-Agent header so each request reports the SDK version & language/runtime making the requests.
|
||||
- NewUniqueRequestIDPolicyFactory Adds a x-ms-client-request-id header with a unique UUID value to an HTTP request to help with diagnosing failures.
|
||||
|
||||
Also, note that all the NewXxxCredential functions return request policy factory objects which get injected into the pipeline.
|
||||
*/
|
||||
package azblob
|
||||
|
||||
// TokenCredential Use this to access resources using Role-Based Access Control (RBAC).
|
||||
|
||||
-239
@@ -1,239 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
--------------------------------------------------
|
||||
SOFTWARE DISTRIBUTED WITH THRIFT:
|
||||
|
||||
The Apache Thrift software includes a number of subcomponents with
|
||||
separate copyright notices and license terms. Your use of the source
|
||||
code for the these subcomponents is subject to the terms and
|
||||
conditions of the following licenses.
|
||||
|
||||
--------------------------------------------------
|
||||
Portions of the following files are licensed under the MIT License:
|
||||
|
||||
lib/erl/src/Makefile.am
|
||||
|
||||
Please see doc/otp-base-license.txt for the full terms of this license.
|
||||
|
||||
--------------------------------------------------
|
||||
For the aclocal/ax_boost_base.m4 and contrib/fb303/aclocal/ax_boost_base.m4 components:
|
||||
|
||||
# Copyright (c) 2007 Thomas Porschberg <thomas@randspringer.de>
|
||||
#
|
||||
# Copying and distribution of this file, with or without
|
||||
# modification, are permitted in any medium without royalty provided
|
||||
# the copyright notice and this notice are preserved.
|
||||
|
||||
--------------------------------------------------
|
||||
For the lib/nodejs/lib/thrift/json_parse.js:
|
||||
|
||||
/*
|
||||
json_parse.js
|
||||
2015-05-02
|
||||
Public Domain.
|
||||
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
|
||||
*/
|
||||
(By Douglas Crockford <douglas@crockford.com>)
|
||||
--------------------------------------------------
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
Apache Thrift
|
||||
Copyright 2006-2010 The Apache Software Foundation.
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
This package was debianized by Thrift Developer's <dev@thrift.apache.org>.
|
||||
|
||||
|
||||
This package and the Debian packaging is licensed under the Apache License,
|
||||
see `/usr/share/common-licenses/Apache-2.0'.
|
||||
|
||||
The following information was copied from Apache Thrift LICENSE file.
|
||||
|
||||
--------------------------------------------------
|
||||
SOFTWARE DISTRIBUTED WITH THRIFT:
|
||||
|
||||
The Apache Thrift software includes a number of subcomponents with
|
||||
separate copyright notices and license terms. Your use of the source
|
||||
code for the these subcomponents is subject to the terms and
|
||||
conditions of the following licenses.
|
||||
|
||||
--------------------------------------------------
|
||||
Portions of the following files are licensed under the MIT License:
|
||||
|
||||
lib/erl/src/Makefile.am
|
||||
|
||||
Please see doc/otp-base-license.txt for the full terms of this license.
|
||||
|
||||
|
||||
--------------------------------------------------
|
||||
The following files contain some portions of code contributed under
|
||||
the Thrift Software License (see doc/old-thrift-license.txt), and relicensed
|
||||
under the Apache 2.0 License:
|
||||
|
||||
compiler/cpp/Makefile.am
|
||||
compiler/cpp/src/generate/t_cocoa_generator.cc
|
||||
compiler/cpp/src/generate/t_cpp_generator.cc
|
||||
compiler/cpp/src/generate/t_csharp_generator.cc
|
||||
compiler/cpp/src/generate/t_erl_generator.cc
|
||||
compiler/cpp/src/generate/t_hs_generator.cc
|
||||
compiler/cpp/src/generate/t_java_generator.cc
|
||||
compiler/cpp/src/generate/t_ocaml_generator.cc
|
||||
compiler/cpp/src/generate/t_perl_generator.cc
|
||||
compiler/cpp/src/generate/t_php_generator.cc
|
||||
compiler/cpp/src/generate/t_py_generator.cc
|
||||
compiler/cpp/src/generate/t_rb_generator.cc
|
||||
compiler/cpp/src/generate/t_st_generator.cc
|
||||
compiler/cpp/src/generate/t_xsd_generator.cc
|
||||
compiler/cpp/src/main.cc
|
||||
compiler/cpp/src/parse/t_field.h
|
||||
compiler/cpp/src/parse/t_program.h
|
||||
compiler/cpp/src/platform.h
|
||||
compiler/cpp/src/thriftl.ll
|
||||
compiler/cpp/src/thrifty.yy
|
||||
lib/csharp/src/Protocol/TBinaryProtocol.cs
|
||||
lib/csharp/src/Protocol/TField.cs
|
||||
lib/csharp/src/Protocol/TList.cs
|
||||
lib/csharp/src/Protocol/TMap.cs
|
||||
lib/csharp/src/Protocol/TMessage.cs
|
||||
lib/csharp/src/Protocol/TMessageType.cs
|
||||
lib/csharp/src/Protocol/TProtocol.cs
|
||||
lib/csharp/src/Protocol/TProtocolException.cs
|
||||
lib/csharp/src/Protocol/TProtocolFactory.cs
|
||||
lib/csharp/src/Protocol/TProtocolUtil.cs
|
||||
lib/csharp/src/Protocol/TSet.cs
|
||||
lib/csharp/src/Protocol/TStruct.cs
|
||||
lib/csharp/src/Protocol/TType.cs
|
||||
lib/csharp/src/Server/TServer.cs
|
||||
lib/csharp/src/Server/TSimpleServer.cs
|
||||
lib/csharp/src/Server/TThreadPoolServer.cs
|
||||
lib/csharp/src/TApplicationException.cs
|
||||
lib/csharp/src/Thrift.csproj
|
||||
lib/csharp/src/Thrift.sln
|
||||
lib/csharp/src/TProcessor.cs
|
||||
lib/csharp/src/Transport/TServerSocket.cs
|
||||
lib/csharp/src/Transport/TServerTransport.cs
|
||||
lib/csharp/src/Transport/TSocket.cs
|
||||
lib/csharp/src/Transport/TStreamTransport.cs
|
||||
lib/csharp/src/Transport/TTransport.cs
|
||||
lib/csharp/src/Transport/TTransportException.cs
|
||||
lib/csharp/src/Transport/TTransportFactory.cs
|
||||
lib/csharp/ThriftMSBuildTask/Properties/AssemblyInfo.cs
|
||||
lib/csharp/ThriftMSBuildTask/ThriftBuild.cs
|
||||
lib/csharp/ThriftMSBuildTask/ThriftMSBuildTask.csproj
|
||||
lib/rb/lib/thrift.rb
|
||||
lib/st/README
|
||||
lib/st/thrift.st
|
||||
test/OptionalRequiredTest.cpp
|
||||
test/OptionalRequiredTest.thrift
|
||||
test/ThriftTest.thrift
|
||||
|
||||
--------------------------------------------------
|
||||
For the aclocal/ax_boost_base.m4 and contrib/fb303/aclocal/ax_boost_base.m4 components:
|
||||
|
||||
# Copyright (c) 2007 Thomas Porschberg <thomas@randspringer.de>
|
||||
#
|
||||
# Copying and distribution of this file, with or without
|
||||
# modification, are permitted in any medium without royalty provided
|
||||
# the copyright notice and this notice are preserved.
|
||||
|
||||
--------------------------------------------------
|
||||
For the compiler/cpp/src/md5.[ch] components:
|
||||
|
||||
/*
|
||||
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
|
||||
---------------------------------------------------
|
||||
For the lib/rb/setup.rb: Copyright (c) 2000-2005 Minero Aoki,
|
||||
lib/ocaml/OCamlMakefile and lib/ocaml/README-OCamlMakefile components:
|
||||
Copyright (C) 1999 - 2007 Markus Mottl
|
||||
|
||||
Licensed under the terms of the GNU Lesser General Public License 2.1
|
||||
(see doc/lgpl-2.1.txt for the full terms of this license)
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
)
|
||||
|
||||
type TBufferedTransportFactory struct {
|
||||
size int
|
||||
}
|
||||
|
||||
type TBufferedTransport struct {
|
||||
bufio.ReadWriter
|
||||
tp TTransport
|
||||
}
|
||||
|
||||
func (p *TBufferedTransportFactory) GetTransport(trans TTransport) TTransport {
|
||||
return NewTBufferedTransport(trans, p.size)
|
||||
}
|
||||
|
||||
func NewTBufferedTransportFactory(bufferSize int) *TBufferedTransportFactory {
|
||||
return &TBufferedTransportFactory{size: bufferSize}
|
||||
}
|
||||
|
||||
func NewTBufferedTransport(trans TTransport, bufferSize int) *TBufferedTransport {
|
||||
return &TBufferedTransport{
|
||||
ReadWriter: bufio.ReadWriter{
|
||||
Reader: bufio.NewReaderSize(trans, bufferSize),
|
||||
Writer: bufio.NewWriterSize(trans, bufferSize),
|
||||
},
|
||||
tp: trans,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *TBufferedTransport) IsOpen() bool {
|
||||
return p.tp.IsOpen()
|
||||
}
|
||||
|
||||
func (p *TBufferedTransport) Open() (err error) {
|
||||
return p.tp.Open()
|
||||
}
|
||||
|
||||
func (p *TBufferedTransport) Close() (err error) {
|
||||
return p.tp.Close()
|
||||
}
|
||||
|
||||
func (p *TBufferedTransport) Read(b []byte) (int, error) {
|
||||
n, err := p.ReadWriter.Read(b)
|
||||
if err != nil {
|
||||
p.ReadWriter.Reader.Reset(p.tp)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (p *TBufferedTransport) Write(b []byte) (int, error) {
|
||||
n, err := p.ReadWriter.Write(b)
|
||||
if err != nil {
|
||||
p.ReadWriter.Writer.Reset(p.tp)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (p *TBufferedTransport) Flush() error {
|
||||
if err := p.ReadWriter.Flush(); err != nil {
|
||||
p.ReadWriter.Writer.Reset(p.tp)
|
||||
return err
|
||||
}
|
||||
return p.tp.Flush()
|
||||
}
|
||||
|
||||
func (p *TBufferedTransport) RemainingBytes() (num_bytes uint64) {
|
||||
return p.tp.RemainingBytes()
|
||||
}
|
||||
-269
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
type TDebugProtocol struct {
|
||||
Delegate TProtocol
|
||||
LogPrefix string
|
||||
}
|
||||
|
||||
type TDebugProtocolFactory struct {
|
||||
Underlying TProtocolFactory
|
||||
LogPrefix string
|
||||
}
|
||||
|
||||
func NewTDebugProtocolFactory(underlying TProtocolFactory, logPrefix string) *TDebugProtocolFactory {
|
||||
return &TDebugProtocolFactory{
|
||||
Underlying: underlying,
|
||||
LogPrefix: logPrefix,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TDebugProtocolFactory) GetProtocol(trans TTransport) TProtocol {
|
||||
return &TDebugProtocol{
|
||||
Delegate: t.Underlying.GetProtocol(trans),
|
||||
LogPrefix: t.LogPrefix,
|
||||
}
|
||||
}
|
||||
|
||||
func (tdp *TDebugProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error {
|
||||
err := tdp.Delegate.WriteMessageBegin(name, typeId, seqid)
|
||||
log.Printf("%sWriteMessageBegin(name=%#v, typeId=%#v, seqid=%#v) => %#v", tdp.LogPrefix, name, typeId, seqid, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteMessageEnd() error {
|
||||
err := tdp.Delegate.WriteMessageEnd()
|
||||
log.Printf("%sWriteMessageEnd() => %#v", tdp.LogPrefix, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteStructBegin(name string) error {
|
||||
err := tdp.Delegate.WriteStructBegin(name)
|
||||
log.Printf("%sWriteStructBegin(name=%#v) => %#v", tdp.LogPrefix, name, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteStructEnd() error {
|
||||
err := tdp.Delegate.WriteStructEnd()
|
||||
log.Printf("%sWriteStructEnd() => %#v", tdp.LogPrefix, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {
|
||||
err := tdp.Delegate.WriteFieldBegin(name, typeId, id)
|
||||
log.Printf("%sWriteFieldBegin(name=%#v, typeId=%#v, id%#v) => %#v", tdp.LogPrefix, name, typeId, id, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteFieldEnd() error {
|
||||
err := tdp.Delegate.WriteFieldEnd()
|
||||
log.Printf("%sWriteFieldEnd() => %#v", tdp.LogPrefix, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteFieldStop() error {
|
||||
err := tdp.Delegate.WriteFieldStop()
|
||||
log.Printf("%sWriteFieldStop() => %#v", tdp.LogPrefix, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {
|
||||
err := tdp.Delegate.WriteMapBegin(keyType, valueType, size)
|
||||
log.Printf("%sWriteMapBegin(keyType=%#v, valueType=%#v, size=%#v) => %#v", tdp.LogPrefix, keyType, valueType, size, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteMapEnd() error {
|
||||
err := tdp.Delegate.WriteMapEnd()
|
||||
log.Printf("%sWriteMapEnd() => %#v", tdp.LogPrefix, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteListBegin(elemType TType, size int) error {
|
||||
err := tdp.Delegate.WriteListBegin(elemType, size)
|
||||
log.Printf("%sWriteListBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteListEnd() error {
|
||||
err := tdp.Delegate.WriteListEnd()
|
||||
log.Printf("%sWriteListEnd() => %#v", tdp.LogPrefix, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteSetBegin(elemType TType, size int) error {
|
||||
err := tdp.Delegate.WriteSetBegin(elemType, size)
|
||||
log.Printf("%sWriteSetBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteSetEnd() error {
|
||||
err := tdp.Delegate.WriteSetEnd()
|
||||
log.Printf("%sWriteSetEnd() => %#v", tdp.LogPrefix, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteBool(value bool) error {
|
||||
err := tdp.Delegate.WriteBool(value)
|
||||
log.Printf("%sWriteBool(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteByte(value int8) error {
|
||||
err := tdp.Delegate.WriteByte(value)
|
||||
log.Printf("%sWriteByte(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteI16(value int16) error {
|
||||
err := tdp.Delegate.WriteI16(value)
|
||||
log.Printf("%sWriteI16(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteI32(value int32) error {
|
||||
err := tdp.Delegate.WriteI32(value)
|
||||
log.Printf("%sWriteI32(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteI64(value int64) error {
|
||||
err := tdp.Delegate.WriteI64(value)
|
||||
log.Printf("%sWriteI64(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteDouble(value float64) error {
|
||||
err := tdp.Delegate.WriteDouble(value)
|
||||
log.Printf("%sWriteDouble(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteString(value string) error {
|
||||
err := tdp.Delegate.WriteString(value)
|
||||
log.Printf("%sWriteString(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||
return err
|
||||
}
|
||||
func (tdp *TDebugProtocol) WriteBinary(value []byte) error {
|
||||
err := tdp.Delegate.WriteBinary(value)
|
||||
log.Printf("%sWriteBinary(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (tdp *TDebugProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) {
|
||||
name, typeId, seqid, err = tdp.Delegate.ReadMessageBegin()
|
||||
log.Printf("%sReadMessageBegin() (name=%#v, typeId=%#v, seqid=%#v, err=%#v)", tdp.LogPrefix, name, typeId, seqid, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadMessageEnd() (err error) {
|
||||
err = tdp.Delegate.ReadMessageEnd()
|
||||
log.Printf("%sReadMessageEnd() err=%#v", tdp.LogPrefix, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadStructBegin() (name string, err error) {
|
||||
name, err = tdp.Delegate.ReadStructBegin()
|
||||
log.Printf("%sReadStructBegin() (name%#v, err=%#v)", tdp.LogPrefix, name, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadStructEnd() (err error) {
|
||||
err = tdp.Delegate.ReadStructEnd()
|
||||
log.Printf("%sReadStructEnd() err=%#v", tdp.LogPrefix, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) {
|
||||
name, typeId, id, err = tdp.Delegate.ReadFieldBegin()
|
||||
log.Printf("%sReadFieldBegin() (name=%#v, typeId=%#v, id=%#v, err=%#v)", tdp.LogPrefix, name, typeId, id, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadFieldEnd() (err error) {
|
||||
err = tdp.Delegate.ReadFieldEnd()
|
||||
log.Printf("%sReadFieldEnd() err=%#v", tdp.LogPrefix, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) {
|
||||
keyType, valueType, size, err = tdp.Delegate.ReadMapBegin()
|
||||
log.Printf("%sReadMapBegin() (keyType=%#v, valueType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, keyType, valueType, size, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadMapEnd() (err error) {
|
||||
err = tdp.Delegate.ReadMapEnd()
|
||||
log.Printf("%sReadMapEnd() err=%#v", tdp.LogPrefix, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadListBegin() (elemType TType, size int, err error) {
|
||||
elemType, size, err = tdp.Delegate.ReadListBegin()
|
||||
log.Printf("%sReadListBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadListEnd() (err error) {
|
||||
err = tdp.Delegate.ReadListEnd()
|
||||
log.Printf("%sReadListEnd() err=%#v", tdp.LogPrefix, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadSetBegin() (elemType TType, size int, err error) {
|
||||
elemType, size, err = tdp.Delegate.ReadSetBegin()
|
||||
log.Printf("%sReadSetBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadSetEnd() (err error) {
|
||||
err = tdp.Delegate.ReadSetEnd()
|
||||
log.Printf("%sReadSetEnd() err=%#v", tdp.LogPrefix, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadBool() (value bool, err error) {
|
||||
value, err = tdp.Delegate.ReadBool()
|
||||
log.Printf("%sReadBool() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadByte() (value int8, err error) {
|
||||
value, err = tdp.Delegate.ReadByte()
|
||||
log.Printf("%sReadByte() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadI16() (value int16, err error) {
|
||||
value, err = tdp.Delegate.ReadI16()
|
||||
log.Printf("%sReadI16() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadI32() (value int32, err error) {
|
||||
value, err = tdp.Delegate.ReadI32()
|
||||
log.Printf("%sReadI32() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadI64() (value int64, err error) {
|
||||
value, err = tdp.Delegate.ReadI64()
|
||||
log.Printf("%sReadI64() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadDouble() (value float64, err error) {
|
||||
value, err = tdp.Delegate.ReadDouble()
|
||||
log.Printf("%sReadDouble() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadString() (value string, err error) {
|
||||
value, err = tdp.Delegate.ReadString()
|
||||
log.Printf("%sReadString() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) ReadBinary() (value []byte, err error) {
|
||||
value, err = tdp.Delegate.ReadBinary()
|
||||
log.Printf("%sReadBinary() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) Skip(fieldType TType) (err error) {
|
||||
err = tdp.Delegate.Skip(fieldType)
|
||||
log.Printf("%sSkip(fieldType=%#v) (err=%#v)", tdp.LogPrefix, fieldType, err)
|
||||
return
|
||||
}
|
||||
func (tdp *TDebugProtocol) Flush() (err error) {
|
||||
err = tdp.Delegate.Flush()
|
||||
log.Printf("%sFlush() (err=%#v)", tdp.LogPrefix, err)
|
||||
return
|
||||
}
|
||||
|
||||
func (tdp *TDebugProtocol) Transport() TTransport {
|
||||
return tdp.Delegate.Transport()
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
type TDeserializer struct {
|
||||
Transport TTransport
|
||||
Protocol TProtocol
|
||||
}
|
||||
|
||||
func NewTDeserializer() *TDeserializer {
|
||||
var transport TTransport
|
||||
transport = NewTMemoryBufferLen(1024)
|
||||
|
||||
protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport)
|
||||
|
||||
return &TDeserializer{
|
||||
transport,
|
||||
protocol}
|
||||
}
|
||||
|
||||
func (t *TDeserializer) ReadString(msg TStruct, s string) (err error) {
|
||||
err = nil
|
||||
if _, err = t.Transport.Write([]byte(s)); err != nil {
|
||||
return
|
||||
}
|
||||
if err = msg.Read(t.Protocol); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *TDeserializer) Read(msg TStruct, b []byte) (err error) {
|
||||
err = nil
|
||||
if _, err = t.Transport.Write(b); err != nil {
|
||||
return
|
||||
}
|
||||
if err = msg.Read(t.Protocol); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
// Helper class that encapsulates field metadata.
|
||||
type field struct {
|
||||
name string
|
||||
typeId TType
|
||||
id int
|
||||
}
|
||||
|
||||
func newField(n string, t TType, i int) *field {
|
||||
return &field{name: n, typeId: t, id: i}
|
||||
}
|
||||
|
||||
func (p *field) Name() string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
return p.name
|
||||
}
|
||||
|
||||
func (p *field) TypeId() TType {
|
||||
if p == nil {
|
||||
return TType(VOID)
|
||||
}
|
||||
return p.typeId
|
||||
}
|
||||
|
||||
func (p *field) Id() int {
|
||||
if p == nil {
|
||||
return -1
|
||||
}
|
||||
return p.id
|
||||
}
|
||||
|
||||
func (p *field) String() string {
|
||||
if p == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return "<TField name:'" + p.name + "' type:" + string(p.typeId) + " field-id:" + string(p.id) + ">"
|
||||
}
|
||||
|
||||
var ANONYMOUS_FIELD *field
|
||||
|
||||
type fieldSlice []field
|
||||
|
||||
func (p fieldSlice) Len() int {
|
||||
return len(p)
|
||||
}
|
||||
|
||||
func (p fieldSlice) Less(i, j int) bool {
|
||||
return p[i].Id() < p[j].Id()
|
||||
}
|
||||
|
||||
func (p fieldSlice) Swap(i, j int) {
|
||||
p[i], p[j] = p[j], p[i]
|
||||
}
|
||||
|
||||
func init() {
|
||||
ANONYMOUS_FIELD = newField("", STOP, 0)
|
||||
}
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const DEFAULT_MAX_LENGTH = 16384000
|
||||
|
||||
type TFramedTransport struct {
|
||||
transport TTransport
|
||||
buf bytes.Buffer
|
||||
reader *bufio.Reader
|
||||
frameSize uint32 //Current remaining size of the frame. if ==0 read next frame header
|
||||
buffer [4]byte
|
||||
maxLength uint32
|
||||
}
|
||||
|
||||
type tFramedTransportFactory struct {
|
||||
factory TTransportFactory
|
||||
maxLength uint32
|
||||
}
|
||||
|
||||
func NewTFramedTransportFactory(factory TTransportFactory) TTransportFactory {
|
||||
return &tFramedTransportFactory{factory: factory, maxLength: DEFAULT_MAX_LENGTH}
|
||||
}
|
||||
|
||||
func NewTFramedTransportFactoryMaxLength(factory TTransportFactory, maxLength uint32) TTransportFactory {
|
||||
return &tFramedTransportFactory{factory: factory, maxLength: maxLength}
|
||||
}
|
||||
|
||||
func (p *tFramedTransportFactory) GetTransport(base TTransport) TTransport {
|
||||
return NewTFramedTransportMaxLength(p.factory.GetTransport(base), p.maxLength)
|
||||
}
|
||||
|
||||
func NewTFramedTransport(transport TTransport) *TFramedTransport {
|
||||
return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: DEFAULT_MAX_LENGTH}
|
||||
}
|
||||
|
||||
func NewTFramedTransportMaxLength(transport TTransport, maxLength uint32) *TFramedTransport {
|
||||
return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: maxLength}
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) Open() error {
|
||||
return p.transport.Open()
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) IsOpen() bool {
|
||||
return p.transport.IsOpen()
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) Close() error {
|
||||
return p.transport.Close()
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) Read(buf []byte) (l int, err error) {
|
||||
if p.frameSize == 0 {
|
||||
p.frameSize, err = p.readFrameHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if p.frameSize < uint32(len(buf)) {
|
||||
frameSize := p.frameSize
|
||||
tmp := make([]byte, p.frameSize)
|
||||
l, err = p.Read(tmp)
|
||||
copy(buf, tmp)
|
||||
if err == nil {
|
||||
err = NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", frameSize, len(buf)))
|
||||
return
|
||||
}
|
||||
}
|
||||
got, err := p.reader.Read(buf)
|
||||
p.frameSize = p.frameSize - uint32(got)
|
||||
//sanity check
|
||||
if p.frameSize < 0 {
|
||||
return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "Negative frame size")
|
||||
}
|
||||
return got, NewTTransportExceptionFromError(err)
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) ReadByte() (c byte, err error) {
|
||||
if p.frameSize == 0 {
|
||||
p.frameSize, err = p.readFrameHeader()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if p.frameSize < 1 {
|
||||
return 0, NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", p.frameSize, 1))
|
||||
}
|
||||
c, err = p.reader.ReadByte()
|
||||
if err == nil {
|
||||
p.frameSize--
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) Write(buf []byte) (int, error) {
|
||||
n, err := p.buf.Write(buf)
|
||||
return n, NewTTransportExceptionFromError(err)
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) WriteByte(c byte) error {
|
||||
return p.buf.WriteByte(c)
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) WriteString(s string) (n int, err error) {
|
||||
return p.buf.WriteString(s)
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) Flush() error {
|
||||
size := p.buf.Len()
|
||||
buf := p.buffer[:4]
|
||||
binary.BigEndian.PutUint32(buf, uint32(size))
|
||||
_, err := p.transport.Write(buf)
|
||||
if err != nil {
|
||||
return NewTTransportExceptionFromError(err)
|
||||
}
|
||||
if size > 0 {
|
||||
if n, err := p.buf.WriteTo(p.transport); err != nil {
|
||||
print("Error while flushing write buffer of size ", size, " to transport, only wrote ", n, " bytes: ", err.Error(), "\n")
|
||||
return NewTTransportExceptionFromError(err)
|
||||
}
|
||||
}
|
||||
err = p.transport.Flush()
|
||||
return NewTTransportExceptionFromError(err)
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) readFrameHeader() (uint32, error) {
|
||||
buf := p.buffer[:4]
|
||||
if _, err := io.ReadFull(p.reader, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
size := binary.BigEndian.Uint32(buf)
|
||||
if size < 0 || size > p.maxLength {
|
||||
return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, fmt.Sprintf("Incorrect frame size (%d)", size))
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func (p *TFramedTransport) RemainingBytes() (num_bytes uint64) {
|
||||
return uint64(p.frameSize)
|
||||
}
|
||||
|
||||
-258
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Default to using the shared http client. Library users are
|
||||
// free to change this global client or specify one through
|
||||
// THttpClientOptions.
|
||||
var DefaultHttpClient *http.Client = http.DefaultClient
|
||||
|
||||
type THttpClient struct {
|
||||
client *http.Client
|
||||
response *http.Response
|
||||
url *url.URL
|
||||
requestBuffer *bytes.Buffer
|
||||
header http.Header
|
||||
nsecConnectTimeout int64
|
||||
nsecReadTimeout int64
|
||||
}
|
||||
|
||||
type THttpClientTransportFactory struct {
|
||||
options THttpClientOptions
|
||||
url string
|
||||
isPost bool
|
||||
}
|
||||
|
||||
func (p *THttpClientTransportFactory) GetTransport(trans TTransport) TTransport {
|
||||
if trans != nil {
|
||||
t, ok := trans.(*THttpClient)
|
||||
if ok && t.url != nil {
|
||||
if t.requestBuffer != nil {
|
||||
t2, _ := NewTHttpPostClientWithOptions(t.url.String(), p.options)
|
||||
return t2
|
||||
}
|
||||
t2, _ := NewTHttpClientWithOptions(t.url.String(), p.options)
|
||||
return t2
|
||||
}
|
||||
}
|
||||
if p.isPost {
|
||||
s, _ := NewTHttpPostClientWithOptions(p.url, p.options)
|
||||
return s
|
||||
}
|
||||
s, _ := NewTHttpClientWithOptions(p.url, p.options)
|
||||
return s
|
||||
}
|
||||
|
||||
type THttpClientOptions struct {
|
||||
// If nil, DefaultHttpClient is used
|
||||
Client *http.Client
|
||||
}
|
||||
|
||||
func NewTHttpClientTransportFactory(url string) *THttpClientTransportFactory {
|
||||
return NewTHttpClientTransportFactoryWithOptions(url, THttpClientOptions{})
|
||||
}
|
||||
|
||||
func NewTHttpClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory {
|
||||
return &THttpClientTransportFactory{url: url, isPost: false, options: options}
|
||||
}
|
||||
|
||||
func NewTHttpPostClientTransportFactory(url string) *THttpClientTransportFactory {
|
||||
return NewTHttpPostClientTransportFactoryWithOptions(url, THttpClientOptions{})
|
||||
}
|
||||
|
||||
func NewTHttpPostClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory {
|
||||
return &THttpClientTransportFactory{url: url, isPost: true, options: options}
|
||||
}
|
||||
|
||||
func NewTHttpClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) {
|
||||
parsedURL, err := url.Parse(urlstr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := http.Get(urlstr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := options.Client
|
||||
if client == nil {
|
||||
client = DefaultHttpClient
|
||||
}
|
||||
httpHeader := map[string][]string{"Content-Type": []string{"application/x-thrift"}}
|
||||
return &THttpClient{client: client, response: response, url: parsedURL, header: httpHeader}, nil
|
||||
}
|
||||
|
||||
func NewTHttpClient(urlstr string) (TTransport, error) {
|
||||
return NewTHttpClientWithOptions(urlstr, THttpClientOptions{})
|
||||
}
|
||||
|
||||
func NewTHttpPostClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) {
|
||||
parsedURL, err := url.Parse(urlstr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := make([]byte, 0, 1024)
|
||||
client := options.Client
|
||||
if client == nil {
|
||||
client = DefaultHttpClient
|
||||
}
|
||||
httpHeader := map[string][]string{"Content-Type": []string{"application/x-thrift"}}
|
||||
return &THttpClient{client: client, url: parsedURL, requestBuffer: bytes.NewBuffer(buf), header: httpHeader}, nil
|
||||
}
|
||||
|
||||
func NewTHttpPostClient(urlstr string) (TTransport, error) {
|
||||
return NewTHttpPostClientWithOptions(urlstr, THttpClientOptions{})
|
||||
}
|
||||
|
||||
// Set the HTTP Header for this specific Thrift Transport
|
||||
// It is important that you first assert the TTransport as a THttpClient type
|
||||
// like so:
|
||||
//
|
||||
// httpTrans := trans.(THttpClient)
|
||||
// httpTrans.SetHeader("User-Agent","Thrift Client 1.0")
|
||||
func (p *THttpClient) SetHeader(key string, value string) {
|
||||
p.header.Add(key, value)
|
||||
}
|
||||
|
||||
// Get the HTTP Header represented by the supplied Header Key for this specific Thrift Transport
|
||||
// It is important that you first assert the TTransport as a THttpClient type
|
||||
// like so:
|
||||
//
|
||||
// httpTrans := trans.(THttpClient)
|
||||
// hdrValue := httpTrans.GetHeader("User-Agent")
|
||||
func (p *THttpClient) GetHeader(key string) string {
|
||||
return p.header.Get(key)
|
||||
}
|
||||
|
||||
// Deletes the HTTP Header given a Header Key for this specific Thrift Transport
|
||||
// It is important that you first assert the TTransport as a THttpClient type
|
||||
// like so:
|
||||
//
|
||||
// httpTrans := trans.(THttpClient)
|
||||
// httpTrans.DelHeader("User-Agent")
|
||||
func (p *THttpClient) DelHeader(key string) {
|
||||
p.header.Del(key)
|
||||
}
|
||||
|
||||
func (p *THttpClient) Open() error {
|
||||
// do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *THttpClient) IsOpen() bool {
|
||||
return p.response != nil || p.requestBuffer != nil
|
||||
}
|
||||
|
||||
func (p *THttpClient) closeResponse() error {
|
||||
var err error
|
||||
if p.response != nil && p.response.Body != nil {
|
||||
// The docs specify that if keepalive is enabled and the response body is not
|
||||
// read to completion the connection will never be returned to the pool and
|
||||
// reused. Errors are being ignored here because if the connection is invalid
|
||||
// and this fails for some reason, the Close() method will do any remaining
|
||||
// cleanup.
|
||||
io.Copy(ioutil.Discard, p.response.Body)
|
||||
|
||||
err = p.response.Body.Close()
|
||||
}
|
||||
|
||||
p.response = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *THttpClient) Close() error {
|
||||
if p.requestBuffer != nil {
|
||||
p.requestBuffer.Reset()
|
||||
p.requestBuffer = nil
|
||||
}
|
||||
return p.closeResponse()
|
||||
}
|
||||
|
||||
func (p *THttpClient) Read(buf []byte) (int, error) {
|
||||
if p.response == nil {
|
||||
return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.")
|
||||
}
|
||||
n, err := p.response.Body.Read(buf)
|
||||
if n > 0 && (err == nil || err == io.EOF) {
|
||||
return n, nil
|
||||
}
|
||||
return n, NewTTransportExceptionFromError(err)
|
||||
}
|
||||
|
||||
func (p *THttpClient) ReadByte() (c byte, err error) {
|
||||
return readByte(p.response.Body)
|
||||
}
|
||||
|
||||
func (p *THttpClient) Write(buf []byte) (int, error) {
|
||||
n, err := p.requestBuffer.Write(buf)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (p *THttpClient) WriteByte(c byte) error {
|
||||
return p.requestBuffer.WriteByte(c)
|
||||
}
|
||||
|
||||
func (p *THttpClient) WriteString(s string) (n int, err error) {
|
||||
return p.requestBuffer.WriteString(s)
|
||||
}
|
||||
|
||||
func (p *THttpClient) Flush() error {
|
||||
// Close any previous response body to avoid leaking connections.
|
||||
p.closeResponse()
|
||||
|
||||
req, err := http.NewRequest("POST", p.url.String(), p.requestBuffer)
|
||||
if err != nil {
|
||||
return NewTTransportExceptionFromError(err)
|
||||
}
|
||||
req.Header = p.header
|
||||
response, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
return NewTTransportExceptionFromError(err)
|
||||
}
|
||||
if response.StatusCode != http.StatusOK {
|
||||
// Close the response to avoid leaking file descriptors. closeResponse does
|
||||
// more than just call Close(), so temporarily assign it and reuse the logic.
|
||||
p.response = response
|
||||
p.closeResponse()
|
||||
|
||||
// TODO(pomack) log bad response
|
||||
return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "HTTP Response code: "+strconv.Itoa(response.StatusCode))
|
||||
}
|
||||
p.response = response
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *THttpClient) RemainingBytes() (num_bytes uint64) {
|
||||
len := p.response.ContentLength
|
||||
if len >= 0 {
|
||||
return uint64(len)
|
||||
}
|
||||
|
||||
const maxSize = ^uint64(0)
|
||||
return maxSize // the thruth is, we just don't know unless framed is used
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import "net/http"
|
||||
|
||||
// NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function
|
||||
func NewThriftHandlerFunc(processor TProcessor,
|
||||
inPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/x-thrift")
|
||||
|
||||
transport := NewStreamTransport(r.Body, w)
|
||||
processor.Process(inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport))
|
||||
}
|
||||
}
|
||||
-214
@@ -1,214 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
)
|
||||
|
||||
// StreamTransport is a Transport made of an io.Reader and/or an io.Writer
|
||||
type StreamTransport struct {
|
||||
io.Reader
|
||||
io.Writer
|
||||
isReadWriter bool
|
||||
closed bool
|
||||
}
|
||||
|
||||
type StreamTransportFactory struct {
|
||||
Reader io.Reader
|
||||
Writer io.Writer
|
||||
isReadWriter bool
|
||||
}
|
||||
|
||||
func (p *StreamTransportFactory) GetTransport(trans TTransport) TTransport {
|
||||
if trans != nil {
|
||||
t, ok := trans.(*StreamTransport)
|
||||
if ok {
|
||||
if t.isReadWriter {
|
||||
return NewStreamTransportRW(t.Reader.(io.ReadWriter))
|
||||
}
|
||||
if t.Reader != nil && t.Writer != nil {
|
||||
return NewStreamTransport(t.Reader, t.Writer)
|
||||
}
|
||||
if t.Reader != nil && t.Writer == nil {
|
||||
return NewStreamTransportR(t.Reader)
|
||||
}
|
||||
if t.Reader == nil && t.Writer != nil {
|
||||
return NewStreamTransportW(t.Writer)
|
||||
}
|
||||
return &StreamTransport{}
|
||||
}
|
||||
}
|
||||
if p.isReadWriter {
|
||||
return NewStreamTransportRW(p.Reader.(io.ReadWriter))
|
||||
}
|
||||
if p.Reader != nil && p.Writer != nil {
|
||||
return NewStreamTransport(p.Reader, p.Writer)
|
||||
}
|
||||
if p.Reader != nil && p.Writer == nil {
|
||||
return NewStreamTransportR(p.Reader)
|
||||
}
|
||||
if p.Reader == nil && p.Writer != nil {
|
||||
return NewStreamTransportW(p.Writer)
|
||||
}
|
||||
return &StreamTransport{}
|
||||
}
|
||||
|
||||
func NewStreamTransportFactory(reader io.Reader, writer io.Writer, isReadWriter bool) *StreamTransportFactory {
|
||||
return &StreamTransportFactory{Reader: reader, Writer: writer, isReadWriter: isReadWriter}
|
||||
}
|
||||
|
||||
func NewStreamTransport(r io.Reader, w io.Writer) *StreamTransport {
|
||||
return &StreamTransport{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)}
|
||||
}
|
||||
|
||||
func NewStreamTransportR(r io.Reader) *StreamTransport {
|
||||
return &StreamTransport{Reader: bufio.NewReader(r)}
|
||||
}
|
||||
|
||||
func NewStreamTransportW(w io.Writer) *StreamTransport {
|
||||
return &StreamTransport{Writer: bufio.NewWriter(w)}
|
||||
}
|
||||
|
||||
func NewStreamTransportRW(rw io.ReadWriter) *StreamTransport {
|
||||
bufrw := bufio.NewReadWriter(bufio.NewReader(rw), bufio.NewWriter(rw))
|
||||
return &StreamTransport{Reader: bufrw, Writer: bufrw, isReadWriter: true}
|
||||
}
|
||||
|
||||
func (p *StreamTransport) IsOpen() bool {
|
||||
return !p.closed
|
||||
}
|
||||
|
||||
// implicitly opened on creation, can't be reopened once closed
|
||||
func (p *StreamTransport) Open() error {
|
||||
if !p.closed {
|
||||
return NewTTransportException(ALREADY_OPEN, "StreamTransport already open.")
|
||||
} else {
|
||||
return NewTTransportException(NOT_OPEN, "cannot reopen StreamTransport.")
|
||||
}
|
||||
}
|
||||
|
||||
// Closes both the input and output streams.
|
||||
func (p *StreamTransport) Close() error {
|
||||
if p.closed {
|
||||
return NewTTransportException(NOT_OPEN, "StreamTransport already closed.")
|
||||
}
|
||||
p.closed = true
|
||||
closedReader := false
|
||||
if p.Reader != nil {
|
||||
c, ok := p.Reader.(io.Closer)
|
||||
if ok {
|
||||
e := c.Close()
|
||||
closedReader = true
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
p.Reader = nil
|
||||
}
|
||||
if p.Writer != nil && (!closedReader || !p.isReadWriter) {
|
||||
c, ok := p.Writer.(io.Closer)
|
||||
if ok {
|
||||
e := c.Close()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
p.Writer = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flushes the underlying output stream if not null.
|
||||
func (p *StreamTransport) Flush() error {
|
||||
if p.Writer == nil {
|
||||
return NewTTransportException(NOT_OPEN, "Cannot flush null outputStream")
|
||||
}
|
||||
f, ok := p.Writer.(Flusher)
|
||||
if ok {
|
||||
err := f.Flush()
|
||||
if err != nil {
|
||||
return NewTTransportExceptionFromError(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *StreamTransport) Read(c []byte) (n int, err error) {
|
||||
n, err = p.Reader.Read(c)
|
||||
if err != nil {
|
||||
err = NewTTransportExceptionFromError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *StreamTransport) ReadByte() (c byte, err error) {
|
||||
f, ok := p.Reader.(io.ByteReader)
|
||||
if ok {
|
||||
c, err = f.ReadByte()
|
||||
} else {
|
||||
c, err = readByte(p.Reader)
|
||||
}
|
||||
if err != nil {
|
||||
err = NewTTransportExceptionFromError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *StreamTransport) Write(c []byte) (n int, err error) {
|
||||
n, err = p.Writer.Write(c)
|
||||
if err != nil {
|
||||
err = NewTTransportExceptionFromError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *StreamTransport) WriteByte(c byte) (err error) {
|
||||
f, ok := p.Writer.(io.ByteWriter)
|
||||
if ok {
|
||||
err = f.WriteByte(c)
|
||||
} else {
|
||||
err = writeByte(p.Writer, c)
|
||||
}
|
||||
if err != nil {
|
||||
err = NewTTransportExceptionFromError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *StreamTransport) WriteString(s string) (n int, err error) {
|
||||
f, ok := p.Writer.(stringWriter)
|
||||
if ok {
|
||||
n, err = f.WriteString(s)
|
||||
} else {
|
||||
n, err = p.Writer.Write([]byte(s))
|
||||
}
|
||||
if err != nil {
|
||||
err = NewTTransportExceptionFromError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *StreamTransport) RemainingBytes() (num_bytes uint64) {
|
||||
const maxSize = ^uint64(0)
|
||||
return maxSize // the thruth is, we just don't know unless framed is used
|
||||
}
|
||||
|
||||
-583
@@ -1,583 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
THRIFT_JSON_PROTOCOL_VERSION = 1
|
||||
)
|
||||
|
||||
// for references to _ParseContext see tsimplejson_protocol.go
|
||||
|
||||
// JSON protocol implementation for thrift.
|
||||
//
|
||||
// This protocol produces/consumes a simple output format
|
||||
// suitable for parsing by scripting languages. It should not be
|
||||
// confused with the full-featured TJSONProtocol.
|
||||
//
|
||||
type TJSONProtocol struct {
|
||||
*TSimpleJSONProtocol
|
||||
}
|
||||
|
||||
// Constructor
|
||||
func NewTJSONProtocol(t TTransport) *TJSONProtocol {
|
||||
v := &TJSONProtocol{TSimpleJSONProtocol: NewTSimpleJSONProtocol(t)}
|
||||
v.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL))
|
||||
v.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL))
|
||||
return v
|
||||
}
|
||||
|
||||
// Factory
|
||||
type TJSONProtocolFactory struct{}
|
||||
|
||||
func (p *TJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol {
|
||||
return NewTJSONProtocol(trans)
|
||||
}
|
||||
|
||||
func NewTJSONProtocolFactory() *TJSONProtocolFactory {
|
||||
return &TJSONProtocolFactory{}
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error {
|
||||
p.resetContextStack() // THRIFT-3735
|
||||
if e := p.OutputListBegin(); e != nil {
|
||||
return e
|
||||
}
|
||||
if e := p.WriteI32(THRIFT_JSON_PROTOCOL_VERSION); e != nil {
|
||||
return e
|
||||
}
|
||||
if e := p.WriteString(name); e != nil {
|
||||
return e
|
||||
}
|
||||
if e := p.WriteByte(int8(typeId)); e != nil {
|
||||
return e
|
||||
}
|
||||
if e := p.WriteI32(seqId); e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteMessageEnd() error {
|
||||
return p.OutputListEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteStructBegin(name string) error {
|
||||
if e := p.OutputObjectBegin(); e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteStructEnd() error {
|
||||
return p.OutputObjectEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error {
|
||||
if e := p.WriteI16(id); e != nil {
|
||||
return e
|
||||
}
|
||||
if e := p.OutputObjectBegin(); e != nil {
|
||||
return e
|
||||
}
|
||||
s, e1 := p.TypeIdToString(typeId)
|
||||
if e1 != nil {
|
||||
return e1
|
||||
}
|
||||
if e := p.WriteString(s); e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteFieldEnd() error {
|
||||
return p.OutputObjectEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteFieldStop() error { return nil }
|
||||
|
||||
func (p *TJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error {
|
||||
if e := p.OutputListBegin(); e != nil {
|
||||
return e
|
||||
}
|
||||
s, e1 := p.TypeIdToString(keyType)
|
||||
if e1 != nil {
|
||||
return e1
|
||||
}
|
||||
if e := p.WriteString(s); e != nil {
|
||||
return e
|
||||
}
|
||||
s, e1 = p.TypeIdToString(valueType)
|
||||
if e1 != nil {
|
||||
return e1
|
||||
}
|
||||
if e := p.WriteString(s); e != nil {
|
||||
return e
|
||||
}
|
||||
if e := p.WriteI64(int64(size)); e != nil {
|
||||
return e
|
||||
}
|
||||
return p.OutputObjectBegin()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteMapEnd() error {
|
||||
if e := p.OutputObjectEnd(); e != nil {
|
||||
return e
|
||||
}
|
||||
return p.OutputListEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteListBegin(elemType TType, size int) error {
|
||||
return p.OutputElemListBegin(elemType, size)
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteListEnd() error {
|
||||
return p.OutputListEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteSetBegin(elemType TType, size int) error {
|
||||
return p.OutputElemListBegin(elemType, size)
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteSetEnd() error {
|
||||
return p.OutputListEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteBool(b bool) error {
|
||||
if b {
|
||||
return p.WriteI32(1)
|
||||
}
|
||||
return p.WriteI32(0)
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteByte(b int8) error {
|
||||
return p.WriteI32(int32(b))
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteI16(v int16) error {
|
||||
return p.WriteI32(int32(v))
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteI32(v int32) error {
|
||||
return p.OutputI64(int64(v))
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteI64(v int64) error {
|
||||
return p.OutputI64(int64(v))
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteDouble(v float64) error {
|
||||
return p.OutputF64(v)
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteString(v string) error {
|
||||
return p.OutputString(v)
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) WriteBinary(v []byte) error {
|
||||
// JSON library only takes in a string,
|
||||
// not an arbitrary byte array, to ensure bytes are transmitted
|
||||
// efficiently we must convert this into a valid JSON string
|
||||
// therefore we use base64 encoding to avoid excessive escaping/quoting
|
||||
if e := p.OutputPreValue(); e != nil {
|
||||
return e
|
||||
}
|
||||
if _, e := p.write(JSON_QUOTE_BYTES); e != nil {
|
||||
return NewTProtocolException(e)
|
||||
}
|
||||
writer := base64.NewEncoder(base64.StdEncoding, p.writer)
|
||||
if _, e := writer.Write(v); e != nil {
|
||||
p.writer.Reset(p.trans) // THRIFT-3735
|
||||
return NewTProtocolException(e)
|
||||
}
|
||||
if e := writer.Close(); e != nil {
|
||||
return NewTProtocolException(e)
|
||||
}
|
||||
if _, e := p.write(JSON_QUOTE_BYTES); e != nil {
|
||||
return NewTProtocolException(e)
|
||||
}
|
||||
return p.OutputPostValue()
|
||||
}
|
||||
|
||||
// Reading methods.
|
||||
func (p *TJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) {
|
||||
p.resetContextStack() // THRIFT-3735
|
||||
if isNull, err := p.ParseListBegin(); isNull || err != nil {
|
||||
return name, typeId, seqId, err
|
||||
}
|
||||
version, err := p.ReadI32()
|
||||
if err != nil {
|
||||
return name, typeId, seqId, err
|
||||
}
|
||||
if version != THRIFT_JSON_PROTOCOL_VERSION {
|
||||
e := fmt.Errorf("Unknown Protocol version %d, expected version %d", version, THRIFT_JSON_PROTOCOL_VERSION)
|
||||
return name, typeId, seqId, NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||
|
||||
}
|
||||
if name, err = p.ReadString(); err != nil {
|
||||
return name, typeId, seqId, err
|
||||
}
|
||||
bTypeId, err := p.ReadByte()
|
||||
typeId = TMessageType(bTypeId)
|
||||
if err != nil {
|
||||
return name, typeId, seqId, err
|
||||
}
|
||||
if seqId, err = p.ReadI32(); err != nil {
|
||||
return name, typeId, seqId, err
|
||||
}
|
||||
return name, typeId, seqId, nil
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadMessageEnd() error {
|
||||
err := p.ParseListEnd()
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadStructBegin() (name string, err error) {
|
||||
_, err = p.ParseObjectStart()
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadStructEnd() error {
|
||||
return p.ParseObjectEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadFieldBegin() (string, TType, int16, error) {
|
||||
b, _ := p.reader.Peek(1)
|
||||
if len(b) < 1 || b[0] == JSON_RBRACE[0] || b[0] == JSON_RBRACKET[0] {
|
||||
return "", STOP, -1, nil
|
||||
}
|
||||
fieldId, err := p.ReadI16()
|
||||
if err != nil {
|
||||
return "", STOP, fieldId, err
|
||||
}
|
||||
if _, err = p.ParseObjectStart(); err != nil {
|
||||
return "", STOP, fieldId, err
|
||||
}
|
||||
sType, err := p.ReadString()
|
||||
if err != nil {
|
||||
return "", STOP, fieldId, err
|
||||
}
|
||||
fType, err := p.StringToTypeId(sType)
|
||||
return "", fType, fieldId, err
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadFieldEnd() error {
|
||||
return p.ParseObjectEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) {
|
||||
if isNull, e := p.ParseListBegin(); isNull || e != nil {
|
||||
return VOID, VOID, 0, e
|
||||
}
|
||||
|
||||
// read keyType
|
||||
sKeyType, e := p.ReadString()
|
||||
if e != nil {
|
||||
return keyType, valueType, size, e
|
||||
}
|
||||
keyType, e = p.StringToTypeId(sKeyType)
|
||||
if e != nil {
|
||||
return keyType, valueType, size, e
|
||||
}
|
||||
|
||||
// read valueType
|
||||
sValueType, e := p.ReadString()
|
||||
if e != nil {
|
||||
return keyType, valueType, size, e
|
||||
}
|
||||
valueType, e = p.StringToTypeId(sValueType)
|
||||
if e != nil {
|
||||
return keyType, valueType, size, e
|
||||
}
|
||||
|
||||
// read size
|
||||
iSize, e := p.ReadI64()
|
||||
if e != nil {
|
||||
return keyType, valueType, size, e
|
||||
}
|
||||
size = int(iSize)
|
||||
|
||||
_, e = p.ParseObjectStart()
|
||||
return keyType, valueType, size, e
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadMapEnd() error {
|
||||
e := p.ParseObjectEnd()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
return p.ParseListEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadListBegin() (elemType TType, size int, e error) {
|
||||
return p.ParseElemListBegin()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadListEnd() error {
|
||||
return p.ParseListEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) {
|
||||
return p.ParseElemListBegin()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadSetEnd() error {
|
||||
return p.ParseListEnd()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadBool() (bool, error) {
|
||||
value, err := p.ReadI32()
|
||||
return (value != 0), err
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadByte() (int8, error) {
|
||||
v, err := p.ReadI64()
|
||||
return int8(v), err
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadI16() (int16, error) {
|
||||
v, err := p.ReadI64()
|
||||
return int16(v), err
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadI32() (int32, error) {
|
||||
v, err := p.ReadI64()
|
||||
return int32(v), err
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadI64() (int64, error) {
|
||||
v, _, err := p.ParseI64()
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadDouble() (float64, error) {
|
||||
v, _, err := p.ParseF64()
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadString() (string, error) {
|
||||
var v string
|
||||
if err := p.ParsePreValue(); err != nil {
|
||||
return v, err
|
||||
}
|
||||
f, _ := p.reader.Peek(1)
|
||||
if len(f) > 0 && f[0] == JSON_QUOTE {
|
||||
p.reader.ReadByte()
|
||||
value, err := p.ParseStringBody()
|
||||
v = value
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
} else if len(f) > 0 && f[0] == JSON_NULL[0] {
|
||||
b := make([]byte, len(JSON_NULL))
|
||||
_, err := p.reader.Read(b)
|
||||
if err != nil {
|
||||
return v, NewTProtocolException(err)
|
||||
}
|
||||
if string(b) != string(JSON_NULL) {
|
||||
e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b))
|
||||
return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||
}
|
||||
} else {
|
||||
e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f))
|
||||
return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||
}
|
||||
return v, p.ParsePostValue()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ReadBinary() ([]byte, error) {
|
||||
var v []byte
|
||||
if err := p.ParsePreValue(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := p.reader.Peek(1)
|
||||
if len(f) > 0 && f[0] == JSON_QUOTE {
|
||||
p.reader.ReadByte()
|
||||
value, err := p.ParseBase64EncodedBody()
|
||||
v = value
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
} else if len(f) > 0 && f[0] == JSON_NULL[0] {
|
||||
b := make([]byte, len(JSON_NULL))
|
||||
_, err := p.reader.Read(b)
|
||||
if err != nil {
|
||||
return v, NewTProtocolException(err)
|
||||
}
|
||||
if string(b) != string(JSON_NULL) {
|
||||
e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b))
|
||||
return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||
}
|
||||
} else {
|
||||
e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f))
|
||||
return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||
}
|
||||
|
||||
return v, p.ParsePostValue()
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) Flush() (err error) {
|
||||
err = p.writer.Flush()
|
||||
if err == nil {
|
||||
err = p.trans.Flush()
|
||||
}
|
||||
return NewTProtocolException(err)
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) Skip(fieldType TType) (err error) {
|
||||
return SkipDefaultDepth(p, fieldType)
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) Transport() TTransport {
|
||||
return p.trans
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) OutputElemListBegin(elemType TType, size int) error {
|
||||
if e := p.OutputListBegin(); e != nil {
|
||||
return e
|
||||
}
|
||||
s, e1 := p.TypeIdToString(elemType)
|
||||
if e1 != nil {
|
||||
return e1
|
||||
}
|
||||
if e := p.WriteString(s); e != nil {
|
||||
return e
|
||||
}
|
||||
if e := p.WriteI64(int64(size)); e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) {
|
||||
if isNull, e := p.ParseListBegin(); isNull || e != nil {
|
||||
return VOID, 0, e
|
||||
}
|
||||
sElemType, err := p.ReadString()
|
||||
if err != nil {
|
||||
return VOID, size, err
|
||||
}
|
||||
elemType, err = p.StringToTypeId(sElemType)
|
||||
if err != nil {
|
||||
return elemType, size, err
|
||||
}
|
||||
nSize, err2 := p.ReadI64()
|
||||
size = int(nSize)
|
||||
return elemType, size, err2
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) readElemListBegin() (elemType TType, size int, e error) {
|
||||
if isNull, e := p.ParseListBegin(); isNull || e != nil {
|
||||
return VOID, 0, e
|
||||
}
|
||||
sElemType, err := p.ReadString()
|
||||
if err != nil {
|
||||
return VOID, size, err
|
||||
}
|
||||
elemType, err = p.StringToTypeId(sElemType)
|
||||
if err != nil {
|
||||
return elemType, size, err
|
||||
}
|
||||
nSize, err2 := p.ReadI64()
|
||||
size = int(nSize)
|
||||
return elemType, size, err2
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) writeElemListBegin(elemType TType, size int) error {
|
||||
if e := p.OutputListBegin(); e != nil {
|
||||
return e
|
||||
}
|
||||
s, e1 := p.TypeIdToString(elemType)
|
||||
if e1 != nil {
|
||||
return e1
|
||||
}
|
||||
if e := p.OutputString(s); e != nil {
|
||||
return e
|
||||
}
|
||||
if e := p.OutputI64(int64(size)); e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) TypeIdToString(fieldType TType) (string, error) {
|
||||
switch byte(fieldType) {
|
||||
case BOOL:
|
||||
return "tf", nil
|
||||
case BYTE:
|
||||
return "i8", nil
|
||||
case I16:
|
||||
return "i16", nil
|
||||
case I32:
|
||||
return "i32", nil
|
||||
case I64:
|
||||
return "i64", nil
|
||||
case DOUBLE:
|
||||
return "dbl", nil
|
||||
case STRING:
|
||||
return "str", nil
|
||||
case STRUCT:
|
||||
return "rec", nil
|
||||
case MAP:
|
||||
return "map", nil
|
||||
case SET:
|
||||
return "set", nil
|
||||
case LIST:
|
||||
return "lst", nil
|
||||
}
|
||||
|
||||
e := fmt.Errorf("Unknown fieldType: %d", int(fieldType))
|
||||
return "", NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||
}
|
||||
|
||||
func (p *TJSONProtocol) StringToTypeId(fieldType string) (TType, error) {
|
||||
switch fieldType {
|
||||
case "tf":
|
||||
return TType(BOOL), nil
|
||||
case "i8":
|
||||
return TType(BYTE), nil
|
||||
case "i16":
|
||||
return TType(I16), nil
|
||||
case "i32":
|
||||
return TType(I32), nil
|
||||
case "i64":
|
||||
return TType(I64), nil
|
||||
case "dbl":
|
||||
return TType(DOUBLE), nil
|
||||
case "str":
|
||||
return TType(STRING), nil
|
||||
case "rec":
|
||||
return TType(STRUCT), nil
|
||||
case "map":
|
||||
return TType(MAP), nil
|
||||
case "set":
|
||||
return TType(SET), nil
|
||||
case "lst":
|
||||
return TType(LIST), nil
|
||||
}
|
||||
|
||||
e := fmt.Errorf("Unknown type identifier: %s", fieldType)
|
||||
return TType(STOP), NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||
}
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
TMultiplexedProtocol is a protocol-independent concrete decorator
|
||||
that allows a Thrift client to communicate with a multiplexing Thrift server,
|
||||
by prepending the service name to the function name during function calls.
|
||||
|
||||
NOTE: THIS IS NOT USED BY SERVERS. On the server, use TMultiplexedProcessor to handle request
|
||||
from a multiplexing client.
|
||||
|
||||
This example uses a single socket transport to invoke two services:
|
||||
|
||||
socket := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT)
|
||||
transport := thrift.NewTFramedTransport(socket)
|
||||
protocol := thrift.NewTBinaryProtocolTransport(transport)
|
||||
|
||||
mp := thrift.NewTMultiplexedProtocol(protocol, "Calculator")
|
||||
service := Calculator.NewCalculatorClient(mp)
|
||||
|
||||
mp2 := thrift.NewTMultiplexedProtocol(protocol, "WeatherReport")
|
||||
service2 := WeatherReport.NewWeatherReportClient(mp2)
|
||||
|
||||
err := transport.Open()
|
||||
if err != nil {
|
||||
t.Fatal("Unable to open client socket", err)
|
||||
}
|
||||
|
||||
fmt.Println(service.Add(2,2))
|
||||
fmt.Println(service2.GetTemperature())
|
||||
*/
|
||||
|
||||
type TMultiplexedProtocol struct {
|
||||
TProtocol
|
||||
serviceName string
|
||||
}
|
||||
|
||||
const MULTIPLEXED_SEPARATOR = ":"
|
||||
|
||||
func NewTMultiplexedProtocol(protocol TProtocol, serviceName string) *TMultiplexedProtocol {
|
||||
return &TMultiplexedProtocol{
|
||||
TProtocol: protocol,
|
||||
serviceName: serviceName,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TMultiplexedProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error {
|
||||
if typeId == CALL || typeId == ONEWAY {
|
||||
return t.TProtocol.WriteMessageBegin(t.serviceName+MULTIPLEXED_SEPARATOR+name, typeId, seqid)
|
||||
} else {
|
||||
return t.TProtocol.WriteMessageBegin(name, typeId, seqid)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TMultiplexedProcessor is a TProcessor allowing
|
||||
a single TServer to provide multiple services.
|
||||
|
||||
To do so, you instantiate the processor and then register additional
|
||||
processors with it, as shown in the following example:
|
||||
|
||||
var processor = thrift.NewTMultiplexedProcessor()
|
||||
|
||||
firstProcessor :=
|
||||
processor.RegisterProcessor("FirstService", firstProcessor)
|
||||
|
||||
processor.registerProcessor(
|
||||
"Calculator",
|
||||
Calculator.NewCalculatorProcessor(&CalculatorHandler{}),
|
||||
)
|
||||
|
||||
processor.registerProcessor(
|
||||
"WeatherReport",
|
||||
WeatherReport.NewWeatherReportProcessor(&WeatherReportHandler{}),
|
||||
)
|
||||
|
||||
serverTransport, err := thrift.NewTServerSocketTimeout(addr, TIMEOUT)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to create server socket", err)
|
||||
}
|
||||
server := thrift.NewTSimpleServer2(processor, serverTransport)
|
||||
server.Serve();
|
||||
*/
|
||||
|
||||
type TMultiplexedProcessor struct {
|
||||
serviceProcessorMap map[string]TProcessor
|
||||
DefaultProcessor TProcessor
|
||||
}
|
||||
|
||||
func NewTMultiplexedProcessor() *TMultiplexedProcessor {
|
||||
return &TMultiplexedProcessor{
|
||||
serviceProcessorMap: make(map[string]TProcessor),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TMultiplexedProcessor) RegisterDefault(processor TProcessor) {
|
||||
t.DefaultProcessor = processor
|
||||
}
|
||||
|
||||
func (t *TMultiplexedProcessor) RegisterProcessor(name string, processor TProcessor) {
|
||||
if t.serviceProcessorMap == nil {
|
||||
t.serviceProcessorMap = make(map[string]TProcessor)
|
||||
}
|
||||
t.serviceProcessorMap[name] = processor
|
||||
}
|
||||
|
||||
func (t *TMultiplexedProcessor) Process(in, out TProtocol) (bool, TException) {
|
||||
name, typeId, seqid, err := in.ReadMessageBegin()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if typeId != CALL && typeId != ONEWAY {
|
||||
return false, fmt.Errorf("Unexpected message type %v", typeId)
|
||||
}
|
||||
//extract the service name
|
||||
v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2)
|
||||
if len(v) != 2 {
|
||||
if t.DefaultProcessor != nil {
|
||||
smb := NewStoredMessageProtocol(in, name, typeId, seqid)
|
||||
return t.DefaultProcessor.Process(smb, out)
|
||||
}
|
||||
return false, fmt.Errorf("Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?", name)
|
||||
}
|
||||
actualProcessor, ok := t.serviceProcessorMap[v[0]]
|
||||
if !ok {
|
||||
return false, fmt.Errorf("Service name not found: %s. Did you forget to call registerProcessor()?", v[0])
|
||||
}
|
||||
smb := NewStoredMessageProtocol(in, v[1], typeId, seqid)
|
||||
return actualProcessor.Process(smb, out)
|
||||
}
|
||||
|
||||
//Protocol that use stored message for ReadMessageBegin
|
||||
type storedMessageProtocol struct {
|
||||
TProtocol
|
||||
name string
|
||||
typeId TMessageType
|
||||
seqid int32
|
||||
}
|
||||
|
||||
func NewStoredMessageProtocol(protocol TProtocol, name string, typeId TMessageType, seqid int32) *storedMessageProtocol {
|
||||
return &storedMessageProtocol{protocol, name, typeId, seqid}
|
||||
}
|
||||
|
||||
func (s *storedMessageProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) {
|
||||
return s.name, s.typeId, s.seqid, nil
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// This file is home to helpers that convert from various base types to
|
||||
// respective pointer types. This is necessary because Go does not permit
|
||||
// references to constants, nor can a pointer type to base type be allocated
|
||||
// and initialized in a single expression.
|
||||
//
|
||||
// E.g., this is not allowed:
|
||||
//
|
||||
// var ip *int = &5
|
||||
//
|
||||
// But this *is* allowed:
|
||||
//
|
||||
// func IntPtr(i int) *int { return &i }
|
||||
// var ip *int = IntPtr(5)
|
||||
//
|
||||
// Since pointers to base types are commonplace as [optional] fields in
|
||||
// exported thrift structs, we factor such helpers here.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func Float32Ptr(v float32) *float32 { return &v }
|
||||
func Float64Ptr(v float64) *float64 { return &v }
|
||||
func IntPtr(v int) *int { return &v }
|
||||
func Int32Ptr(v int32) *int32 { return &v }
|
||||
func Int64Ptr(v int64) *int64 { return &v }
|
||||
func StringPtr(v string) *string { return &v }
|
||||
func Uint32Ptr(v uint32) *uint32 { return &v }
|
||||
func Uint64Ptr(v uint64) *uint64 { return &v }
|
||||
func BoolPtr(v bool) *bool { return &v }
|
||||
func ByteSlicePtr(v []byte) *[]byte { return &v }
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
// The default processor factory just returns a singleton
|
||||
// instance.
|
||||
type TProcessorFactory interface {
|
||||
GetProcessor(trans TTransport) TProcessor
|
||||
}
|
||||
|
||||
type tProcessorFactory struct {
|
||||
processor TProcessor
|
||||
}
|
||||
|
||||
func NewTProcessorFactory(p TProcessor) TProcessorFactory {
|
||||
return &tProcessorFactory{processor: p}
|
||||
}
|
||||
|
||||
func (p *tProcessorFactory) GetProcessor(trans TTransport) TProcessor {
|
||||
return p.processor
|
||||
}
|
||||
|
||||
/**
|
||||
* The default processor factory just returns a singleton
|
||||
* instance.
|
||||
*/
|
||||
type TProcessorFunctionFactory interface {
|
||||
GetProcessorFunction(trans TTransport) TProcessorFunction
|
||||
}
|
||||
|
||||
type tProcessorFunctionFactory struct {
|
||||
processor TProcessorFunction
|
||||
}
|
||||
|
||||
func NewTProcessorFunctionFactory(p TProcessorFunction) TProcessorFunctionFactory {
|
||||
return &tProcessorFunctionFactory{processor: p}
|
||||
}
|
||||
|
||||
func (p *tProcessorFunctionFactory) GetProcessorFunction(trans TTransport) TProcessorFunction {
|
||||
return p.processor
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
type TServer interface {
|
||||
ProcessorFactory() TProcessorFactory
|
||||
ServerTransport() TServerTransport
|
||||
InputTransportFactory() TTransportFactory
|
||||
OutputTransportFactory() TTransportFactory
|
||||
InputProtocolFactory() TProtocolFactory
|
||||
OutputProtocolFactory() TProtocolFactory
|
||||
|
||||
// Starts the server
|
||||
Serve() error
|
||||
// Stops the server. This is optional on a per-implementation basis. Not
|
||||
// all servers are required to be cleanly stoppable.
|
||||
Stop() error
|
||||
}
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TServerSocket struct {
|
||||
listener net.Listener
|
||||
addr net.Addr
|
||||
clientTimeout time.Duration
|
||||
|
||||
// Protects the interrupted value to make it thread safe.
|
||||
mu sync.RWMutex
|
||||
interrupted bool
|
||||
}
|
||||
|
||||
func NewTServerSocket(listenAddr string) (*TServerSocket, error) {
|
||||
return NewTServerSocketTimeout(listenAddr, 0)
|
||||
}
|
||||
|
||||
func NewTServerSocketTimeout(listenAddr string, clientTimeout time.Duration) (*TServerSocket, error) {
|
||||
addr, err := net.ResolveTCPAddr("tcp", listenAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TServerSocket{addr: addr, clientTimeout: clientTimeout}, nil
|
||||
}
|
||||
|
||||
func (p *TServerSocket) Listen() error {
|
||||
if p.IsListening() {
|
||||
return nil
|
||||
}
|
||||
l, err := net.Listen(p.addr.Network(), p.addr.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.listener = l
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TServerSocket) Accept() (TTransport, error) {
|
||||
p.mu.RLock()
|
||||
interrupted := p.interrupted
|
||||
p.mu.RUnlock()
|
||||
|
||||
if interrupted {
|
||||
return nil, errTransportInterrupted
|
||||
}
|
||||
if p.listener == nil {
|
||||
return nil, NewTTransportException(NOT_OPEN, "No underlying server socket")
|
||||
}
|
||||
conn, err := p.listener.Accept()
|
||||
if err != nil {
|
||||
return nil, NewTTransportExceptionFromError(err)
|
||||
}
|
||||
return NewTSocketFromConnTimeout(conn, p.clientTimeout), nil
|
||||
}
|
||||
|
||||
// Checks whether the socket is listening.
|
||||
func (p *TServerSocket) IsListening() bool {
|
||||
return p.listener != nil
|
||||
}
|
||||
|
||||
// Connects the socket, creating a new socket object if necessary.
|
||||
func (p *TServerSocket) Open() error {
|
||||
if p.IsListening() {
|
||||
return NewTTransportException(ALREADY_OPEN, "Server socket already open")
|
||||
}
|
||||
if l, err := net.Listen(p.addr.Network(), p.addr.String()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
p.listener = l
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TServerSocket) Addr() net.Addr {
|
||||
if p.listener != nil {
|
||||
return p.listener.Addr()
|
||||
}
|
||||
return p.addr
|
||||
}
|
||||
|
||||
func (p *TServerSocket) Close() error {
|
||||
defer func() {
|
||||
p.listener = nil
|
||||
}()
|
||||
if p.IsListening() {
|
||||
return p.listener.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TServerSocket) Interrupt() error {
|
||||
p.mu.Lock()
|
||||
p.interrupted = true
|
||||
p.Close()
|
||||
p.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
// Server transport. Object which provides client transports.
|
||||
type TServerTransport interface {
|
||||
Listen() error
|
||||
Accept() (TTransport, error)
|
||||
Close() error
|
||||
|
||||
// Optional method implementation. This signals to the server transport
|
||||
// that it should break out of any accept() or listen() that it is currently
|
||||
// blocked on. This method, if implemented, MUST be thread safe, as it may
|
||||
// be called from a different thread context than the other TServerTransport
|
||||
// methods.
|
||||
Interrupt() error
|
||||
}
|
||||
-196
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"log"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Simple, non-concurrent server for testing.
|
||||
type TSimpleServer struct {
|
||||
quit chan struct{}
|
||||
|
||||
processorFactory TProcessorFactory
|
||||
serverTransport TServerTransport
|
||||
inputTransportFactory TTransportFactory
|
||||
outputTransportFactory TTransportFactory
|
||||
inputProtocolFactory TProtocolFactory
|
||||
outputProtocolFactory TProtocolFactory
|
||||
}
|
||||
|
||||
func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer {
|
||||
return NewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport)
|
||||
}
|
||||
|
||||
func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {
|
||||
return NewTSimpleServerFactory4(NewTProcessorFactory(processor),
|
||||
serverTransport,
|
||||
transportFactory,
|
||||
protocolFactory,
|
||||
)
|
||||
}
|
||||
|
||||
func NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {
|
||||
return NewTSimpleServerFactory6(NewTProcessorFactory(processor),
|
||||
serverTransport,
|
||||
inputTransportFactory,
|
||||
outputTransportFactory,
|
||||
inputProtocolFactory,
|
||||
outputProtocolFactory,
|
||||
)
|
||||
}
|
||||
|
||||
func NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer {
|
||||
return NewTSimpleServerFactory6(processorFactory,
|
||||
serverTransport,
|
||||
NewTTransportFactory(),
|
||||
NewTTransportFactory(),
|
||||
NewTBinaryProtocolFactoryDefault(),
|
||||
NewTBinaryProtocolFactoryDefault(),
|
||||
)
|
||||
}
|
||||
|
||||
func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {
|
||||
return NewTSimpleServerFactory6(processorFactory,
|
||||
serverTransport,
|
||||
transportFactory,
|
||||
transportFactory,
|
||||
protocolFactory,
|
||||
protocolFactory,
|
||||
)
|
||||
}
|
||||
|
||||
func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {
|
||||
return &TSimpleServer{
|
||||
processorFactory: processorFactory,
|
||||
serverTransport: serverTransport,
|
||||
inputTransportFactory: inputTransportFactory,
|
||||
outputTransportFactory: outputTransportFactory,
|
||||
inputProtocolFactory: inputProtocolFactory,
|
||||
outputProtocolFactory: outputProtocolFactory,
|
||||
quit: make(chan struct{}, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *TSimpleServer) ProcessorFactory() TProcessorFactory {
|
||||
return p.processorFactory
|
||||
}
|
||||
|
||||
func (p *TSimpleServer) ServerTransport() TServerTransport {
|
||||
return p.serverTransport
|
||||
}
|
||||
|
||||
func (p *TSimpleServer) InputTransportFactory() TTransportFactory {
|
||||
return p.inputTransportFactory
|
||||
}
|
||||
|
||||
func (p *TSimpleServer) OutputTransportFactory() TTransportFactory {
|
||||
return p.outputTransportFactory
|
||||
}
|
||||
|
||||
func (p *TSimpleServer) InputProtocolFactory() TProtocolFactory {
|
||||
return p.inputProtocolFactory
|
||||
}
|
||||
|
||||
func (p *TSimpleServer) OutputProtocolFactory() TProtocolFactory {
|
||||
return p.outputProtocolFactory
|
||||
}
|
||||
|
||||
func (p *TSimpleServer) Listen() error {
|
||||
return p.serverTransport.Listen()
|
||||
}
|
||||
|
||||
func (p *TSimpleServer) AcceptLoop() error {
|
||||
for {
|
||||
client, err := p.serverTransport.Accept()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-p.quit:
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return err
|
||||
}
|
||||
if client != nil {
|
||||
go func() {
|
||||
if err := p.processRequests(client); err != nil {
|
||||
log.Println("error processing request:", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *TSimpleServer) Serve() error {
|
||||
err := p.Listen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AcceptLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
|
||||
func (p *TSimpleServer) Stop() error {
|
||||
q := func() {
|
||||
p.quit <- struct{}{}
|
||||
p.serverTransport.Interrupt()
|
||||
}
|
||||
once.Do(q)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TSimpleServer) processRequests(client TTransport) error {
|
||||
processor := p.processorFactory.GetProcessor(client)
|
||||
inputTransport := p.inputTransportFactory.GetTransport(client)
|
||||
outputTransport := p.outputTransportFactory.GetTransport(client)
|
||||
inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport)
|
||||
outputProtocol := p.outputProtocolFactory.GetProtocol(outputTransport)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
log.Printf("panic in processor: %s: %s", e, debug.Stack())
|
||||
}
|
||||
}()
|
||||
if inputTransport != nil {
|
||||
defer inputTransport.Close()
|
||||
}
|
||||
if outputTransport != nil {
|
||||
defer outputTransport.Close()
|
||||
}
|
||||
for {
|
||||
ok, err := processor.Process(inputProtocol, outputProtocol)
|
||||
if err, ok := err.(TTransportException); ok && err.TypeId() == END_OF_FILE {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
log.Printf("error processing request: %s", err)
|
||||
return err
|
||||
}
|
||||
if err, ok := err.(TApplicationException); ok && err.TypeId() == UNKNOWN_METHOD {
|
||||
continue
|
||||
}
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TSocket struct {
|
||||
conn net.Conn
|
||||
addr net.Addr
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewTSocket creates a net.Conn-backed TTransport, given a host and port
|
||||
//
|
||||
// Example:
|
||||
// trans, err := thrift.NewTSocket("localhost:9090")
|
||||
func NewTSocket(hostPort string) (*TSocket, error) {
|
||||
return NewTSocketTimeout(hostPort, 0)
|
||||
}
|
||||
|
||||
// NewTSocketTimeout creates a net.Conn-backed TTransport, given a host and port
|
||||
// it also accepts a timeout as a time.Duration
|
||||
func NewTSocketTimeout(hostPort string, timeout time.Duration) (*TSocket, error) {
|
||||
//conn, err := net.DialTimeout(network, address, timeout)
|
||||
addr, err := net.ResolveTCPAddr("tcp", hostPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTSocketFromAddrTimeout(addr, timeout), nil
|
||||
}
|
||||
|
||||
// Creates a TSocket from a net.Addr
|
||||
func NewTSocketFromAddrTimeout(addr net.Addr, timeout time.Duration) *TSocket {
|
||||
return &TSocket{addr: addr, timeout: timeout}
|
||||
}
|
||||
|
||||
// Creates a TSocket from an existing net.Conn
|
||||
func NewTSocketFromConnTimeout(conn net.Conn, timeout time.Duration) *TSocket {
|
||||
return &TSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout}
|
||||
}
|
||||
|
||||
// Sets the socket timeout
|
||||
func (p *TSocket) SetTimeout(timeout time.Duration) error {
|
||||
p.timeout = timeout
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TSocket) pushDeadline(read, write bool) {
|
||||
var t time.Time
|
||||
if p.timeout > 0 {
|
||||
t = time.Now().Add(time.Duration(p.timeout))
|
||||
}
|
||||
if read && write {
|
||||
p.conn.SetDeadline(t)
|
||||
} else if read {
|
||||
p.conn.SetReadDeadline(t)
|
||||
} else if write {
|
||||
p.conn.SetWriteDeadline(t)
|
||||
}
|
||||
}
|
||||
|
||||
// Connects the socket, creating a new socket object if necessary.
|
||||
func (p *TSocket) Open() error {
|
||||
if p.IsOpen() {
|
||||
return NewTTransportException(ALREADY_OPEN, "Socket already connected.")
|
||||
}
|
||||
if p.addr == nil {
|
||||
return NewTTransportException(NOT_OPEN, "Cannot open nil address.")
|
||||
}
|
||||
if len(p.addr.Network()) == 0 {
|
||||
return NewTTransportException(NOT_OPEN, "Cannot open bad network name.")
|
||||
}
|
||||
if len(p.addr.String()) == 0 {
|
||||
return NewTTransportException(NOT_OPEN, "Cannot open bad address.")
|
||||
}
|
||||
var err error
|
||||
if p.conn, err = net.DialTimeout(p.addr.Network(), p.addr.String(), p.timeout); err != nil {
|
||||
return NewTTransportException(NOT_OPEN, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Retrieve the underlying net.Conn
|
||||
func (p *TSocket) Conn() net.Conn {
|
||||
return p.conn
|
||||
}
|
||||
|
||||
// Returns true if the connection is open
|
||||
func (p *TSocket) IsOpen() bool {
|
||||
if p.conn == nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Closes the socket.
|
||||
func (p *TSocket) Close() error {
|
||||
// Close the socket
|
||||
if p.conn != nil {
|
||||
err := p.conn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.conn = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Returns the remote address of the socket.
|
||||
func (p *TSocket) Addr() net.Addr {
|
||||
return p.addr
|
||||
}
|
||||
|
||||
func (p *TSocket) Read(buf []byte) (int, error) {
|
||||
if !p.IsOpen() {
|
||||
return 0, NewTTransportException(NOT_OPEN, "Connection not open")
|
||||
}
|
||||
p.pushDeadline(true, false)
|
||||
n, err := p.conn.Read(buf)
|
||||
return n, NewTTransportExceptionFromError(err)
|
||||
}
|
||||
|
||||
func (p *TSocket) Write(buf []byte) (int, error) {
|
||||
if !p.IsOpen() {
|
||||
return 0, NewTTransportException(NOT_OPEN, "Connection not open")
|
||||
}
|
||||
p.pushDeadline(false, true)
|
||||
return p.conn.Write(buf)
|
||||
}
|
||||
|
||||
func (p *TSocket) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TSocket) Interrupt() error {
|
||||
if !p.IsOpen() {
|
||||
return nil
|
||||
}
|
||||
return p.conn.Close()
|
||||
}
|
||||
|
||||
func (p *TSocket) RemainingBytes() (num_bytes uint64) {
|
||||
const maxSize = ^uint64(0)
|
||||
return maxSize // the thruth is, we just don't know unless framed is used
|
||||
}
|
||||
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
type TSSLServerSocket struct {
|
||||
listener net.Listener
|
||||
addr net.Addr
|
||||
clientTimeout time.Duration
|
||||
interrupted bool
|
||||
cfg *tls.Config
|
||||
}
|
||||
|
||||
func NewTSSLServerSocket(listenAddr string, cfg *tls.Config) (*TSSLServerSocket, error) {
|
||||
return NewTSSLServerSocketTimeout(listenAddr, cfg, 0)
|
||||
}
|
||||
|
||||
func NewTSSLServerSocketTimeout(listenAddr string, cfg *tls.Config, clientTimeout time.Duration) (*TSSLServerSocket, error) {
|
||||
addr, err := net.ResolveTCPAddr("tcp", listenAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TSSLServerSocket{addr: addr, clientTimeout: clientTimeout, cfg: cfg}, nil
|
||||
}
|
||||
|
||||
func (p *TSSLServerSocket) Listen() error {
|
||||
if p.IsListening() {
|
||||
return nil
|
||||
}
|
||||
l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.listener = l
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TSSLServerSocket) Accept() (TTransport, error) {
|
||||
if p.interrupted {
|
||||
return nil, errTransportInterrupted
|
||||
}
|
||||
if p.listener == nil {
|
||||
return nil, NewTTransportException(NOT_OPEN, "No underlying server socket")
|
||||
}
|
||||
conn, err := p.listener.Accept()
|
||||
if err != nil {
|
||||
return nil, NewTTransportExceptionFromError(err)
|
||||
}
|
||||
return NewTSSLSocketFromConnTimeout(conn, p.cfg, p.clientTimeout), nil
|
||||
}
|
||||
|
||||
// Checks whether the socket is listening.
|
||||
func (p *TSSLServerSocket) IsListening() bool {
|
||||
return p.listener != nil
|
||||
}
|
||||
|
||||
// Connects the socket, creating a new socket object if necessary.
|
||||
func (p *TSSLServerSocket) Open() error {
|
||||
if p.IsListening() {
|
||||
return NewTTransportException(ALREADY_OPEN, "Server socket already open")
|
||||
}
|
||||
if l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg); err != nil {
|
||||
return err
|
||||
} else {
|
||||
p.listener = l
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TSSLServerSocket) Addr() net.Addr {
|
||||
return p.addr
|
||||
}
|
||||
|
||||
func (p *TSSLServerSocket) Close() error {
|
||||
defer func() {
|
||||
p.listener = nil
|
||||
}()
|
||||
if p.IsListening() {
|
||||
return p.listener.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TSSLServerSocket) Interrupt() error {
|
||||
p.interrupted = true
|
||||
return nil
|
||||
}
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TSSLSocket struct {
|
||||
conn net.Conn
|
||||
// hostPort contains host:port (e.g. "asdf.com:12345"). The field is
|
||||
// only valid if addr is nil.
|
||||
hostPort string
|
||||
// addr is nil when hostPort is not "", and is only used when the
|
||||
// TSSLSocket is constructed from a net.Addr.
|
||||
addr net.Addr
|
||||
timeout time.Duration
|
||||
cfg *tls.Config
|
||||
}
|
||||
|
||||
// NewTSSLSocket creates a net.Conn-backed TTransport, given a host and port and tls Configuration
|
||||
//
|
||||
// Example:
|
||||
// trans, err := thrift.NewTSSLSocket("localhost:9090", nil)
|
||||
func NewTSSLSocket(hostPort string, cfg *tls.Config) (*TSSLSocket, error) {
|
||||
return NewTSSLSocketTimeout(hostPort, cfg, 0)
|
||||
}
|
||||
|
||||
// NewTSSLSocketTimeout creates a net.Conn-backed TTransport, given a host and port
|
||||
// it also accepts a tls Configuration and a timeout as a time.Duration
|
||||
func NewTSSLSocketTimeout(hostPort string, cfg *tls.Config, timeout time.Duration) (*TSSLSocket, error) {
|
||||
return &TSSLSocket{hostPort: hostPort, timeout: timeout, cfg: cfg}, nil
|
||||
}
|
||||
|
||||
// Creates a TSSLSocket from a net.Addr
|
||||
func NewTSSLSocketFromAddrTimeout(addr net.Addr, cfg *tls.Config, timeout time.Duration) *TSSLSocket {
|
||||
return &TSSLSocket{addr: addr, timeout: timeout, cfg: cfg}
|
||||
}
|
||||
|
||||
// Creates a TSSLSocket from an existing net.Conn
|
||||
func NewTSSLSocketFromConnTimeout(conn net.Conn, cfg *tls.Config, timeout time.Duration) *TSSLSocket {
|
||||
return &TSSLSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout, cfg: cfg}
|
||||
}
|
||||
|
||||
// Sets the socket timeout
|
||||
func (p *TSSLSocket) SetTimeout(timeout time.Duration) error {
|
||||
p.timeout = timeout
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TSSLSocket) pushDeadline(read, write bool) {
|
||||
var t time.Time
|
||||
if p.timeout > 0 {
|
||||
t = time.Now().Add(time.Duration(p.timeout))
|
||||
}
|
||||
if read && write {
|
||||
p.conn.SetDeadline(t)
|
||||
} else if read {
|
||||
p.conn.SetReadDeadline(t)
|
||||
} else if write {
|
||||
p.conn.SetWriteDeadline(t)
|
||||
}
|
||||
}
|
||||
|
||||
// Connects the socket, creating a new socket object if necessary.
|
||||
func (p *TSSLSocket) Open() error {
|
||||
var err error
|
||||
// If we have a hostname, we need to pass the hostname to tls.Dial for
|
||||
// certificate hostname checks.
|
||||
if p.hostPort != "" {
|
||||
if p.conn, err = tls.Dial("tcp", p.hostPort, p.cfg); err != nil {
|
||||
return NewTTransportException(NOT_OPEN, err.Error())
|
||||
}
|
||||
} else {
|
||||
if p.IsOpen() {
|
||||
return NewTTransportException(ALREADY_OPEN, "Socket already connected.")
|
||||
}
|
||||
if p.addr == nil {
|
||||
return NewTTransportException(NOT_OPEN, "Cannot open nil address.")
|
||||
}
|
||||
if len(p.addr.Network()) == 0 {
|
||||
return NewTTransportException(NOT_OPEN, "Cannot open bad network name.")
|
||||
}
|
||||
if len(p.addr.String()) == 0 {
|
||||
return NewTTransportException(NOT_OPEN, "Cannot open bad address.")
|
||||
}
|
||||
if p.conn, err = tls.Dial(p.addr.Network(), p.addr.String(), p.cfg); err != nil {
|
||||
return NewTTransportException(NOT_OPEN, err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Retrieve the underlying net.Conn
|
||||
func (p *TSSLSocket) Conn() net.Conn {
|
||||
return p.conn
|
||||
}
|
||||
|
||||
// Returns true if the connection is open
|
||||
func (p *TSSLSocket) IsOpen() bool {
|
||||
if p.conn == nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Closes the socket.
|
||||
func (p *TSSLSocket) Close() error {
|
||||
// Close the socket
|
||||
if p.conn != nil {
|
||||
err := p.conn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.conn = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TSSLSocket) Read(buf []byte) (int, error) {
|
||||
if !p.IsOpen() {
|
||||
return 0, NewTTransportException(NOT_OPEN, "Connection not open")
|
||||
}
|
||||
p.pushDeadline(true, false)
|
||||
n, err := p.conn.Read(buf)
|
||||
return n, NewTTransportExceptionFromError(err)
|
||||
}
|
||||
|
||||
func (p *TSSLSocket) Write(buf []byte) (int, error) {
|
||||
if !p.IsOpen() {
|
||||
return 0, NewTTransportException(NOT_OPEN, "Connection not open")
|
||||
}
|
||||
p.pushDeadline(false, true)
|
||||
return p.conn.Write(buf)
|
||||
}
|
||||
|
||||
func (p *TSSLSocket) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *TSSLSocket) Interrupt() error {
|
||||
if !p.IsOpen() {
|
||||
return nil
|
||||
}
|
||||
return p.conn.Close()
|
||||
}
|
||||
|
||||
func (p *TSSLSocket) RemainingBytes() (num_bytes uint64) {
|
||||
const maxSize = ^uint64(0)
|
||||
return maxSize // the thruth is, we just don't know unless framed is used
|
||||
}
|
||||
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package thrift
|
||||
|
||||
import (
|
||||
"compress/zlib"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
// TZlibTransportFactory is a factory for TZlibTransport instances
|
||||
type TZlibTransportFactory struct {
|
||||
level int
|
||||
}
|
||||
|
||||
// TZlibTransport is a TTransport implementation that makes use of zlib compression.
|
||||
type TZlibTransport struct {
|
||||
reader io.ReadCloser
|
||||
transport TTransport
|
||||
writer *zlib.Writer
|
||||
}
|
||||
|
||||
// GetTransport constructs a new instance of NewTZlibTransport
|
||||
func (p *TZlibTransportFactory) GetTransport(trans TTransport) TTransport {
|
||||
t, _ := NewTZlibTransport(trans, p.level)
|
||||
return t
|
||||
}
|
||||
|
||||
// NewTZlibTransportFactory constructs a new instance of NewTZlibTransportFactory
|
||||
func NewTZlibTransportFactory(level int) *TZlibTransportFactory {
|
||||
return &TZlibTransportFactory{level: level}
|
||||
}
|
||||
|
||||
// NewTZlibTransport constructs a new instance of TZlibTransport
|
||||
func NewTZlibTransport(trans TTransport, level int) (*TZlibTransport, error) {
|
||||
w, err := zlib.NewWriterLevel(trans, level)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &TZlibTransport{
|
||||
writer: w,
|
||||
transport: trans,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the reader and writer (flushing any unwritten data) and closes
|
||||
// the underlying transport.
|
||||
func (z *TZlibTransport) Close() error {
|
||||
if z.reader != nil {
|
||||
if err := z.reader.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := z.writer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return z.transport.Close()
|
||||
}
|
||||
|
||||
// Flush flushes the writer and its underlying transport.
|
||||
func (z *TZlibTransport) Flush() error {
|
||||
if err := z.writer.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
return z.transport.Flush()
|
||||
}
|
||||
|
||||
// IsOpen returns true if the transport is open
|
||||
func (z *TZlibTransport) IsOpen() bool {
|
||||
return z.transport.IsOpen()
|
||||
}
|
||||
|
||||
// Open opens the transport for communication
|
||||
func (z *TZlibTransport) Open() error {
|
||||
return z.transport.Open()
|
||||
}
|
||||
|
||||
func (z *TZlibTransport) Read(p []byte) (int, error) {
|
||||
if z.reader == nil {
|
||||
r, err := zlib.NewReader(z.transport)
|
||||
if err != nil {
|
||||
return 0, NewTTransportExceptionFromError(err)
|
||||
}
|
||||
z.reader = r
|
||||
}
|
||||
|
||||
return z.reader.Read(p)
|
||||
}
|
||||
|
||||
// RemainingBytes returns the size in bytes of the data that is still to be
|
||||
// read.
|
||||
func (z *TZlibTransport) RemainingBytes() uint64 {
|
||||
return z.transport.RemainingBytes()
|
||||
}
|
||||
|
||||
func (z *TZlibTransport) Write(p []byte) (int, error) {
|
||||
return z.writer.Write(p)
|
||||
}
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-1
@@ -1 +0,0 @@
|
||||
server.sh
|
||||
Generated
Vendored
+3
-3
@@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -12,6 +11,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkuri"
|
||||
)
|
||||
|
||||
// ProviderName provides a name of EC2Role provider
|
||||
@@ -125,7 +125,7 @@ type ec2RoleCredRespBody struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
const iamSecurityCredsPath = "/iam/security-credentials"
|
||||
const iamSecurityCredsPath = "iam/security-credentials/"
|
||||
|
||||
// requestCredList requests a list of credentials from the EC2 service.
|
||||
// If there are no credentials, or there is an error making or receiving the request
|
||||
@@ -153,7 +153,7 @@ func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
|
||||
// If the credentials cannot be found, or there is an error reading the response
|
||||
// and error will be returned.
|
||||
func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) {
|
||||
resp, err := client.GetMetadata(path.Join(iamSecurityCredsPath, credsName))
|
||||
resp, err := client.GetMetadata(sdkuri.PathJoin(iamSecurityCredsPath, credsName))
|
||||
if err != nil {
|
||||
return ec2RoleCredRespBody{},
|
||||
awserr.New("EC2RoleRequestError",
|
||||
|
||||
Generated
Vendored
+4
-2
@@ -223,8 +223,10 @@ func (rep *Reporter) InjectHandlers(handlers *request.Handlers) {
|
||||
}
|
||||
|
||||
apiCallHandler := request.NamedHandler{Name: APICallMetricHandlerName, Fn: rep.sendAPICallMetric}
|
||||
handlers.Complete.PushFrontNamed(apiCallHandler)
|
||||
|
||||
apiCallAttemptHandler := request.NamedHandler{Name: APICallAttemptMetricHandlerName, Fn: rep.sendAPICallAttemptMetric}
|
||||
|
||||
handlers.Complete.PushFrontNamed(apiCallHandler)
|
||||
handlers.Complete.PushFrontNamed(apiCallAttemptHandler)
|
||||
|
||||
handlers.AfterRetry.PushFrontNamed(apiCallAttemptHandler)
|
||||
}
|
||||
|
||||
+4
-4
@@ -4,12 +4,12 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkuri"
|
||||
)
|
||||
|
||||
// GetMetadata uses the path provided to request information from the EC2
|
||||
@@ -19,7 +19,7 @@ func (c *EC2Metadata) GetMetadata(p string) (string, error) {
|
||||
op := &request.Operation{
|
||||
Name: "GetMetadata",
|
||||
HTTPMethod: "GET",
|
||||
HTTPPath: path.Join("/", "meta-data", p),
|
||||
HTTPPath: sdkuri.PathJoin("/meta-data", p),
|
||||
}
|
||||
|
||||
output := &metadataOutput{}
|
||||
@@ -35,7 +35,7 @@ func (c *EC2Metadata) GetUserData() (string, error) {
|
||||
op := &request.Operation{
|
||||
Name: "GetUserData",
|
||||
HTTPMethod: "GET",
|
||||
HTTPPath: path.Join("/", "user-data"),
|
||||
HTTPPath: "/user-data",
|
||||
}
|
||||
|
||||
output := &metadataOutput{}
|
||||
@@ -56,7 +56,7 @@ func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
|
||||
op := &request.Operation{
|
||||
Name: "GetDynamicData",
|
||||
HTTPMethod: "GET",
|
||||
HTTPPath: path.Join("/", "dynamic", p),
|
||||
HTTPPath: sdkuri.PathJoin("/dynamic", p),
|
||||
}
|
||||
|
||||
output := &metadataOutput{}
|
||||
|
||||
+57
-7
@@ -571,6 +571,7 @@ var awsPartition = partition{
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
@@ -1193,8 +1194,10 @@ var awsPartition = partition{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
@@ -1208,6 +1211,7 @@ var awsPartition = partition{
|
||||
Protocols: []string{"https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
@@ -1530,6 +1534,7 @@ var awsPartition = partition{
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
@@ -1922,6 +1927,7 @@ var awsPartition = partition{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
@@ -2040,6 +2046,7 @@ var awsPartition = partition{
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
@@ -2130,11 +2137,31 @@ var awsPartition = partition{
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"fips-us-east-1": endpoint{},
|
||||
"fips-us-east-2": endpoint{},
|
||||
"fips-us-west-1": endpoint{},
|
||||
"fips-us-west-2": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "sqs-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-2": endpoint{
|
||||
Hostname: "sqs-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"fips-us-west-1": endpoint{
|
||||
Hostname: "sqs-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"fips-us-west-2": endpoint{
|
||||
Hostname: "sqs-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{
|
||||
SSLCommonName: "queue.{dnsSuffix}",
|
||||
},
|
||||
@@ -2462,7 +2489,8 @@ var awscnPartition = partition{
|
||||
"apigateway": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"cn-north-1": endpoint{},
|
||||
"cn-north-1": endpoint{},
|
||||
"cn-northwest-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"application-autoscaling": service{
|
||||
@@ -2528,6 +2556,13 @@ var awscnPartition = partition{
|
||||
"cn-northwest-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"ds": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"cn-north-1": endpoint{},
|
||||
"cn-northwest-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"dynamodb": service{
|
||||
Defaults: endpoint{
|
||||
Protocols: []string{"http", "https"},
|
||||
@@ -2658,7 +2693,8 @@ var awscnPartition = partition{
|
||||
"lambda": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"cn-north-1": endpoint{},
|
||||
"cn-north-1": endpoint{},
|
||||
"cn-northwest-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"logs": service{
|
||||
@@ -2982,6 +3018,12 @@ var awsusgovPartition = partition{
|
||||
},
|
||||
},
|
||||
},
|
||||
"inspector": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"kinesis": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
@@ -3138,5 +3180,13 @@ var awsusgovPartition = partition{
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"translate": service{
|
||||
Defaults: endpoint{
|
||||
Protocols: []string{"https"},
|
||||
},
|
||||
Endpoints: endpoints{
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
+1
-1
@@ -128,7 +128,7 @@ read. The Session will be created from configuration values from the shared
|
||||
credentials file (~/.aws/credentials) over those in the shared config file (~/.aws/config).
|
||||
|
||||
Credentials are the values the SDK should use for authenticating requests with
|
||||
AWS Services. They arfrom a configuration file will need to include both
|
||||
AWS Services. They are from a configuration file will need to include both
|
||||
aws_access_key_id and aws_secret_access_key must be provided together in the
|
||||
same file to be considered valid. The values will be ignored if not a complete
|
||||
group. aws_session_token is an optional field that can be provided if both of
|
||||
|
||||
+1
-1
@@ -5,4 +5,4 @@ package aws
|
||||
const SDKName = "aws-sdk-go"
|
||||
|
||||
// SDKVersion is the version of this SDK
|
||||
const SDKVersion = "1.14.12"
|
||||
const SDKVersion = "1.14.27"
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package sdkuri
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PathJoin will join the elements of the path delimited by the "/"
|
||||
// character. Similar to path.Join with the exception the trailing "/"
|
||||
// character is preserved if present.
|
||||
func PathJoin(elems ...string) string {
|
||||
if len(elems) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
hasTrailing := strings.HasSuffix(elems[len(elems)-1], "/")
|
||||
str := path.Join(elems...)
|
||||
if hasTrailing && str != "/" {
|
||||
str += "/"
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
Generated
Vendored
+36
@@ -94,6 +94,9 @@ func (r *EventReader) ReadEvent() (event interface{}, err error) {
|
||||
switch typ {
|
||||
case EventMessageType:
|
||||
return r.unmarshalEventMessage(msg)
|
||||
case ExceptionMessageType:
|
||||
err = r.unmarshalEventException(msg)
|
||||
return nil, err
|
||||
case ErrorMessageType:
|
||||
return nil, r.unmarshalErrorMessage(msg)
|
||||
default:
|
||||
@@ -122,6 +125,39 @@ func (r *EventReader) unmarshalEventMessage(
|
||||
return ev, nil
|
||||
}
|
||||
|
||||
func (r *EventReader) unmarshalEventException(
|
||||
msg eventstream.Message,
|
||||
) (err error) {
|
||||
eventType, err := GetHeaderString(msg, ExceptionTypeHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ev, err := r.unmarshalerForEventType(eventType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ev.UnmarshalEvent(r.payloadUnmarshaler, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
err, ok = ev.(error)
|
||||
if !ok {
|
||||
err = messageError{
|
||||
code: "SerializationError",
|
||||
msg: fmt.Sprintf(
|
||||
"event stream exception %s mapped to non-error %T, %v",
|
||||
eventType, ev, ev,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *EventReader) unmarshalErrorMessage(msg eventstream.Message) (err error) {
|
||||
var msgErr messageError
|
||||
|
||||
|
||||
+1
-1
@@ -464,7 +464,7 @@ func (v *TimestampValue) decode(r io.Reader) error {
|
||||
func timeFromEpochMilli(t int64) time.Time {
|
||||
secs := t / 1e3
|
||||
msec := t % 1e3
|
||||
return time.Unix(secs, msec*int64(time.Millisecond))
|
||||
return time.Unix(secs, msec*int64(time.Millisecond)).UTC()
|
||||
}
|
||||
|
||||
// An UUIDValue provides eventstream encoding, and representation of a UUID
|
||||
|
||||
+7
-3
@@ -13,9 +13,13 @@ import (
|
||||
"github.com/aws/aws-sdk-go/private/protocol"
|
||||
)
|
||||
|
||||
// BuildXML will serialize params into an xml.Encoder.
|
||||
// Error will be returned if the serialization of any of the params or nested values fails.
|
||||
// BuildXML will serialize params into an xml.Encoder. Error will be returned
|
||||
// if the serialization of any of the params or nested values fails.
|
||||
func BuildXML(params interface{}, e *xml.Encoder) error {
|
||||
return buildXML(params, e, false)
|
||||
}
|
||||
|
||||
func buildXML(params interface{}, e *xml.Encoder, sorted bool) error {
|
||||
b := xmlBuilder{encoder: e, namespaces: map[string]string{}}
|
||||
root := NewXMLElement(xml.Name{})
|
||||
if err := b.buildValue(reflect.ValueOf(params), root, ""); err != nil {
|
||||
@@ -23,7 +27,7 @@ func BuildXML(params interface{}, e *xml.Encoder) error {
|
||||
}
|
||||
for _, c := range root.Children {
|
||||
for _, v := range c {
|
||||
return StructToXML(e, v, false)
|
||||
return StructToXML(e, v, sorted)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
+1
@@ -29,6 +29,7 @@ func NewXMLElement(name xml.Name) *XMLNode {
|
||||
|
||||
// AddChild adds child to the XMLNode.
|
||||
func (n *XMLNode) AddChild(child *XMLNode) {
|
||||
child.parent = n
|
||||
if _, ok := n.Children[child.Name.Local]; !ok {
|
||||
n.Children[child.Name.Local] = []*XMLNode{}
|
||||
}
|
||||
|
||||
+41
-16
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
@@ -7066,6 +7067,11 @@ func (s *CORSRule) SetMaxAgeSeconds(v int64) *CORSRule {
|
||||
type CSVInput struct {
|
||||
_ struct{} `type:"structure"`
|
||||
|
||||
// Specifies that CSV field values may contain quoted record delimiters and
|
||||
// such records should be allowed. Default value is FALSE. Setting this value
|
||||
// to TRUE may lower performance.
|
||||
AllowQuotedRecordDelimiter *bool `type:"boolean"`
|
||||
|
||||
// Single character used to indicate a row should be ignored when present at
|
||||
// the start of a row.
|
||||
Comments *string `type:"string"`
|
||||
@@ -7097,6 +7103,12 @@ func (s CSVInput) GoString() string {
|
||||
return s.String()
|
||||
}
|
||||
|
||||
// SetAllowQuotedRecordDelimiter sets the AllowQuotedRecordDelimiter field's value.
|
||||
func (s *CSVInput) SetAllowQuotedRecordDelimiter(v bool) *CSVInput {
|
||||
s.AllowQuotedRecordDelimiter = &v
|
||||
return s
|
||||
}
|
||||
|
||||
// SetComments sets the Comments field's value.
|
||||
func (s *CSVInput) SetComments(v string) *CSVInput {
|
||||
s.Comments = &v
|
||||
@@ -7564,7 +7576,7 @@ func (s *Condition) SetKeyPrefixEquals(v string) *Condition {
|
||||
}
|
||||
|
||||
type ContinuationEvent struct {
|
||||
_ struct{} `type:"structure"`
|
||||
_ struct{} `locationName:"ContinuationEvent" type:"structure"`
|
||||
}
|
||||
|
||||
// String returns the string representation
|
||||
@@ -10035,7 +10047,7 @@ func (s *EncryptionConfiguration) SetReplicaKmsKeyID(v string) *EncryptionConfig
|
||||
}
|
||||
|
||||
type EndEvent struct {
|
||||
_ struct{} `type:"structure"`
|
||||
_ struct{} `locationName:"EndEvent" type:"structure"`
|
||||
}
|
||||
|
||||
// String returns the string representation
|
||||
@@ -10155,6 +10167,7 @@ type FilterRule struct {
|
||||
// the filtering rule applies. Maximum prefix length can be up to 1,024 characters.
|
||||
// Overlapping prefixes and suffixes are not supported. For more information,
|
||||
// go to Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
Name *string `type:"string" enum:"FilterRuleName"`
|
||||
|
||||
Value *string `type:"string"`
|
||||
@@ -13013,7 +13026,7 @@ type InputSerialization struct {
|
||||
// Describes the serialization of a CSV-encoded object.
|
||||
CSV *CSVInput `type:"structure"`
|
||||
|
||||
// Specifies object's compression format. Valid values: NONE, GZIP. Default
|
||||
// Specifies object's compression format. Valid values: NONE, GZIP, BZIP2. Default
|
||||
// Value: NONE.
|
||||
CompressionType *string `type:"string" enum:"CompressionType"`
|
||||
|
||||
@@ -13519,6 +13532,7 @@ type LambdaFunctionConfiguration struct {
|
||||
|
||||
// Container for object key name filtering rules. For information about key
|
||||
// name filtering, go to Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
Filter *NotificationConfigurationFilter `type:"structure"`
|
||||
|
||||
// Optional unique identifier for configurations in a notification configuration.
|
||||
@@ -15966,7 +15980,8 @@ type NoncurrentVersionExpiration struct {
|
||||
// Specifies the number of days an object is noncurrent before Amazon S3 can
|
||||
// perform the associated action. For information about the noncurrent days
|
||||
// calculations, see How Amazon S3 Calculates When an Object Became Noncurrent
|
||||
// (http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-access-control.html)
|
||||
// (http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-access-control.html) in
|
||||
// the Amazon Simple Storage Service Developer Guide.
|
||||
NoncurrentDays *int64 `type:"integer"`
|
||||
}
|
||||
|
||||
@@ -15998,7 +16013,8 @@ type NoncurrentVersionTransition struct {
|
||||
// Specifies the number of days an object is noncurrent before Amazon S3 can
|
||||
// perform the associated action. For information about the noncurrent days
|
||||
// calculations, see How Amazon S3 Calculates When an Object Became Noncurrent
|
||||
// (http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-access-control.html)
|
||||
// (http://docs.aws.amazon.com/AmazonS3/latest/dev/s3-access-control.html) in
|
||||
// the Amazon Simple Storage Service Developer Guide.
|
||||
NoncurrentDays *int64 `type:"integer"`
|
||||
|
||||
// The class of storage used to store the object.
|
||||
@@ -16147,6 +16163,7 @@ func (s *NotificationConfigurationDeprecated) SetTopicConfiguration(v *TopicConf
|
||||
|
||||
// Container for object key name filtering rules. For information about key
|
||||
// name filtering, go to Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
type NotificationConfigurationFilter struct {
|
||||
_ struct{} `type:"structure"`
|
||||
|
||||
@@ -16563,7 +16580,7 @@ func (s *Progress) SetBytesScanned(v int64) *Progress {
|
||||
}
|
||||
|
||||
type ProgressEvent struct {
|
||||
_ struct{} `type:"structure" payload:"Details"`
|
||||
_ struct{} `locationName:"ProgressEvent" type:"structure" payload:"Details"`
|
||||
|
||||
// The Progress event details.
|
||||
Details *Progress `locationName:"Details" type:"structure"`
|
||||
@@ -16597,7 +16614,7 @@ func (s *ProgressEvent) UnmarshalEvent(
|
||||
if err := payloadUnmarshaler.UnmarshalPayload(
|
||||
bytes.NewReader(msg.Payload), s,
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal payload, %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -18732,6 +18749,7 @@ type QueueConfiguration struct {
|
||||
|
||||
// Container for object key name filtering rules. For information about key
|
||||
// name filtering, go to Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
Filter *NotificationConfigurationFilter `type:"structure"`
|
||||
|
||||
// Optional unique identifier for configurations in a notification configuration.
|
||||
@@ -18845,7 +18863,7 @@ func (s *QueueConfigurationDeprecated) SetQueue(v string) *QueueConfigurationDep
|
||||
}
|
||||
|
||||
type RecordsEvent struct {
|
||||
_ struct{} `type:"structure" payload:"Payload"`
|
||||
_ struct{} `locationName:"RecordsEvent" type:"structure" payload:"Payload"`
|
||||
|
||||
// The byte array of partial, one or more result records.
|
||||
//
|
||||
@@ -19889,8 +19907,11 @@ func (r *readSelectObjectContentEventStream) unmarshalerForEventType(
|
||||
case "Stats":
|
||||
return &StatsEvent{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"unknown event type name, %s, for SelectObjectContentEventStream", eventType)
|
||||
return nil, awserr.New(
|
||||
request.ErrCodeSerialization,
|
||||
fmt.Sprintf("unknown event type name, %s, for SelectObjectContentEventStream", eventType),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19900,7 +19921,7 @@ func (r *readSelectObjectContentEventStream) unmarshalerForEventType(
|
||||
// Amazon S3 uses this to parse object data into records, and returns only records
|
||||
// that match the specified SQL expression. You must also specify the data serialization
|
||||
// format for the response. For more information, go to S3Select API Documentation
|
||||
// (https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectSELECTContent.html)
|
||||
// (http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectSELECTContent.html).
|
||||
type SelectObjectContentInput struct {
|
||||
_ struct{} `locationName:"SelectObjectContentRequest" type:"structure" xmlURI:"http://s3.amazonaws.com/doc/2006-03-01/"`
|
||||
|
||||
@@ -19938,15 +19959,15 @@ type SelectObjectContentInput struct {
|
||||
RequestProgress *RequestProgress `type:"structure"`
|
||||
|
||||
// The SSE Algorithm used to encrypt the object. For more information, go to
|
||||
// Server-Side Encryption (Using Customer-Provided Encryption Keys (https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html)
|
||||
// Server-Side Encryption (Using Customer-Provided Encryption Keys (http://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html).
|
||||
SSECustomerAlgorithm *string `location:"header" locationName:"x-amz-server-side-encryption-customer-algorithm" type:"string"`
|
||||
|
||||
// The SSE Customer Key. For more information, go to Server-Side Encryption
|
||||
// (Using Customer-Provided Encryption Keys (https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html)
|
||||
// (Using Customer-Provided Encryption Keys (http://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html).
|
||||
SSECustomerKey *string `location:"header" locationName:"x-amz-server-side-encryption-customer-key" type:"string"`
|
||||
|
||||
// The SSE Customer Key MD5. For more information, go to Server-Side Encryption
|
||||
// (Using Customer-Provided Encryption Keys (https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html)
|
||||
// (Using Customer-Provided Encryption Keys (http://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html).
|
||||
SSECustomerKeyMD5 *string `location:"header" locationName:"x-amz-server-side-encryption-customer-key-MD5" type:"string"`
|
||||
}
|
||||
|
||||
@@ -20453,7 +20474,7 @@ func (s *Stats) SetBytesScanned(v int64) *Stats {
|
||||
}
|
||||
|
||||
type StatsEvent struct {
|
||||
_ struct{} `type:"structure" payload:"Details"`
|
||||
_ struct{} `locationName:"StatsEvent" type:"structure" payload:"Details"`
|
||||
|
||||
// The Stats event details.
|
||||
Details *Stats `locationName:"Details" type:"structure"`
|
||||
@@ -20487,7 +20508,7 @@ func (s *StatsEvent) UnmarshalEvent(
|
||||
if err := payloadUnmarshaler.UnmarshalPayload(
|
||||
bytes.NewReader(msg.Payload), s,
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal payload, %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -20745,6 +20766,7 @@ type TopicConfiguration struct {
|
||||
|
||||
// Container for object key name filtering rules. For information about key
|
||||
// name filtering, go to Configuring Event Notifications (http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html)
|
||||
// in the Amazon Simple Storage Service Developer Guide.
|
||||
Filter *NotificationConfigurationFilter `type:"structure"`
|
||||
|
||||
// Optional unique identifier for configurations in a notification configuration.
|
||||
@@ -21678,6 +21700,9 @@ const (
|
||||
|
||||
// CompressionTypeGzip is a CompressionType enum value
|
||||
CompressionTypeGzip = "GZIP"
|
||||
|
||||
// CompressionTypeBzip2 is a CompressionType enum value
|
||||
CompressionTypeBzip2 = "BZIP2"
|
||||
)
|
||||
|
||||
// Requests Amazon S3 to encode the object keys in the response and specifies
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ func (d Downloader) Download(w io.WriterAt, input *s3.GetObjectInput, options ..
|
||||
//
|
||||
// DownloadWithContext is the same as Download with the additional support for
|
||||
// Context input parameters. The Context must not be nil. A nil Context will
|
||||
// cause a panic. Use the Context to add deadlining, timeouts, ect. The
|
||||
// cause a panic. Use the Context to add deadlining, timeouts, etc. The
|
||||
// DownloadWithContext may create sub-contexts for individual underlying
|
||||
// requests.
|
||||
//
|
||||
|
||||
+2
-2
@@ -357,7 +357,7 @@ func (u Uploader) Upload(input *UploadInput, options ...func(*Uploader)) (*Uploa
|
||||
//
|
||||
// UploadWithContext is the same as Upload with the additional support for
|
||||
// Context input parameters. The Context must not be nil. A nil Context will
|
||||
// cause a panic. Use the context to add deadlining, timeouts, ect. The
|
||||
// cause a panic. Use the context to add deadlining, timeouts, etc. The
|
||||
// UploadWithContext may create sub-contexts for individual underlying requests.
|
||||
//
|
||||
// Additional functional options can be provided to configure the individual
|
||||
@@ -395,7 +395,7 @@ func (u Uploader) UploadWithContext(ctx aws.Context, input *UploadInput, opts ..
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// iter := &s3managee.UploadObjectsIterator{Objects: objects}
|
||||
// iter := &s3manager.UploadObjectsIterator{Objects: objects}
|
||||
// if err := svc.UploadWithIterator(aws.BackgroundContext(), iter); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
+1
@@ -1 +1,2 @@
|
||||
dist
|
||||
.envrc
|
||||
|
||||
+12
-2
@@ -4,7 +4,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gobuffalo/buffalo"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
olog "github.com/opentracing/opentracing-go/log"
|
||||
)
|
||||
@@ -27,7 +27,17 @@ func OpenTracing(tr opentracing.Tracer) buffalo.MiddlewareFunc {
|
||||
opName = operation(route.HandlerName)
|
||||
}
|
||||
}
|
||||
sp := tr.StartSpan(opName)
|
||||
|
||||
wireContext, _ := tr.Extract(
|
||||
opentracing.HTTPHeaders,
|
||||
opentracing.HTTPHeadersCarrier(c.Request().Header))
|
||||
|
||||
// Create the span referring to the RPC client if available.
|
||||
// If wireContext == nil, a root span will be created.
|
||||
sp := tr.StartSpan(
|
||||
opName,
|
||||
ext.RPCServerOption(wireContext))
|
||||
|
||||
ext.HTTPMethod.Set(sp, c.Request().Method)
|
||||
ext.HTTPUrl.Set(sp, c.Request().URL.String())
|
||||
|
||||
|
||||
+8
@@ -76,6 +76,14 @@ func Commaf(v float64) string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// CommafWithDigits works like the Commaf but limits the resulting
|
||||
// string to the given number of decimal places.
|
||||
//
|
||||
// e.g. CommafWithDigits(834142.32, 1) -> 834,142.3
|
||||
func CommafWithDigits(f float64, decimals int) string {
|
||||
return stripTrailingDigits(Commaf(f), decimals)
|
||||
}
|
||||
|
||||
// BigComma produces a string form of the given big.Int in base 10
|
||||
// with commas after every three orders of magnitude.
|
||||
func BigComma(b *big.Int) string {
|
||||
|
||||
+24
-1
@@ -1,6 +1,9 @@
|
||||
package humanize
|
||||
|
||||
import "strconv"
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func stripTrailingZeros(s string) string {
|
||||
offset := len(s) - 1
|
||||
@@ -17,7 +20,27 @@ func stripTrailingZeros(s string) string {
|
||||
return s[:offset+1]
|
||||
}
|
||||
|
||||
func stripTrailingDigits(s string, digits int) string {
|
||||
if i := strings.Index(s, "."); i >= 0 {
|
||||
if digits <= 0 {
|
||||
return s[:i]
|
||||
}
|
||||
i++
|
||||
if i+digits >= len(s) {
|
||||
return s
|
||||
}
|
||||
return s[:i+digits]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Ftoa converts a float to a string with no trailing zeros.
|
||||
func Ftoa(num float64) string {
|
||||
return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64))
|
||||
}
|
||||
|
||||
// FtoaWithDigits converts a float to a string but limits the resulting string
|
||||
// to the given number of decimal places, and no trailing zeros.
|
||||
func FtoaWithDigits(num float64, digits int) string {
|
||||
return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits))
|
||||
}
|
||||
|
||||
+10
@@ -93,6 +93,16 @@ func SI(input float64, unit string) string {
|
||||
return Ftoa(value) + " " + prefix + unit
|
||||
}
|
||||
|
||||
// SIWithDigits works like SI but limits the resulting string to the
|
||||
// given number of decimal places.
|
||||
//
|
||||
// e.g. SIWithDigits(1000000, 0, "B") -> 1 MB
|
||||
// e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF
|
||||
func SIWithDigits(input float64, decimals int, unit string) string {
|
||||
value, prefix := ComputeSI(input)
|
||||
return FtoaWithDigits(value, decimals) + " " + prefix + unit
|
||||
}
|
||||
|
||||
var errInvalid = errors.New("invalid input")
|
||||
|
||||
// ParseSI parses an SI string back into the number and unit.
|
||||
|
||||
+3
@@ -24,6 +24,9 @@ var (
|
||||
// os.Stdout is used.
|
||||
Output = colorable.NewColorableStdout()
|
||||
|
||||
// Error defines a color supporting writer for os.Stderr.
|
||||
Error = colorable.NewColorableStderr()
|
||||
|
||||
// colorsCache is used to reduce the count of created Color objects and
|
||||
// allows to reuse already created objects with required Attribute.
|
||||
colorsCache = make(map[Attribute]*Color)
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
_harness
|
||||
|
||||
.vscode
|
||||
+2
-2
@@ -3,8 +3,8 @@ language: go
|
||||
go_import_path: github.com/globalsign/mgo
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
|
||||
env:
|
||||
global:
|
||||
@@ -29,7 +29,7 @@ install:
|
||||
- go get gopkg.in/check.v1
|
||||
- go get gopkg.in/yaml.v2
|
||||
- go get gopkg.in/tomb.v2
|
||||
- go get github.com/golang/lint/golint
|
||||
- go get github.com/golang/lint
|
||||
|
||||
before_script:
|
||||
- golint ./... | grep -v 'ID' | cat
|
||||
|
||||
+46
-7
@@ -3,10 +3,16 @@
|
||||
The MongoDB driver for Go
|
||||
-------------------------
|
||||
|
||||
This fork has had a few improvements by ourselves as well as several PR's merged from the original mgo repo that are currently awaiting review. Changes are mostly geared towards performance improvements and bug fixes, though a few new features have been added.
|
||||
This fork has had a few improvements by ourselves as well as several PR's merged from the original mgo repo that are currently awaiting review.
|
||||
Changes are mostly geared towards performance improvements and bug fixes, though a few new features have been added.
|
||||
|
||||
Further PR's (with tests) are welcome, but please maintain backwards compatibility.
|
||||
|
||||
Detailed documentation of the API is available at
|
||||
[GoDoc](https://godoc.org/github.com/globalsign/mgo).
|
||||
|
||||
A [sub-package](https://godoc.org/github.com/globalsign/mgo/bson) that implements the [BSON](http://bsonspec.org) specification is also included, and may be used independently of the driver.
|
||||
|
||||
## Changes
|
||||
* Fixes attempting to authenticate before every query ([details](https://github.com/go-mgo/mgo/issues/254))
|
||||
* Removes bulk update / delete batch size limitations ([details](https://github.com/go-mgo/mgo/issues/288))
|
||||
@@ -15,17 +21,17 @@ Further PR's (with tests) are welcome, but please maintain backwards compatibili
|
||||
* Support majority read concerns ([details](https://github.com/globalsign/mgo/pull/2))
|
||||
* Improved connection handling ([details](https://github.com/globalsign/mgo/pull/5))
|
||||
* Hides SASL warnings ([details](https://github.com/globalsign/mgo/pull/7))
|
||||
* Support for partial indexes ([detials](https://github.com/domodwyer/mgo/commit/5efe8eccb028238d93c222828cae4806aeae9f51))
|
||||
* Fixes timezone handling ([details](https://github.com/go-mgo/mgo/pull/464))
|
||||
* Support for partial indexes ([details](https://github.com/domodwyer/mgo/commit/5efe8eccb028238d93c222828cae4806aeae9f51))
|
||||
* Fixes timezone handling ([details](https://github.com/go-mgo/mgo/pull/464))
|
||||
* Integration tests run against MongoDB 3.2 & 3.4 releases ([details](https://github.com/globalsign/mgo/pull/4), [more](https://github.com/globalsign/mgo/pull/24), [more](https://github.com/globalsign/mgo/pull/35))
|
||||
* Improved multi-document transaction performance ([details](https://github.com/globalsign/mgo/pull/10), [more](https://github.com/globalsign/mgo/pull/11), [more](https://github.com/globalsign/mgo/pull/16))
|
||||
* Fixes cursor timeouts ([details](https://jira.mongodb.org/browse/SERVER-24899))
|
||||
* Support index hints and timeouts for count queries ([details](https://github.com/globalsign/mgo/pull/17))
|
||||
* Don't panic when handling indexed `int64` fields ([detials](https://github.com/go-mgo/mgo/issues/475))
|
||||
* Don't panic when handling indexed `int64` fields ([details](https://github.com/go-mgo/mgo/issues/475))
|
||||
* Supports dropping all indexes on a collection ([details](https://github.com/globalsign/mgo/pull/25))
|
||||
* Annotates log entries/profiler output with optional appName on 3.4+ ([details](https://github.com/globalsign/mgo/pull/28))
|
||||
* Support for read-only [views](https://docs.mongodb.com/manual/core/views/) in 3.4+ ([details](https://github.com/globalsign/mgo/pull/33))
|
||||
* Support for [collations](https://docs.mongodb.com/manual/reference/collation/) in 3.4+ ([details](https://github.com/globalsign/mgo/pull/37))
|
||||
* Support for [collations](https://docs.mongodb.com/manual/reference/collation/) in 3.4+ ([details](https://github.com/globalsign/mgo/pull/37), [more](https://github.com/globalsign/mgo/pull/166))
|
||||
* Provide BSON constants for convenience/sanity ([details](https://github.com/globalsign/mgo/pull/41))
|
||||
* Consistently unmarshal time.Time values as UTC ([details](https://github.com/globalsign/mgo/pull/42))
|
||||
* Enforces best practise coding guidelines ([details](https://github.com/globalsign/mgo/pull/44))
|
||||
@@ -36,25 +42,58 @@ Further PR's (with tests) are welcome, but please maintain backwards compatibili
|
||||
* Gracefully recover from a temporarily unreachable server ([details](https://github.com/globalsign/mgo/pull/69))
|
||||
* Use JSON tags when no explicit BSON are tags set ([details](https://github.com/globalsign/mgo/pull/91))
|
||||
* Support [$changeStream](https://docs.mongodb.com/manual/changeStreams/) tailing on 3.6+ ([details](https://github.com/globalsign/mgo/pull/97))
|
||||
* Fix deadlock in cluster synchronisation ([details](https://github.com/globalsign/mgo/issues/120))
|
||||
* Implement `maxIdleTimeout` for pooled connections ([details](https://github.com/globalsign/mgo/pull/116))
|
||||
* Connection pool waiting improvements ([details](https://github.com/globalsign/mgo/pull/115))
|
||||
* Fixes BSON encoding for `$in` and friends ([details](https://github.com/globalsign/mgo/pull/128))
|
||||
* Add BSON stream encoders ([details](https://github.com/globalsign/mgo/pull/127))
|
||||
* Add integer map key support in the BSON encoder ([details](https://github.com/globalsign/mgo/pull/140))
|
||||
* Support aggregation [collations](https://docs.mongodb.com/manual/reference/collation/) ([details](https://github.com/globalsign/mgo/pull/144))
|
||||
* Support encoding of inline struct references ([details](https://github.com/globalsign/mgo/pull/146))
|
||||
* Improved windows test harness ([details](https://github.com/globalsign/mgo/pull/158))
|
||||
* Improved type and nil handling in the BSON codec ([details](https://github.com/globalsign/mgo/pull/147/files), [more](https://github.com/globalsign/mgo/pull/181))
|
||||
* Separated network read/write timeouts ([details](https://github.com/globalsign/mgo/pull/161))
|
||||
* Expanded dial string configuration options ([details](https://github.com/globalsign/mgo/pull/162))
|
||||
* Implement MongoTimestamp ([details](https://github.com/globalsign/mgo/pull/171))
|
||||
* Support setting `writeConcern` for `findAndModify` operations ([details](https://github.com/globalsign/mgo/pull/185))
|
||||
* Add `ssl` to the dial string options ([details](https://github.com/globalsign/mgo/pull/184))
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Thanks to
|
||||
* @aksentyev
|
||||
* @bachue
|
||||
* @bozaro
|
||||
* @BenLubar
|
||||
* @carldunham
|
||||
* @carter2000
|
||||
* @cedric-cordenier
|
||||
* @cezarsa
|
||||
* @DaytonG
|
||||
* @ddspog
|
||||
* @drichelson
|
||||
* @dvic
|
||||
* @eaglerayp
|
||||
* @feliixx
|
||||
* @fmpwizard
|
||||
* @gazoon
|
||||
* @gedge
|
||||
* @gnawux
|
||||
* @idy
|
||||
* @jameinel
|
||||
* @gazoon
|
||||
* @jefferickson
|
||||
* @johnlawsharrison
|
||||
* @KJTsanaktsidis
|
||||
* @larrycinnabar
|
||||
* @mapete94
|
||||
* @maxnoel
|
||||
* @mcspring
|
||||
* @Mei-Zhao
|
||||
* @peterdeka
|
||||
* @Reenjii
|
||||
* @roobre
|
||||
* @smoya
|
||||
* @steve-gray
|
||||
* @wgallagher
|
||||
* @tbruyelle
|
||||
* @wgallagher
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
[](https://godoc.org/github.com/globalsign/mgo/bson)
|
||||
|
||||
An Implementation of BSON for Go
|
||||
--------------------------------
|
||||
|
||||
Package bson is an implementation of the [BSON specification](http://bsonspec.org) for Go.
|
||||
|
||||
While the BSON package implements the BSON spec as faithfully as possible, there
|
||||
is some MongoDB specific behaviour (such as map keys `$in`, `$all`, etc) in the
|
||||
`bson` package. The priority is for backwards compatibility for the `mgo`
|
||||
driver, though fixes for obviously buggy behaviour is welcome (and features, etc
|
||||
behind feature flags).
|
||||
+40
-1
@@ -42,6 +42,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
@@ -426,6 +427,36 @@ func Now() time.Time {
|
||||
// strange reason has its own datatype defined in BSON.
|
||||
type MongoTimestamp int64
|
||||
|
||||
// Time returns the time part of ts which is stored with second precision.
|
||||
func (ts MongoTimestamp) Time() time.Time {
|
||||
return time.Unix(int64(uint64(ts)>>32), 0)
|
||||
}
|
||||
|
||||
// Counter returns the counter part of ts.
|
||||
func (ts MongoTimestamp) Counter() uint32 {
|
||||
return uint32(ts)
|
||||
}
|
||||
|
||||
// NewMongoTimestamp creates a timestamp using the given
|
||||
// date `t` (with second precision) and counter `c` (unique for `t`).
|
||||
//
|
||||
// Returns an error if time `t` is not between 1970-01-01T00:00:00Z
|
||||
// and 2106-02-07T06:28:15Z (inclusive).
|
||||
//
|
||||
// Note that two MongoTimestamps should never have the same (time, counter) combination:
|
||||
// the caller must ensure the counter `c` is increased if creating multiple MongoTimestamp
|
||||
// values for the same time `t` (ignoring fractions of seconds).
|
||||
func NewMongoTimestamp(t time.Time, c uint32) (MongoTimestamp, error) {
|
||||
u := t.Unix()
|
||||
if u < 0 || u > math.MaxUint32 {
|
||||
return -1, errors.New("invalid value for time")
|
||||
}
|
||||
|
||||
i := int64(u<<32 | int64(c))
|
||||
|
||||
return MongoTimestamp(i), nil
|
||||
}
|
||||
|
||||
type orderKey int64
|
||||
|
||||
// MaxKey is a special value that compares higher than all other possible BSON
|
||||
@@ -746,6 +777,14 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
|
||||
return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
|
||||
}
|
||||
inlineMap = info.Num
|
||||
case reflect.Ptr:
|
||||
// allow only pointer to struct
|
||||
if kind := field.Type.Elem().Kind(); kind != reflect.Struct {
|
||||
return nil, errors.New("Option ,inline allows a pointer only to a struct, was given pointer to " + kind.String())
|
||||
}
|
||||
|
||||
field.Type = field.Type.Elem()
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
sinfo, err := getStructInfo(field.Type)
|
||||
if err != nil {
|
||||
@@ -765,7 +804,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
|
||||
fieldsList = append(fieldsList, finfo)
|
||||
}
|
||||
default:
|
||||
panic("Option ,inline needs a struct value or map field")
|
||||
panic("Option ,inline needs a struct value or a pointer to a struct or map field")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
+14
-1
@@ -1,7 +1,8 @@
|
||||
package bson
|
||||
|
||||
// Current state of the JSON tag fallback option.
|
||||
// Current state of the JSON tag fallback option.
|
||||
var useJSONTagFallback = false
|
||||
var useRespectNilValues = false
|
||||
|
||||
// SetJSONTagFallback enables or disables the JSON-tag fallback for structure tagging. When this is enabled, structures
|
||||
// without BSON tags on a field will fall-back to using the JSON tag (if present).
|
||||
@@ -14,3 +15,15 @@ func SetJSONTagFallback(state bool) {
|
||||
func JSONTagFallbackState() bool {
|
||||
return useJSONTagFallback
|
||||
}
|
||||
|
||||
// SetRespectNilValues enables or disables serializing nil slices or maps to `null` values.
|
||||
// In other words it enables `encoding/json` compatible behaviour.
|
||||
func SetRespectNilValues(state bool) {
|
||||
useRespectNilValues = state
|
||||
}
|
||||
|
||||
// RespectNilValuesState returns the current status of the JSON nil slices and maps fallback compatibility option.
|
||||
// See SetRespectNilValues for more information.
|
||||
func RespectNilValuesState() bool {
|
||||
return useRespectNilValues
|
||||
}
|
||||
|
||||
+46
-4
@@ -176,9 +176,6 @@ func (d *decoder) readDocTo(out reflect.Value) {
|
||||
switch outk {
|
||||
case reflect.Map:
|
||||
keyType = outt.Key()
|
||||
if keyType.Kind() != reflect.String {
|
||||
panic("BSON map must have string keys. Got: " + outt.String())
|
||||
}
|
||||
if keyType != typeString {
|
||||
convertKey = true
|
||||
}
|
||||
@@ -240,7 +237,42 @@ func (d *decoder) readDocTo(out reflect.Value) {
|
||||
if d.readElemTo(e, kind) {
|
||||
k := reflect.ValueOf(name)
|
||||
if convertKey {
|
||||
k = k.Convert(keyType)
|
||||
mapKeyType := out.Type().Key()
|
||||
mapKeyKind := mapKeyType.Kind()
|
||||
|
||||
switch mapKeyKind {
|
||||
case reflect.Int:
|
||||
fallthrough
|
||||
case reflect.Int8:
|
||||
fallthrough
|
||||
case reflect.Int16:
|
||||
fallthrough
|
||||
case reflect.Int32:
|
||||
fallthrough
|
||||
case reflect.Int64:
|
||||
fallthrough
|
||||
case reflect.Uint:
|
||||
fallthrough
|
||||
case reflect.Uint8:
|
||||
fallthrough
|
||||
case reflect.Uint16:
|
||||
fallthrough
|
||||
case reflect.Uint32:
|
||||
fallthrough
|
||||
case reflect.Uint64:
|
||||
fallthrough
|
||||
case reflect.Float32:
|
||||
fallthrough
|
||||
case reflect.Float64:
|
||||
parsed := d.parseMapKeyAsFloat(k, mapKeyKind)
|
||||
k = reflect.ValueOf(parsed)
|
||||
case reflect.String:
|
||||
mapKeyType = keyType
|
||||
default:
|
||||
panic("BSON map must have string or decimal keys. Got: " + outt.String())
|
||||
}
|
||||
|
||||
k = k.Convert(mapKeyType)
|
||||
}
|
||||
out.SetMapIndex(k, e)
|
||||
}
|
||||
@@ -276,6 +308,16 @@ func (d *decoder) readDocTo(out reflect.Value) {
|
||||
d.docType = docType
|
||||
}
|
||||
|
||||
func (decoder) parseMapKeyAsFloat(k reflect.Value, mapKeyKind reflect.Kind) float64 {
|
||||
parsed, err := strconv.ParseFloat(k.String(), 64)
|
||||
if err != nil {
|
||||
panic("Map key is defined to be a decimal type (" + mapKeyKind.String() + ") but got error " +
|
||||
err.Error())
|
||||
}
|
||||
|
||||
return parsed
|
||||
}
|
||||
|
||||
func (d *decoder) readArrayDocTo(out reflect.Value) {
|
||||
end := int(d.readInt32())
|
||||
end += d.i - 4
|
||||
|
||||
+65
-13
@@ -60,6 +60,15 @@ var (
|
||||
typeTimeDuration = reflect.TypeOf(time.Duration(0))
|
||||
)
|
||||
|
||||
var (
|
||||
// spec for []uint8 or []byte encoding
|
||||
arrayOps = map[string]bool{
|
||||
"$in": true,
|
||||
"$nin": true,
|
||||
"$all": true,
|
||||
}
|
||||
)
|
||||
|
||||
const itoaCacheSize = 32
|
||||
|
||||
const (
|
||||
@@ -194,7 +203,7 @@ func (e *encoder) addDoc(v reflect.Value) {
|
||||
|
||||
func (e *encoder) addMap(v reflect.Value) {
|
||||
for _, k := range v.MapKeys() {
|
||||
e.addElem(k.String(), v.MapIndex(k), false)
|
||||
e.addElem(fmt.Sprint(k), v.MapIndex(k), false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,15 +229,48 @@ func (e *encoder) addStruct(v reflect.Value) {
|
||||
if info.Inline == nil {
|
||||
value = v.Field(info.Num)
|
||||
} else {
|
||||
value = v.FieldByIndex(info.Inline)
|
||||
// as pointers to struct are allowed here,
|
||||
// there is no guarantee that pointer won't be nil.
|
||||
//
|
||||
// It is expected allowed behaviour
|
||||
// so info.Inline MAY consist index to a nil pointer
|
||||
// and that is why we safely call v.FieldByIndex and just continue on panic
|
||||
field, errField := safeFieldByIndex(v, info.Inline)
|
||||
if errField != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
value = field
|
||||
}
|
||||
if info.OmitEmpty && isZero(value) {
|
||||
continue
|
||||
}
|
||||
if useRespectNilValues &&
|
||||
(value.Kind() == reflect.Slice || value.Kind() == reflect.Map) &&
|
||||
value.IsNil() {
|
||||
e.addElem(info.Key, reflect.ValueOf(nil), info.MinSize)
|
||||
continue
|
||||
}
|
||||
e.addElem(info.Key, value, info.MinSize)
|
||||
}
|
||||
}
|
||||
|
||||
func safeFieldByIndex(v reflect.Value, index []int) (result reflect.Value, err error) {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
switch r := recovered.(type) {
|
||||
case string:
|
||||
err = fmt.Errorf("%s", r)
|
||||
case error:
|
||||
err = r
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
result = v.FieldByIndex(index)
|
||||
return
|
||||
}
|
||||
|
||||
func isZero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
@@ -423,8 +465,13 @@ func (e *encoder) addElem(name string, v reflect.Value, minSize bool) {
|
||||
vt := v.Type()
|
||||
et := vt.Elem()
|
||||
if et.Kind() == reflect.Uint8 {
|
||||
e.addElemName(0x05, name)
|
||||
e.addBinary(0x00, v.Bytes())
|
||||
if arrayOps[name] {
|
||||
e.addElemName(0x04, name)
|
||||
e.addDoc(v)
|
||||
} else {
|
||||
e.addElemName(0x05, name)
|
||||
e.addBinary(0x00, v.Bytes())
|
||||
}
|
||||
} else if et == typeDocElem || et == typeRawDocElem {
|
||||
e.addElemName(0x03, name)
|
||||
e.addDoc(v)
|
||||
@@ -436,16 +483,21 @@ func (e *encoder) addElem(name string, v reflect.Value, minSize bool) {
|
||||
case reflect.Array:
|
||||
et := v.Type().Elem()
|
||||
if et.Kind() == reflect.Uint8 {
|
||||
e.addElemName(0x05, name)
|
||||
if v.CanAddr() {
|
||||
e.addBinary(0x00, v.Slice(0, v.Len()).Interface().([]byte))
|
||||
if arrayOps[name] {
|
||||
e.addElemName(0x04, name)
|
||||
e.addDoc(v)
|
||||
} else {
|
||||
n := v.Len()
|
||||
e.addInt32(int32(n))
|
||||
e.addBytes(0x00)
|
||||
for i := 0; i < n; i++ {
|
||||
el := v.Index(i)
|
||||
e.addBytes(byte(el.Uint()))
|
||||
e.addElemName(0x05, name)
|
||||
if v.CanAddr() {
|
||||
e.addBinary(0x00, v.Slice(0, v.Len()).Interface().([]byte))
|
||||
} else {
|
||||
n := v.Len()
|
||||
e.addInt32(int32(n))
|
||||
e.addBytes(0x00)
|
||||
for i := 0; i < n; i++ {
|
||||
el := v.Index(i)
|
||||
e.addBytes(byte(el.Uint()))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package bson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
// MinDocumentSize is the size of the smallest possible valid BSON document:
|
||||
// an int32 size header + 0x00 (end of document).
|
||||
MinDocumentSize = 5
|
||||
|
||||
// MaxDocumentSize is the largest possible size for a BSON document allowed by MongoDB,
|
||||
// that is, 16 MiB (see https://docs.mongodb.com/manual/reference/limits/).
|
||||
MaxDocumentSize = 16777216
|
||||
)
|
||||
|
||||
// ErrInvalidDocumentSize is an error returned when a BSON document's header
|
||||
// contains a size smaller than MinDocumentSize or greater than MaxDocumentSize.
|
||||
type ErrInvalidDocumentSize struct {
|
||||
DocumentSize int32
|
||||
}
|
||||
|
||||
func (e ErrInvalidDocumentSize) Error() string {
|
||||
return fmt.Sprintf("invalid document size %d", e.DocumentSize)
|
||||
}
|
||||
|
||||
// A Decoder reads and decodes BSON values from an input stream.
|
||||
type Decoder struct {
|
||||
source io.Reader
|
||||
}
|
||||
|
||||
// NewDecoder returns a new Decoder that reads from source.
|
||||
// It does not add any extra buffering, and may not read data from source beyond the BSON values requested.
|
||||
func NewDecoder(source io.Reader) *Decoder {
|
||||
return &Decoder{source: source}
|
||||
}
|
||||
|
||||
// Decode reads the next BSON-encoded value from its input and stores it in the value pointed to by v.
|
||||
// See the documentation for Unmarshal for details about the conversion of BSON into a Go value.
|
||||
func (dec *Decoder) Decode(v interface{}) (err error) {
|
||||
// BSON documents start with their size as a *signed* int32.
|
||||
var docSize int32
|
||||
if err = binary.Read(dec.source, binary.LittleEndian, &docSize); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if docSize < MinDocumentSize || docSize > MaxDocumentSize {
|
||||
return ErrInvalidDocumentSize{DocumentSize: docSize}
|
||||
}
|
||||
|
||||
docBuffer := bytes.NewBuffer(make([]byte, 0, docSize))
|
||||
if err = binary.Write(docBuffer, binary.LittleEndian, docSize); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// docSize is the *full* document's size (including the 4-byte size header,
|
||||
// which has already been read).
|
||||
if _, err = io.CopyN(docBuffer, dec.source, int64(docSize-4)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Let Unmarshal handle the rest.
|
||||
defer handleErr(&err)
|
||||
return Unmarshal(docBuffer.Bytes(), v)
|
||||
}
|
||||
|
||||
// An Encoder encodes and writes BSON values to an output stream.
|
||||
type Encoder struct {
|
||||
target io.Writer
|
||||
}
|
||||
|
||||
// NewEncoder returns a new Encoder that writes to target.
|
||||
func NewEncoder(target io.Writer) *Encoder {
|
||||
return &Encoder{target: target}
|
||||
}
|
||||
|
||||
// Encode encodes v to BSON, and if successful writes it to the Encoder's output stream.
|
||||
// See the documentation for Marshal for details about the conversion of Go values to BSON.
|
||||
func (enc *Encoder) Encode(v interface{}) error {
|
||||
data, err := Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = enc.target.Write(data)
|
||||
return err
|
||||
}
|
||||
+32
-49
@@ -55,25 +55,19 @@ type mongoCluster struct {
|
||||
masters mongoServers
|
||||
references int
|
||||
syncing bool
|
||||
direct bool
|
||||
failFast bool
|
||||
syncCount uint
|
||||
setName string
|
||||
cachedIndex map[string]bool
|
||||
sync chan bool
|
||||
dial dialer
|
||||
appName string
|
||||
dialInfo *DialInfo
|
||||
}
|
||||
|
||||
func newCluster(userSeeds []string, direct, failFast bool, dial dialer, setName string, appName string) *mongoCluster {
|
||||
func newCluster(userSeeds []string, info *DialInfo) *mongoCluster {
|
||||
cluster := &mongoCluster{
|
||||
userSeeds: userSeeds,
|
||||
references: 1,
|
||||
direct: direct,
|
||||
failFast: failFast,
|
||||
dial: dial,
|
||||
setName: setName,
|
||||
appName: appName,
|
||||
dial: dialer{info.Dial, info.DialServer},
|
||||
dialInfo: info,
|
||||
}
|
||||
cluster.serverSynced.L = cluster.RWMutex.RLocker()
|
||||
cluster.sync = make(chan bool, 1)
|
||||
@@ -145,7 +139,7 @@ type isMasterResult struct {
|
||||
|
||||
func (cluster *mongoCluster) isMaster(socket *mongoSocket, result *isMasterResult) error {
|
||||
// Monotonic let's it talk to a slave and still hold the socket.
|
||||
session := newSession(Monotonic, cluster, 10*time.Second)
|
||||
session := newSession(Monotonic, cluster, cluster.dialInfo)
|
||||
session.setSocket(socket)
|
||||
|
||||
var cmd = bson.D{{Name: "isMaster", Value: 1}}
|
||||
@@ -169,8 +163,8 @@ func (cluster *mongoCluster) isMaster(socket *mongoSocket, result *isMasterResul
|
||||
}
|
||||
|
||||
// Include the application name if set
|
||||
if cluster.appName != "" {
|
||||
meta["application"] = bson.M{"name": cluster.appName}
|
||||
if cluster.dialInfo.AppName != "" {
|
||||
meta["application"] = bson.M{"name": cluster.dialInfo.AppName}
|
||||
}
|
||||
|
||||
cmd = append(cmd, bson.DocElem{
|
||||
@@ -179,7 +173,7 @@ func (cluster *mongoCluster) isMaster(socket *mongoSocket, result *isMasterResul
|
||||
})
|
||||
})
|
||||
|
||||
err := session.Run(cmd, result)
|
||||
err := session.runOnSocket(socket, cmd, result)
|
||||
session.Close()
|
||||
return err
|
||||
}
|
||||
@@ -188,19 +182,7 @@ type possibleTimeout interface {
|
||||
Timeout() bool
|
||||
}
|
||||
|
||||
var syncSocketTimeout = 5 * time.Second
|
||||
|
||||
func (cluster *mongoCluster) syncServer(server *mongoServer) (info *mongoServerInfo, hosts []string, err error) {
|
||||
var syncTimeout time.Duration
|
||||
if raceDetector {
|
||||
// This variable is only ever touched by tests.
|
||||
globalMutex.Lock()
|
||||
syncTimeout = syncSocketTimeout
|
||||
globalMutex.Unlock()
|
||||
} else {
|
||||
syncTimeout = syncSocketTimeout
|
||||
}
|
||||
|
||||
addr := server.Addr
|
||||
log("SYNC Processing ", addr, "...")
|
||||
|
||||
@@ -208,7 +190,7 @@ func (cluster *mongoCluster) syncServer(server *mongoServer) (info *mongoServerI
|
||||
var result isMasterResult
|
||||
var tryerr error
|
||||
for retry := 0; ; retry++ {
|
||||
if retry == 3 || retry == 1 && cluster.failFast {
|
||||
if retry == 3 || retry == 1 && cluster.dialInfo.FailFast {
|
||||
return nil, nil, tryerr
|
||||
}
|
||||
if retry > 0 {
|
||||
@@ -220,16 +202,22 @@ func (cluster *mongoCluster) syncServer(server *mongoServer) (info *mongoServerI
|
||||
time.Sleep(syncShortDelay)
|
||||
}
|
||||
|
||||
// It's not clear what would be a good timeout here. Is it
|
||||
// better to wait longer or to retry?
|
||||
socket, _, err := server.AcquireSocket(0, syncTimeout)
|
||||
// Don't ever hit the pool limit for syncing
|
||||
config := cluster.dialInfo.Copy()
|
||||
config.PoolLimit = 0
|
||||
|
||||
socket, _, err := server.AcquireSocket(config)
|
||||
if err != nil {
|
||||
tryerr = err
|
||||
logf("SYNC Failed to get socket to %s: %v", addr, err)
|
||||
continue
|
||||
}
|
||||
err = cluster.isMaster(socket, &result)
|
||||
|
||||
// Restore the correct dial config before returning it to the pool
|
||||
socket.dialInfo = cluster.dialInfo
|
||||
socket.Release()
|
||||
|
||||
if err != nil {
|
||||
tryerr = err
|
||||
logf("SYNC Command 'ismaster' to %s failed: %v", addr, err)
|
||||
@@ -239,9 +227,9 @@ func (cluster *mongoCluster) syncServer(server *mongoServer) (info *mongoServerI
|
||||
break
|
||||
}
|
||||
|
||||
if cluster.setName != "" && result.SetName != cluster.setName {
|
||||
logf("SYNC Server %s is not a member of replica set %q", addr, cluster.setName)
|
||||
return nil, nil, fmt.Errorf("server %s is not a member of replica set %q", addr, cluster.setName)
|
||||
if cluster.dialInfo.ReplicaSetName != "" && result.SetName != cluster.dialInfo.ReplicaSetName {
|
||||
logf("SYNC Server %s is not a member of replica set %q", addr, cluster.dialInfo.ReplicaSetName)
|
||||
return nil, nil, fmt.Errorf("server %s is not a member of replica set %q", addr, cluster.dialInfo.ReplicaSetName)
|
||||
}
|
||||
|
||||
if result.IsMaster {
|
||||
@@ -253,7 +241,7 @@ func (cluster *mongoCluster) syncServer(server *mongoServer) (info *mongoServerI
|
||||
}
|
||||
} else if result.Secondary {
|
||||
debugf("SYNC %s is a slave.", addr)
|
||||
} else if cluster.direct {
|
||||
} else if cluster.dialInfo.Direct {
|
||||
logf("SYNC %s in unknown state. Pretending it's a slave due to direct connection.", addr)
|
||||
} else {
|
||||
logf("SYNC %s is neither a master nor a slave.", addr)
|
||||
@@ -384,7 +372,7 @@ func (cluster *mongoCluster) syncServersLoop() {
|
||||
break
|
||||
}
|
||||
cluster.references++ // Keep alive while syncing.
|
||||
direct := cluster.direct
|
||||
direct := cluster.dialInfo.Direct
|
||||
cluster.Unlock()
|
||||
|
||||
cluster.syncServersIteration(direct)
|
||||
@@ -399,7 +387,7 @@ func (cluster *mongoCluster) syncServersLoop() {
|
||||
|
||||
// Hold off before allowing another sync. No point in
|
||||
// burning CPU looking for down servers.
|
||||
if !cluster.failFast {
|
||||
if !cluster.dialInfo.FailFast {
|
||||
time.Sleep(syncShortDelay)
|
||||
}
|
||||
|
||||
@@ -441,7 +429,7 @@ func (cluster *mongoCluster) server(addr string, tcpaddr *net.TCPAddr) *mongoSer
|
||||
if server != nil {
|
||||
return server
|
||||
}
|
||||
return newServer(addr, tcpaddr, cluster.sync, cluster.dial)
|
||||
return newServer(addr, tcpaddr, cluster.sync, cluster.dial, cluster.dialInfo)
|
||||
}
|
||||
|
||||
func resolveAddr(addr string) (*net.TCPAddr, error) {
|
||||
@@ -610,13 +598,12 @@ func (cluster *mongoCluster) syncServersIteration(direct bool) {
|
||||
cluster.Unlock()
|
||||
}
|
||||
|
||||
// AcquireSocket returns a socket to a server in the cluster. If slaveOk is
|
||||
// AcquireSocketWithPoolTimeout returns a socket to a server in the cluster. If slaveOk is
|
||||
// true, it will attempt to return a socket to a slave server. If it is
|
||||
// false, the socket will necessarily be to a master server.
|
||||
func (cluster *mongoCluster) AcquireSocket(mode Mode, slaveOk bool, syncTimeout time.Duration, socketTimeout time.Duration, serverTags []bson.D, poolLimit int) (s *mongoSocket, err error) {
|
||||
func (cluster *mongoCluster) AcquireSocketWithPoolTimeout(mode Mode, slaveOk bool, syncTimeout time.Duration, serverTags []bson.D, info *DialInfo) (s *mongoSocket, err error) {
|
||||
var started time.Time
|
||||
var syncCount uint
|
||||
warnedLimit := false
|
||||
for {
|
||||
cluster.RLock()
|
||||
for {
|
||||
@@ -633,7 +620,7 @@ func (cluster *mongoCluster) AcquireSocket(mode Mode, slaveOk bool, syncTimeout
|
||||
// Initialize after fast path above.
|
||||
started = time.Now()
|
||||
syncCount = cluster.syncCount
|
||||
} else if syncTimeout != 0 && started.Before(time.Now().Add(-syncTimeout)) || cluster.failFast && cluster.syncCount != syncCount {
|
||||
} else if syncTimeout != 0 && started.Before(time.Now().Add(-syncTimeout)) || cluster.dialInfo.FailFast && cluster.syncCount != syncCount {
|
||||
cluster.RUnlock()
|
||||
return nil, errors.New("no reachable servers")
|
||||
}
|
||||
@@ -658,14 +645,10 @@ func (cluster *mongoCluster) AcquireSocket(mode Mode, slaveOk bool, syncTimeout
|
||||
continue
|
||||
}
|
||||
|
||||
s, abended, err := server.AcquireSocket(poolLimit, socketTimeout)
|
||||
if err == errPoolLimit {
|
||||
if !warnedLimit {
|
||||
warnedLimit = true
|
||||
log("WARNING: Per-server connection limit reached.")
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
s, abended, err := server.AcquireSocketWithBlocking(info)
|
||||
if err == errPoolTimeout {
|
||||
// No need to remove servers from the topology if acquiring a socket fails for this reason.
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
cluster.removeServer(server)
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package mgo
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// coarseTimeProvider provides a periodically updated (approximate) time value to
|
||||
// amortise the cost of frequent calls to time.Now.
|
||||
//
|
||||
// A read throughput increase of ~6% was measured when using coarseTimeProvider with the
|
||||
// high-precision event timer (HPET) on FreeBSD 11.1 and Go 1.10.1 after merging
|
||||
// #116.
|
||||
//
|
||||
// Calling Now returns a time.Time that is updated at the configured interval,
|
||||
// however due to scheduling the value may be marginally older than expected.
|
||||
//
|
||||
// coarseTimeProvider is safe for concurrent use.
|
||||
type coarseTimeProvider struct {
|
||||
once sync.Once
|
||||
stop chan struct{}
|
||||
last atomic.Value
|
||||
}
|
||||
|
||||
// Now returns the most recently acquired time.Time value.
|
||||
func (t *coarseTimeProvider) Now() time.Time {
|
||||
return t.last.Load().(time.Time)
|
||||
}
|
||||
|
||||
// Close stops the periodic update of t.
|
||||
//
|
||||
// Any subsequent calls to Now will return the same value forever.
|
||||
func (t *coarseTimeProvider) Close() {
|
||||
t.once.Do(func() {
|
||||
close(t.stop)
|
||||
})
|
||||
}
|
||||
|
||||
// newcoarseTimeProvider returns a coarseTimeProvider configured to update at granularity.
|
||||
func newcoarseTimeProvider(granularity time.Duration) *coarseTimeProvider {
|
||||
t := &coarseTimeProvider{
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
|
||||
t.last.Store(time.Now())
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(granularity)
|
||||
for {
|
||||
select {
|
||||
case <-t.stop:
|
||||
ticker.Stop()
|
||||
return
|
||||
case <-ticker.C:
|
||||
t.last.Store(time.Now())
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return t
|
||||
}
|
||||
+8
-4
@@ -1,9 +1,8 @@
|
||||
// Package mgo offers a rich MongoDB driver for Go.
|
||||
// Package mgo (pronounced as "mango") offers a rich MongoDB driver for Go.
|
||||
//
|
||||
// Details about the mgo project (pronounced as "mango") are found
|
||||
// in its web page:
|
||||
// Detailed documentation of the API is available at GoDoc:
|
||||
//
|
||||
// http://labix.org/mgo
|
||||
// https://godoc.org/github.com/globalsign/mgo
|
||||
//
|
||||
// Usage of the driver revolves around the concept of sessions. To
|
||||
// get started, obtain a session using the Dial function:
|
||||
@@ -26,6 +25,11 @@
|
||||
// of its life time, so its resources may be put back in the pool or
|
||||
// collected, depending on the case.
|
||||
//
|
||||
// There is a sub-package that provides support for BSON, which can be
|
||||
// used by itself as well:
|
||||
//
|
||||
// https://godoc.org/github.com/globalsign/mgo/bson
|
||||
//
|
||||
// For more details, see the documentation for the types and methods.
|
||||
//
|
||||
package mgo
|
||||
|
||||
+1
@@ -127,6 +127,7 @@ func (ss *saslSession) Step(serverData []byte) (clientData []byte, done bool, er
|
||||
if rc == C.SASL_CONTINUE {
|
||||
return clientData, false, nil
|
||||
}
|
||||
|
||||
return nil, false, saslError(rc, ss.conn, "cannot establish SASL session")
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ func NewClient(newHash func() hash.Hash, user, pass string) *Client {
|
||||
// Out returns the data to be sent to the server in the current step.
|
||||
func (c *Client) Out() []byte {
|
||||
if c.out.Len() == 0 {
|
||||
return nil
|
||||
return []byte{}
|
||||
}
|
||||
return c.out.Bytes()
|
||||
}
|
||||
|
||||
+152
-19
@@ -36,6 +36,18 @@ import (
|
||||
"github.com/globalsign/mgo/bson"
|
||||
)
|
||||
|
||||
// coarseTime is used to amortise the cost of querying the timecounter (possibly
|
||||
// incurring a syscall too) when setting a socket.lastTimeUsed value which
|
||||
// happens frequently in the hot-path.
|
||||
//
|
||||
// The lastTimeUsed value may be skewed by at least 25ms (see
|
||||
// coarseTimeProvider).
|
||||
var coarseTime *coarseTimeProvider
|
||||
|
||||
func init() {
|
||||
coarseTime = newcoarseTimeProvider(25 * time.Millisecond)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mongo server encapsulation.
|
||||
|
||||
@@ -55,6 +67,8 @@ type mongoServer struct {
|
||||
pingCount uint32
|
||||
closed bool
|
||||
abended bool
|
||||
poolWaiter *sync.Cond
|
||||
dialInfo *DialInfo
|
||||
}
|
||||
|
||||
type dialer struct {
|
||||
@@ -76,21 +90,27 @@ type mongoServerInfo struct {
|
||||
|
||||
var defaultServerInfo mongoServerInfo
|
||||
|
||||
func newServer(addr string, tcpaddr *net.TCPAddr, sync chan bool, dial dialer) *mongoServer {
|
||||
func newServer(addr string, tcpaddr *net.TCPAddr, syncChan chan bool, dial dialer, info *DialInfo) *mongoServer {
|
||||
server := &mongoServer{
|
||||
Addr: addr,
|
||||
ResolvedAddr: tcpaddr.String(),
|
||||
tcpaddr: tcpaddr,
|
||||
sync: sync,
|
||||
sync: syncChan,
|
||||
dial: dial,
|
||||
info: &defaultServerInfo,
|
||||
pingValue: time.Hour, // Push it back before an actual ping.
|
||||
dialInfo: info,
|
||||
}
|
||||
server.poolWaiter = sync.NewCond(server)
|
||||
go server.pinger(true)
|
||||
if info.MaxIdleTimeMS != 0 {
|
||||
go server.poolShrinker()
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
var errPoolLimit = errors.New("per-server connection limit reached")
|
||||
var errPoolTimeout = errors.New("could not acquire connection within pool timeout")
|
||||
var errServerClosed = errors.New("server was closed")
|
||||
|
||||
// AcquireSocket returns a socket for communicating with the server.
|
||||
@@ -101,7 +121,18 @@ var errServerClosed = errors.New("server was closed")
|
||||
// If the poolLimit argument is greater than zero and the number of sockets in
|
||||
// use in this server is greater than the provided limit, errPoolLimit is
|
||||
// returned.
|
||||
func (server *mongoServer) AcquireSocket(poolLimit int, timeout time.Duration) (socket *mongoSocket, abended bool, err error) {
|
||||
func (server *mongoServer) AcquireSocket(info *DialInfo) (socket *mongoSocket, abended bool, err error) {
|
||||
return server.acquireSocketInternal(info, false)
|
||||
}
|
||||
|
||||
// AcquireSocketWithBlocking wraps AcquireSocket, but if a socket is not available, it will _not_
|
||||
// return errPoolLimit. Instead, it will block waiting for a socket to become available. If poolTimeout
|
||||
// should elapse before a socket is available, it will return errPoolTimeout.
|
||||
func (server *mongoServer) AcquireSocketWithBlocking(info *DialInfo) (socket *mongoSocket, abended bool, err error) {
|
||||
return server.acquireSocketInternal(info, true)
|
||||
}
|
||||
|
||||
func (server *mongoServer) acquireSocketInternal(info *DialInfo, shouldBlock bool) (socket *mongoSocket, abended bool, err error) {
|
||||
for {
|
||||
server.Lock()
|
||||
abended = server.abended
|
||||
@@ -109,24 +140,71 @@ func (server *mongoServer) AcquireSocket(poolLimit int, timeout time.Duration) (
|
||||
server.Unlock()
|
||||
return nil, abended, errServerClosed
|
||||
}
|
||||
n := len(server.unusedSockets)
|
||||
if poolLimit > 0 && len(server.liveSockets)-n >= poolLimit {
|
||||
server.Unlock()
|
||||
return nil, false, errPoolLimit
|
||||
if info.PoolLimit > 0 {
|
||||
if shouldBlock {
|
||||
// Beautiful. Golang conditions don't have a WaitWithTimeout, so I've implemented the timeout
|
||||
// with a wait + broadcast. The broadcast will cause the loop here to re-check the timeout,
|
||||
// and fail if it is blown.
|
||||
// Yes, this is a spurious wakeup, but we can't do a directed signal without having one condition
|
||||
// variable per waiter, which would involve loop traversal in the RecycleSocket
|
||||
// method.
|
||||
// We also can't use the approach of turning a condition variable into a channel outlined in
|
||||
// https://github.com/golang/go/issues/16620, since the lock needs to be held in _this_ goroutine.
|
||||
waitDone := make(chan struct{})
|
||||
timeoutHit := false
|
||||
if info.PoolTimeout > 0 {
|
||||
go func() {
|
||||
select {
|
||||
case <-waitDone:
|
||||
case <-time.After(info.PoolTimeout):
|
||||
// timeoutHit is part of the wait condition, so needs to be changed under mutex.
|
||||
server.Lock()
|
||||
defer server.Unlock()
|
||||
timeoutHit = true
|
||||
server.poolWaiter.Broadcast()
|
||||
}
|
||||
}()
|
||||
}
|
||||
timeSpentWaiting := time.Duration(0)
|
||||
for len(server.liveSockets)-len(server.unusedSockets) >= info.PoolLimit && !timeoutHit {
|
||||
// We only count time spent in Wait(), and not time evaluating the entire loop,
|
||||
// so that in the happy non-blocking path where the condition above evaluates true
|
||||
// first time, we record a nice round zero wait time.
|
||||
waitStart := time.Now()
|
||||
// unlocks server mutex, waits, and locks again. Thus, the condition
|
||||
// above is evaluated only when the lock is held.
|
||||
server.poolWaiter.Wait()
|
||||
timeSpentWaiting += time.Since(waitStart)
|
||||
}
|
||||
close(waitDone)
|
||||
if timeoutHit {
|
||||
server.Unlock()
|
||||
stats.noticePoolTimeout(timeSpentWaiting)
|
||||
return nil, false, errPoolTimeout
|
||||
}
|
||||
// Record that we fetched a connection of of a socket list and how long we spent waiting
|
||||
stats.noticeSocketAcquisition(timeSpentWaiting)
|
||||
} else {
|
||||
if len(server.liveSockets)-len(server.unusedSockets) >= info.PoolLimit {
|
||||
server.Unlock()
|
||||
return nil, false, errPoolLimit
|
||||
}
|
||||
}
|
||||
}
|
||||
n := len(server.unusedSockets)
|
||||
if n > 0 {
|
||||
socket = server.unusedSockets[n-1]
|
||||
server.unusedSockets[n-1] = nil // Help GC.
|
||||
server.unusedSockets = server.unusedSockets[:n-1]
|
||||
info := server.info
|
||||
serverInfo := server.info
|
||||
server.Unlock()
|
||||
err = socket.InitialAcquire(info, timeout)
|
||||
err = socket.InitialAcquire(serverInfo, info)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
server.Unlock()
|
||||
socket, err = server.Connect(timeout)
|
||||
socket, err = server.Connect(info)
|
||||
if err == nil {
|
||||
server.Lock()
|
||||
// We've waited for the Connect, see if we got
|
||||
@@ -147,20 +225,18 @@ func (server *mongoServer) AcquireSocket(poolLimit int, timeout time.Duration) (
|
||||
|
||||
// Connect establishes a new connection to the server. This should
|
||||
// generally be done through server.AcquireSocket().
|
||||
func (server *mongoServer) Connect(timeout time.Duration) (*mongoSocket, error) {
|
||||
func (server *mongoServer) Connect(info *DialInfo) (*mongoSocket, error) {
|
||||
server.RLock()
|
||||
master := server.info.Master
|
||||
dial := server.dial
|
||||
server.RUnlock()
|
||||
|
||||
logf("Establishing new connection to %s (timeout=%s)...", server.Addr, timeout)
|
||||
logf("Establishing new connection to %s (timeout=%s)...", server.Addr, info.Timeout)
|
||||
var conn net.Conn
|
||||
var err error
|
||||
switch {
|
||||
case !dial.isSet():
|
||||
// Cannot do this because it lacks timeout support. :-(
|
||||
//conn, err = net.DialTCP("tcp", nil, server.tcpaddr)
|
||||
conn, err = net.DialTimeout("tcp", server.ResolvedAddr, timeout)
|
||||
conn, err = net.DialTimeout("tcp", server.ResolvedAddr, info.Timeout)
|
||||
if tcpconn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpconn.SetKeepAlive(true)
|
||||
} else if err == nil {
|
||||
@@ -180,7 +256,7 @@ func (server *mongoServer) Connect(timeout time.Duration) (*mongoSocket, error)
|
||||
logf("Connection to %s established.", server.Addr)
|
||||
|
||||
stats.conn(+1, master)
|
||||
return newSocket(server, conn, timeout), nil
|
||||
return newSocket(server, conn, info), nil
|
||||
}
|
||||
|
||||
// Close forces closing all sockets that are alive, whether
|
||||
@@ -221,8 +297,17 @@ func (server *mongoServer) close(waitForIdle bool) {
|
||||
func (server *mongoServer) RecycleSocket(socket *mongoSocket) {
|
||||
server.Lock()
|
||||
if !server.closed {
|
||||
socket.lastTimeUsed = coarseTime.Now() // A rough approximation of the current time - see courseTime
|
||||
server.unusedSockets = append(server.unusedSockets, socket)
|
||||
}
|
||||
// If anybody is waiting for a connection, they should try now.
|
||||
// Note that this _has_ to be broadcast, not signal; the signature of AcquireSocket
|
||||
// and AcquireSocketWithBlocking allow the caller to specify the max number of connections,
|
||||
// rather than that being an intrinsic property of the connection pool (I assume to ensure
|
||||
// that there is always a connection available for replset topology discovery). Thus, once
|
||||
// a connection is returned to the pool, _every_ waiter needs to check if the connection count
|
||||
// is underneath their particular value for poolLimit.
|
||||
server.poolWaiter.Broadcast()
|
||||
server.Unlock()
|
||||
}
|
||||
|
||||
@@ -314,7 +399,8 @@ func (server *mongoServer) pinger(loop bool) {
|
||||
time.Sleep(delay)
|
||||
}
|
||||
op := op
|
||||
socket, _, err := server.AcquireSocket(0, delay)
|
||||
|
||||
socket, _, err := server.AcquireSocket(server.dialInfo)
|
||||
if err == nil {
|
||||
start := time.Now()
|
||||
_, _ = socket.SimpleQuery(&op)
|
||||
@@ -346,6 +432,53 @@ func (server *mongoServer) pinger(loop bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (server *mongoServer) poolShrinker() {
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
for _ = range ticker.C {
|
||||
if server.closed {
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
server.Lock()
|
||||
unused := len(server.unusedSockets)
|
||||
if unused < server.dialInfo.MinPoolSize {
|
||||
server.Unlock()
|
||||
continue
|
||||
}
|
||||
now := time.Now()
|
||||
end := 0
|
||||
reclaimMap := map[*mongoSocket]struct{}{}
|
||||
// Because the acquisition and recycle are done at the tail of array,
|
||||
// the head is always the oldest unused socket.
|
||||
for _, s := range server.unusedSockets[:unused-server.dialInfo.MinPoolSize] {
|
||||
if s.lastTimeUsed.Add(time.Duration(server.dialInfo.MaxIdleTimeMS) * time.Millisecond).After(now) {
|
||||
break
|
||||
}
|
||||
end++
|
||||
reclaimMap[s] = struct{}{}
|
||||
}
|
||||
tbr := server.unusedSockets[:end]
|
||||
if end > 0 {
|
||||
next := make([]*mongoSocket, unused-end)
|
||||
copy(next, server.unusedSockets[end:])
|
||||
server.unusedSockets = next
|
||||
remainSockets := []*mongoSocket{}
|
||||
for _, s := range server.liveSockets {
|
||||
if _, ok := reclaimMap[s]; !ok {
|
||||
remainSockets = append(remainSockets, s)
|
||||
}
|
||||
}
|
||||
server.liveSockets = remainSockets
|
||||
stats.conn(-1*end, server.info.Master)
|
||||
}
|
||||
server.Unlock()
|
||||
|
||||
for _, s := range tbr {
|
||||
s.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type mongoServerSlice []*mongoServer
|
||||
|
||||
func (s mongoServerSlice) Len() int {
|
||||
@@ -432,7 +565,7 @@ func (servers *mongoServers) BestFit(mode Mode, serverTags []bson.D) *mongoServe
|
||||
if best == nil {
|
||||
best = next
|
||||
best.RLock()
|
||||
if serverTags != nil && !next.info.Mongos && !best.hasTags(serverTags) {
|
||||
if len(serverTags) != 0 && !next.info.Mongos && !best.hasTags(serverTags) {
|
||||
best.RUnlock()
|
||||
best = nil
|
||||
}
|
||||
@@ -441,7 +574,7 @@ func (servers *mongoServers) BestFit(mode Mode, serverTags []bson.D) *mongoServe
|
||||
next.RLock()
|
||||
swap := false
|
||||
switch {
|
||||
case serverTags != nil && !next.info.Mongos && !next.hasTags(serverTags):
|
||||
case len(serverTags) != 0 && !next.info.Mongos && !next.hasTags(serverTags):
|
||||
// Must have requested tags.
|
||||
case mode == Secondary && next.info.Master && !next.info.Mongos:
|
||||
// Must be a secondary or mongos.
|
||||
|
||||
+338
-44
@@ -28,6 +28,7 @@ package mgo
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
@@ -73,6 +74,14 @@ const (
|
||||
Monotonic Mode = 1
|
||||
// Strong mode is specific to mgo, and is same as Primary.
|
||||
Strong Mode = 2
|
||||
|
||||
// DefaultConnectionPoolLimit defines the default maximum number of
|
||||
// connections in the connection pool.
|
||||
//
|
||||
// To override this value set DialInfo.PoolLimit.
|
||||
DefaultConnectionPoolLimit = 4096
|
||||
|
||||
zeroDuration = time.Duration(0)
|
||||
)
|
||||
|
||||
// mgo.v3: Drop Strong mode, suffix all modes with "Mode".
|
||||
@@ -90,8 +99,6 @@ type Session struct {
|
||||
defaultdb string
|
||||
sourcedb string
|
||||
syncTimeout time.Duration
|
||||
sockTimeout time.Duration
|
||||
poolLimit int
|
||||
consistency Mode
|
||||
creds []Credential
|
||||
dialCred *Credential
|
||||
@@ -103,6 +110,8 @@ type Session struct {
|
||||
queryConfig query
|
||||
bypassValidation bool
|
||||
slaveOk bool
|
||||
|
||||
dialInfo *DialInfo
|
||||
}
|
||||
|
||||
// Database holds collections of documents
|
||||
@@ -195,7 +204,7 @@ const (
|
||||
// Dial will timeout after 10 seconds if a server isn't reached. The returned
|
||||
// session will timeout operations after one minute by default if servers aren't
|
||||
// available. To customize the timeout, see DialWithTimeout, SetSyncTimeout, and
|
||||
// SetSocketTimeout.
|
||||
// DialInfo Read/WriteTimeout.
|
||||
//
|
||||
// This method is generally called just once for a given cluster. Further
|
||||
// sessions to the same cluster are then established using the New or Copy
|
||||
@@ -271,11 +280,27 @@ const (
|
||||
// Defines the per-server socket pool limit. Defaults to 4096.
|
||||
// See Session.SetPoolLimit for details.
|
||||
//
|
||||
// minPoolSize=<limit>
|
||||
//
|
||||
// Defines the per-server socket pool minium size. Defaults to 0.
|
||||
//
|
||||
// maxIdleTimeMS=<millisecond>
|
||||
//
|
||||
// The maximum number of milliseconds that a connection can remain idle in the pool
|
||||
// before being removed and closed. If maxIdleTimeMS is 0, connections will never be
|
||||
// closed due to inactivity.
|
||||
//
|
||||
// appName=<appName>
|
||||
//
|
||||
// The identifier of this client application. This parameter is used to
|
||||
// annotate logs / profiler output and cannot exceed 128 bytes.
|
||||
//
|
||||
// ssl=<true|false>
|
||||
//
|
||||
// true: Initiate the connection with TLS/SSL.
|
||||
// false: Initiate the connection without TLS/SSL.
|
||||
// The default value is false.
|
||||
//
|
||||
// Relevant documentation:
|
||||
//
|
||||
// http://docs.mongodb.org/manual/reference/connection-string/
|
||||
@@ -313,6 +338,7 @@ func ParseURL(url string) (*DialInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ssl := false
|
||||
direct := false
|
||||
mechanism := ""
|
||||
service := ""
|
||||
@@ -322,8 +348,15 @@ func ParseURL(url string) (*DialInfo, error) {
|
||||
appName := ""
|
||||
readPreferenceMode := Primary
|
||||
var readPreferenceTagSets []bson.D
|
||||
minPoolSize := 0
|
||||
maxIdleTimeMS := 0
|
||||
safe := Safe{}
|
||||
for _, opt := range uinfo.options {
|
||||
switch opt.key {
|
||||
case "ssl":
|
||||
if v, err := strconv.ParseBool(opt.value); err == nil && v {
|
||||
ssl = true
|
||||
}
|
||||
case "authSource":
|
||||
source = opt.value
|
||||
case "authMechanism":
|
||||
@@ -332,6 +365,23 @@ func ParseURL(url string) (*DialInfo, error) {
|
||||
service = opt.value
|
||||
case "replicaSet":
|
||||
setName = opt.value
|
||||
case "w":
|
||||
safe.WMode = opt.value
|
||||
case "j":
|
||||
journal, err := strconv.ParseBool(opt.value)
|
||||
if err != nil {
|
||||
return nil, errors.New("bad value for j: " + opt.value)
|
||||
}
|
||||
safe.J = journal
|
||||
case "wtimeoutMS":
|
||||
timeout, err := strconv.Atoi(opt.value)
|
||||
if err != nil {
|
||||
return nil, errors.New("bad value for wtimeoutMS: " + opt.value)
|
||||
}
|
||||
if timeout < 0 {
|
||||
return nil, errors.New("bad value (negative) for wtimeoutMS: " + opt.value)
|
||||
}
|
||||
safe.WTimeout = timeout
|
||||
case "maxPoolSize":
|
||||
poolLimit, err = strconv.Atoi(opt.value)
|
||||
if err != nil {
|
||||
@@ -368,6 +418,22 @@ func ParseURL(url string) (*DialInfo, error) {
|
||||
doc = append(doc, bson.DocElem{Name: strings.TrimSpace(kvp[0]), Value: strings.TrimSpace(kvp[1])})
|
||||
}
|
||||
readPreferenceTagSets = append(readPreferenceTagSets, doc)
|
||||
case "minPoolSize":
|
||||
minPoolSize, err = strconv.Atoi(opt.value)
|
||||
if err != nil {
|
||||
return nil, errors.New("bad value for minPoolSize: " + opt.value)
|
||||
}
|
||||
if minPoolSize < 0 {
|
||||
return nil, errors.New("bad value (negative) for minPoolSize: " + opt.value)
|
||||
}
|
||||
case "maxIdleTimeMS":
|
||||
maxIdleTimeMS, err = strconv.Atoi(opt.value)
|
||||
if err != nil {
|
||||
return nil, errors.New("bad value for maxIdleTimeMS: " + opt.value)
|
||||
}
|
||||
if maxIdleTimeMS < 0 {
|
||||
return nil, errors.New("bad value (negative) for maxIdleTimeMS: " + opt.value)
|
||||
}
|
||||
case "connect":
|
||||
if opt.value == "direct" {
|
||||
direct = true
|
||||
@@ -401,7 +467,17 @@ func ParseURL(url string) (*DialInfo, error) {
|
||||
Mode: readPreferenceMode,
|
||||
TagSets: readPreferenceTagSets,
|
||||
},
|
||||
Safe: safe,
|
||||
ReplicaSetName: setName,
|
||||
MinPoolSize: minPoolSize,
|
||||
MaxIdleTimeMS: maxIdleTimeMS,
|
||||
}
|
||||
if ssl && info.DialServer == nil {
|
||||
// Set DialServer only if nil, we don't want to override user's settings.
|
||||
info.DialServer = func(addr *ServerAddr) (net.Conn, error) {
|
||||
conn, err := tls.Dial("tcp", addr.String(), &tls.Config{})
|
||||
return conn, err
|
||||
}
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
@@ -452,10 +528,38 @@ type DialInfo struct {
|
||||
Username string
|
||||
Password string
|
||||
|
||||
// PoolLimit defines the per-server socket pool limit. Defaults to 4096.
|
||||
// See Session.SetPoolLimit for details.
|
||||
// PoolLimit defines the per-server socket pool limit. Defaults to
|
||||
// DefaultConnectionPoolLimit. See Session.SetPoolLimit for details.
|
||||
PoolLimit int
|
||||
|
||||
// PoolTimeout defines max time to wait for a connection to become available
|
||||
// if the pool limit is reached. Defaults to zero, which means forever. See
|
||||
// Session.SetPoolTimeout for details
|
||||
PoolTimeout time.Duration
|
||||
|
||||
// ReadTimeout defines the maximum duration to wait for a response to be
|
||||
// read from MongoDB.
|
||||
//
|
||||
// This effectively limits the maximum query execution time. If a MongoDB
|
||||
// query duration exceeds this timeout, the caller will receive a timeout,
|
||||
// however MongoDB will continue processing the query. This duration must be
|
||||
// large enough to allow MongoDB to execute the query, and the response be
|
||||
// received over the network connection.
|
||||
//
|
||||
// Only limits the network read - does not include unmarshalling /
|
||||
// processing of the response. Defaults to DialInfo.Timeout. If 0, no
|
||||
// timeout is set.
|
||||
ReadTimeout time.Duration
|
||||
|
||||
// WriteTimeout defines the maximum duration of a write to MongoDB over the
|
||||
// network connection.
|
||||
//
|
||||
// This is can usually be low unless writing large documents, or over a high
|
||||
// latency link. Only limits network write time - does not include
|
||||
// marshalling/processing the request. Defaults to DialInfo.Timeout. If 0,
|
||||
// no timeout is set.
|
||||
WriteTimeout time.Duration
|
||||
|
||||
// The identifier of the client application which ran the operation.
|
||||
AppName string
|
||||
|
||||
@@ -463,6 +567,9 @@ type DialInfo struct {
|
||||
// Session.SetMode and Session.SelectServers.
|
||||
ReadPreference *ReadPreference
|
||||
|
||||
// Safe mostly defines write options, though there is RMode. See Session.SetSafe
|
||||
Safe Safe
|
||||
|
||||
// FailFast will cause connection and query attempts to fail faster when
|
||||
// the server is unavailable, instead of retrying until the configured
|
||||
// timeout period. Note that an unavailable server may silently drop
|
||||
@@ -475,6 +582,14 @@ type DialInfo struct {
|
||||
// cluster and establish connections with further servers too.
|
||||
Direct bool
|
||||
|
||||
// MinPoolSize defines The minimum number of connections in the connection pool.
|
||||
// Defaults to 0.
|
||||
MinPoolSize int
|
||||
|
||||
// The maximum number of milliseconds that a connection can remain idle in the pool
|
||||
// before being removed and closed.
|
||||
MaxIdleTimeMS int
|
||||
|
||||
// DialServer optionally specifies the dial function for establishing
|
||||
// connections with the MongoDB servers.
|
||||
DialServer func(addr *ServerAddr) (net.Conn, error)
|
||||
@@ -483,6 +598,79 @@ type DialInfo struct {
|
||||
Dial func(addr net.Addr) (net.Conn, error)
|
||||
}
|
||||
|
||||
// Copy returns a deep-copy of i.
|
||||
func (i *DialInfo) Copy() *DialInfo {
|
||||
var readPreference *ReadPreference
|
||||
if i.ReadPreference != nil {
|
||||
readPreference = &ReadPreference{
|
||||
Mode: i.ReadPreference.Mode,
|
||||
}
|
||||
readPreference.TagSets = make([]bson.D, len(i.ReadPreference.TagSets))
|
||||
copy(readPreference.TagSets, i.ReadPreference.TagSets)
|
||||
}
|
||||
|
||||
info := &DialInfo{
|
||||
Timeout: i.Timeout,
|
||||
Database: i.Database,
|
||||
ReplicaSetName: i.ReplicaSetName,
|
||||
Source: i.Source,
|
||||
Service: i.Service,
|
||||
ServiceHost: i.ServiceHost,
|
||||
Mechanism: i.Mechanism,
|
||||
Username: i.Username,
|
||||
Password: i.Password,
|
||||
PoolLimit: i.PoolLimit,
|
||||
PoolTimeout: i.PoolTimeout,
|
||||
ReadTimeout: i.ReadTimeout,
|
||||
WriteTimeout: i.WriteTimeout,
|
||||
AppName: i.AppName,
|
||||
ReadPreference: readPreference,
|
||||
FailFast: i.FailFast,
|
||||
Direct: i.Direct,
|
||||
MinPoolSize: i.MinPoolSize,
|
||||
MaxIdleTimeMS: i.MaxIdleTimeMS,
|
||||
DialServer: i.DialServer,
|
||||
Dial: i.Dial,
|
||||
}
|
||||
|
||||
info.Addrs = make([]string, len(i.Addrs))
|
||||
copy(info.Addrs, i.Addrs)
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
// readTimeout returns the configured read timeout, or i.Timeout if it's not set
|
||||
func (i *DialInfo) readTimeout() time.Duration {
|
||||
if i.ReadTimeout == zeroDuration {
|
||||
return i.Timeout
|
||||
}
|
||||
return i.ReadTimeout
|
||||
}
|
||||
|
||||
// writeTimeout returns the configured write timeout, or i.Timeout if it's not
|
||||
// set
|
||||
func (i *DialInfo) writeTimeout() time.Duration {
|
||||
if i.WriteTimeout == zeroDuration {
|
||||
return i.Timeout
|
||||
}
|
||||
return i.WriteTimeout
|
||||
}
|
||||
|
||||
// roundTripTimeout returns the total time allocated for a single network read
|
||||
// and write.
|
||||
func (i *DialInfo) roundTripTimeout() time.Duration {
|
||||
return i.readTimeout() + i.writeTimeout()
|
||||
}
|
||||
|
||||
// poolLimit returns the configured connection pool size, or
|
||||
// DefaultConnectionPoolLimit.
|
||||
func (i *DialInfo) poolLimit() int {
|
||||
if i.PoolLimit == 0 {
|
||||
return DefaultConnectionPoolLimit
|
||||
}
|
||||
return i.PoolLimit
|
||||
}
|
||||
|
||||
// ReadPreference defines the manner in which servers are chosen.
|
||||
type ReadPreference struct {
|
||||
// Mode determines the consistency of results. See Session.SetMode.
|
||||
@@ -512,7 +700,12 @@ func (addr *ServerAddr) TCPAddr() *net.TCPAddr {
|
||||
}
|
||||
|
||||
// DialWithInfo establishes a new session to the cluster identified by info.
|
||||
func DialWithInfo(info *DialInfo) (*Session, error) {
|
||||
func DialWithInfo(dialInfo *DialInfo) (*Session, error) {
|
||||
info := dialInfo.Copy()
|
||||
info.PoolLimit = info.poolLimit()
|
||||
info.ReadTimeout = info.readTimeout()
|
||||
info.WriteTimeout = info.writeTimeout()
|
||||
|
||||
addrs := make([]string, len(info.Addrs))
|
||||
for i, addr := range info.Addrs {
|
||||
p := strings.LastIndexAny(addr, "]:")
|
||||
@@ -522,8 +715,8 @@ func DialWithInfo(info *DialInfo) (*Session, error) {
|
||||
}
|
||||
addrs[i] = addr
|
||||
}
|
||||
cluster := newCluster(addrs, info.Direct, info.FailFast, dialer{info.Dial, info.DialServer}, info.ReplicaSetName, info.AppName)
|
||||
session := newSession(Eventual, cluster, info.Timeout)
|
||||
cluster := newCluster(addrs, info)
|
||||
session := newSession(Eventual, cluster, info)
|
||||
session.defaultdb = info.Database
|
||||
if session.defaultdb == "" {
|
||||
session.defaultdb = "test"
|
||||
@@ -551,9 +744,7 @@ func DialWithInfo(info *DialInfo) (*Session, error) {
|
||||
}
|
||||
session.creds = []Credential{*session.dialCred}
|
||||
}
|
||||
if info.PoolLimit > 0 {
|
||||
session.poolLimit = info.PoolLimit
|
||||
}
|
||||
|
||||
cluster.Release()
|
||||
|
||||
// People get confused when we return a session that is not actually
|
||||
@@ -565,6 +756,8 @@ func DialWithInfo(info *DialInfo) (*Session, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session.SetSafe(&info.Safe)
|
||||
|
||||
if info.ReadPreference != nil {
|
||||
session.SelectServers(info.ReadPreference.TagSets...)
|
||||
session.SetMode(info.ReadPreference.Mode, true)
|
||||
@@ -572,6 +765,8 @@ func DialWithInfo(info *DialInfo) (*Session, error) {
|
||||
session.SetMode(Strong, true)
|
||||
}
|
||||
|
||||
session.dialInfo = info
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
@@ -632,13 +827,12 @@ func extractURL(s string) (*urlInfo, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func newSession(consistency Mode, cluster *mongoCluster, timeout time.Duration) (session *Session) {
|
||||
func newSession(consistency Mode, cluster *mongoCluster, info *DialInfo) (session *Session) {
|
||||
cluster.Acquire()
|
||||
session = &Session{
|
||||
mgoCluster: cluster,
|
||||
syncTimeout: timeout,
|
||||
sockTimeout: timeout,
|
||||
poolLimit: 4096,
|
||||
syncTimeout: info.Timeout,
|
||||
dialInfo: info,
|
||||
}
|
||||
debugf("New session %p on cluster %p", session, cluster)
|
||||
session.SetMode(consistency, true)
|
||||
@@ -667,8 +861,6 @@ func copySession(session *Session, keepCreds bool) (s *Session) {
|
||||
defaultdb: session.defaultdb,
|
||||
sourcedb: session.sourcedb,
|
||||
syncTimeout: session.syncTimeout,
|
||||
sockTimeout: session.sockTimeout,
|
||||
poolLimit: session.poolLimit,
|
||||
consistency: session.consistency,
|
||||
creds: creds,
|
||||
dialCred: session.dialCred,
|
||||
@@ -680,6 +872,7 @@ func copySession(session *Session, keepCreds bool) (s *Session) {
|
||||
queryConfig: session.queryConfig,
|
||||
bypassValidation: session.bypassValidation,
|
||||
slaveOk: session.slaveOk,
|
||||
dialInfo: session.dialInfo,
|
||||
}
|
||||
s = &scopy
|
||||
debugf("New session %p on cluster %p (copy from %p)", s, cluster, session)
|
||||
@@ -806,6 +999,15 @@ func (db *Database) Run(cmd interface{}, result interface{}) error {
|
||||
return db.run(socket, cmd, result)
|
||||
}
|
||||
|
||||
// runOnSocket does the same as Run, but guarantees that your command will be run
|
||||
// on the provided socket instance; if it's unhealthy, you will receive the error
|
||||
// from it.
|
||||
func (db *Database) runOnSocket(socket *mongoSocket, cmd interface{}, result interface{}) error {
|
||||
socket.Acquire()
|
||||
defer socket.Release()
|
||||
return db.run(socket, cmd, result)
|
||||
}
|
||||
|
||||
// Credential holds details to authenticate with a MongoDB server.
|
||||
type Credential struct {
|
||||
// Username and Password hold the basic details for authentication.
|
||||
@@ -1270,7 +1472,6 @@ type Index struct {
|
||||
// Collation allows users to specify language-specific rules for string comparison,
|
||||
// such as rules for lettercase and accent marks.
|
||||
type Collation struct {
|
||||
|
||||
// Locale defines the collation locale.
|
||||
Locale string `bson:"locale"`
|
||||
|
||||
@@ -1956,13 +2157,21 @@ func (s *Session) SetSyncTimeout(d time.Duration) {
|
||||
s.m.Unlock()
|
||||
}
|
||||
|
||||
// SetSocketTimeout sets the amount of time to wait for a non-responding
|
||||
// socket to the database before it is forcefully closed.
|
||||
// SetSocketTimeout is deprecated - use DialInfo read/write timeouts instead.
|
||||
//
|
||||
// SetSocketTimeout sets the amount of time to wait for a non-responding socket
|
||||
// to the database before it is forcefully closed.
|
||||
//
|
||||
// The default timeout is 1 minute.
|
||||
func (s *Session) SetSocketTimeout(d time.Duration) {
|
||||
s.m.Lock()
|
||||
s.sockTimeout = d
|
||||
|
||||
// Set both the read and write timeout, as well as the DialInfo.Timeout for
|
||||
// backwards compatibility,
|
||||
s.dialInfo.Timeout = d
|
||||
s.dialInfo.ReadTimeout = d
|
||||
s.dialInfo.WriteTimeout = d
|
||||
|
||||
if s.masterSocket != nil {
|
||||
s.masterSocket.SetTimeout(d)
|
||||
}
|
||||
@@ -1996,7 +2205,17 @@ func (s *Session) SetCursorTimeout(d time.Duration) {
|
||||
// of used resources and number of goroutines before they are created.
|
||||
func (s *Session) SetPoolLimit(limit int) {
|
||||
s.m.Lock()
|
||||
s.poolLimit = limit
|
||||
s.dialInfo.PoolLimit = limit
|
||||
s.m.Unlock()
|
||||
}
|
||||
|
||||
// SetPoolTimeout sets the maxinum time connection attempts will wait to reuse
|
||||
// an existing connection from the pool if the PoolLimit has been reached. If
|
||||
// the value is exceeded, the attempt to use a session will fail with an error.
|
||||
// The default value is zero, which means to wait forever with no timeout.
|
||||
func (s *Session) SetPoolTimeout(timeout time.Duration) {
|
||||
s.m.Lock()
|
||||
s.dialInfo.PoolTimeout = timeout
|
||||
s.m.Unlock()
|
||||
}
|
||||
|
||||
@@ -2270,6 +2489,13 @@ func (s *Session) Run(cmd interface{}, result interface{}) error {
|
||||
return s.DB("admin").Run(cmd, result)
|
||||
}
|
||||
|
||||
// runOnSocket does the same as Run, but guarantees that your command will be run
|
||||
// on the provided socket instance; if it's unhealthy, you will receive the error
|
||||
// from it.
|
||||
func (s *Session) runOnSocket(socket *mongoSocket, cmd interface{}, result interface{}) error {
|
||||
return s.DB("admin").runOnSocket(socket, cmd, result)
|
||||
}
|
||||
|
||||
// SelectServers restricts communication to servers configured with the
|
||||
// given tags. For example, the following statement restricts servers
|
||||
// used for reading operations to those with both tag "disk" set to
|
||||
@@ -2431,6 +2657,7 @@ type Pipe struct {
|
||||
allowDisk bool
|
||||
batchSize int
|
||||
maxTimeMS int64
|
||||
collation *Collation
|
||||
}
|
||||
|
||||
type pipeCmd struct {
|
||||
@@ -2440,6 +2667,7 @@ type pipeCmd struct {
|
||||
Explain bool `bson:",omitempty"`
|
||||
AllowDisk bool `bson:"allowDiskUse,omitempty"`
|
||||
MaxTimeMS int64 `bson:"maxTimeMS,omitempty"`
|
||||
Collation *Collation `bson:"collation,omitempty"`
|
||||
}
|
||||
|
||||
type pipeCmdCursor struct {
|
||||
@@ -2460,6 +2688,7 @@ type pipeCmdCursor struct {
|
||||
// http://docs.mongodb.org/manual/applications/aggregation
|
||||
// http://docs.mongodb.org/manual/tutorial/aggregation-examples
|
||||
//
|
||||
|
||||
func (c *Collection) Pipe(pipeline interface{}) *Pipe {
|
||||
session := c.Database.Session
|
||||
session.m.RLock()
|
||||
@@ -2493,6 +2722,7 @@ func (p *Pipe) Iter() *Iter {
|
||||
Pipeline: p.pipeline,
|
||||
AllowDisk: p.allowDisk,
|
||||
Cursor: &pipeCmdCursor{p.batchSize},
|
||||
Collation: p.collation,
|
||||
}
|
||||
if p.maxTimeMS > 0 {
|
||||
cmd.MaxTimeMS = p.maxTimeMS
|
||||
@@ -2682,6 +2912,23 @@ func (p *Pipe) SetMaxTime(d time.Duration) *Pipe {
|
||||
return p
|
||||
}
|
||||
|
||||
|
||||
// Collation allows to specify language-specific rules for string comparison,
|
||||
// such as rules for lettercase and accent marks.
|
||||
// When specifying collation, the locale field is mandatory; all other collation
|
||||
// fields are optional
|
||||
//
|
||||
// Relevant documentation:
|
||||
//
|
||||
// https://docs.mongodb.com/manual/reference/collation/
|
||||
//
|
||||
func (p *Pipe) Collation(collation *Collation) *Pipe {
|
||||
if collation != nil {
|
||||
p.collation = collation
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// LastError the error status of the preceding write operation on the current connection.
|
||||
//
|
||||
// Relevant documentation:
|
||||
@@ -4037,9 +4284,11 @@ func (iter *Iter) Timeout() bool {
|
||||
//
|
||||
// Next returns true if a document was successfully unmarshalled onto result,
|
||||
// and false at the end of the result set or if an error happened.
|
||||
// When Next returns false, the Err method should be called to verify if
|
||||
// there was an error during iteration, and the Timeout method to verify if the
|
||||
// false return value was caused by a timeout (no available results).
|
||||
// When Next returns false, either the Err method or the Close method should be
|
||||
// called to verify if there was an error during iteration. While both will
|
||||
// return the error (or nil), Close will also release the cursor on the server.
|
||||
// The Timeout method may also be called to verify if the false return value
|
||||
// was caused by a timeout (no available results).
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
@@ -4047,6 +4296,9 @@ func (iter *Iter) Timeout() bool {
|
||||
// for iter.Next(&result) {
|
||||
// fmt.Printf("Result: %v\n", result.Id)
|
||||
// }
|
||||
// if iter.Timeout() {
|
||||
// // react to timeout
|
||||
// }
|
||||
// if err := iter.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
@@ -4175,10 +4427,19 @@ func (iter *Iter) Next(result interface{}) bool {
|
||||
//
|
||||
func (iter *Iter) All(result interface{}) error {
|
||||
resultv := reflect.ValueOf(result)
|
||||
if resultv.Kind() != reflect.Ptr || resultv.Elem().Kind() != reflect.Slice {
|
||||
if resultv.Kind() != reflect.Ptr {
|
||||
panic("result argument must be a slice address")
|
||||
}
|
||||
|
||||
slicev := resultv.Elem()
|
||||
|
||||
if slicev.Kind() == reflect.Interface {
|
||||
slicev = slicev.Elem()
|
||||
}
|
||||
if slicev.Kind() != reflect.Slice {
|
||||
panic("result argument must be a slice address")
|
||||
}
|
||||
|
||||
slicev = slicev.Slice(0, slicev.Cap())
|
||||
elemt := slicev.Type().Elem()
|
||||
i := 0
|
||||
@@ -4257,11 +4518,13 @@ func (iter *Iter) acquireSocket() (*mongoSocket, error) {
|
||||
// with Eventual sessions, if a Refresh is done, or if a
|
||||
// monotonic session gets a write and shifts from secondary
|
||||
// to primary. Our cursor is in a specific server, though.
|
||||
|
||||
iter.session.m.Lock()
|
||||
sockTimeout := iter.session.sockTimeout
|
||||
info := iter.session.dialInfo
|
||||
iter.session.m.Unlock()
|
||||
|
||||
socket.Release()
|
||||
socket, _, err = iter.server.AcquireSocket(0, sockTimeout)
|
||||
socket, _, err = iter.server.AcquireSocket(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -4334,10 +4597,11 @@ func (iter *Iter) getMoreCmd() *queryOp {
|
||||
type countCmd struct {
|
||||
Count string
|
||||
Query interface{}
|
||||
Limit int32 `bson:",omitempty"`
|
||||
Skip int32 `bson:",omitempty"`
|
||||
Hint bson.D `bson:"hint,omitempty"`
|
||||
MaxTimeMS int `bson:"maxTimeMS,omitempty"`
|
||||
Limit int32 `bson:",omitempty"`
|
||||
Skip int32 `bson:",omitempty"`
|
||||
Hint bson.D `bson:"hint,omitempty"`
|
||||
MaxTimeMS int `bson:"maxTimeMS,omitempty"`
|
||||
Collation *Collation `bson:"collation,omitempty"`
|
||||
}
|
||||
|
||||
// Count returns the total number of documents in the result set.
|
||||
@@ -4363,7 +4627,7 @@ func (q *Query) Count() (n int, err error) {
|
||||
// simply want a Zero bson.D
|
||||
hint, _ := q.op.options.Hint.(bson.D)
|
||||
result := struct{ N int }{}
|
||||
err = session.DB(dbname).Run(countCmd{cname, query, limit, op.skip, hint, op.options.MaxTimeMS}, &result)
|
||||
err = session.DB(dbname).Run(countCmd{cname, query, limit, op.skip, hint, op.options.MaxTimeMS, op.options.Collation}, &result)
|
||||
|
||||
return result.N, err
|
||||
}
|
||||
@@ -4644,11 +4908,13 @@ type findModifyCmd struct {
|
||||
Collection string `bson:"findAndModify"`
|
||||
Query, Update, Sort, Fields interface{} `bson:",omitempty"`
|
||||
Upsert, Remove, New bool `bson:",omitempty"`
|
||||
WriteConcern interface{} `bson:"writeConcern"`
|
||||
}
|
||||
|
||||
type valueResult struct {
|
||||
Value bson.Raw
|
||||
LastError LastError `bson:"lastErrorObject"`
|
||||
Value bson.Raw
|
||||
LastError LastError `bson:"lastErrorObject"`
|
||||
ConcernError writeConcernError `bson:"writeConcernError"`
|
||||
}
|
||||
|
||||
// Apply runs the findAndModify MongoDB command, which allows updating, upserting
|
||||
@@ -4656,6 +4922,8 @@ type valueResult struct {
|
||||
// version (the default) or the new version of the document (when ReturnNew is true).
|
||||
// If no objects are found Apply returns ErrNotFound.
|
||||
//
|
||||
// If the session is in safe mode, the LastError result will be returned as err.
|
||||
//
|
||||
// The Sort and Select query methods affect the result of Apply. In case
|
||||
// multiple documents match the query, Sort enables selecting which document to
|
||||
// act upon by ordering it first. Select enables retrieving only a selection
|
||||
@@ -4692,15 +4960,27 @@ func (q *Query) Apply(change Change, result interface{}) (info *ChangeInfo, err
|
||||
dbname := op.collection[:c]
|
||||
cname := op.collection[c+1:]
|
||||
|
||||
// https://docs.mongodb.com/manual/reference/command/findAndModify/#dbcmd.findAndModify
|
||||
session.m.RLock()
|
||||
safeOp := session.safeOp
|
||||
session.m.RUnlock()
|
||||
var writeConcern interface{}
|
||||
if safeOp == nil {
|
||||
writeConcern = bson.D{{Name: "w", Value: 0}}
|
||||
} else {
|
||||
writeConcern = safeOp.query.(*getLastError)
|
||||
}
|
||||
|
||||
cmd := findModifyCmd{
|
||||
Collection: cname,
|
||||
Update: change.Update,
|
||||
Upsert: change.Upsert,
|
||||
Remove: change.Remove,
|
||||
New: change.ReturnNew,
|
||||
Query: op.query,
|
||||
Sort: op.options.OrderBy,
|
||||
Fields: op.selector,
|
||||
Collection: cname,
|
||||
Update: change.Update,
|
||||
Upsert: change.Upsert,
|
||||
Remove: change.Remove,
|
||||
New: change.ReturnNew,
|
||||
Query: op.query,
|
||||
Sort: op.options.OrderBy,
|
||||
Fields: op.selector,
|
||||
WriteConcern: writeConcern,
|
||||
}
|
||||
|
||||
session = session.Clone()
|
||||
@@ -4743,6 +5023,14 @@ func (q *Query) Apply(change Change, result interface{}) (info *ChangeInfo, err
|
||||
} else if change.Upsert {
|
||||
info.UpsertedId = lerr.UpsertedId
|
||||
}
|
||||
if doc.ConcernError.Code != 0 {
|
||||
var lerr LastError
|
||||
e := doc.ConcernError
|
||||
lerr.Code = e.Code
|
||||
lerr.Err = e.ErrMsg
|
||||
err = &lerr
|
||||
return info, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@@ -4850,7 +5138,13 @@ func (s *Session) acquireSocket(slaveOk bool) (*mongoSocket, error) {
|
||||
}
|
||||
|
||||
// Still not good. We need a new socket.
|
||||
sock, err := s.cluster().AcquireSocket(s.consistency, slaveOk && s.slaveOk, s.syncTimeout, s.sockTimeout, s.queryConfig.op.serverTags, s.poolLimit)
|
||||
sock, err := s.cluster().AcquireSocketWithPoolTimeout(
|
||||
s.consistency,
|
||||
slaveOk && s.slaveOk,
|
||||
s.syncTimeout,
|
||||
s.queryConfig.op.serverTags,
|
||||
s.dialInfo,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+31
-14
@@ -42,7 +42,6 @@ type mongoSocket struct {
|
||||
sync.Mutex
|
||||
server *mongoServer // nil when cached
|
||||
conn net.Conn
|
||||
timeout time.Duration
|
||||
addr string // For debugging only.
|
||||
nextRequestId uint32
|
||||
replyFuncs map[uint32]replyFunc
|
||||
@@ -54,7 +53,10 @@ type mongoSocket struct {
|
||||
dead error
|
||||
serverInfo *mongoServerInfo
|
||||
closeAfterIdle bool
|
||||
lastTimeUsed time.Time // for time based idle socket release
|
||||
sendMeta sync.Once
|
||||
|
||||
dialInfo *DialInfo
|
||||
}
|
||||
|
||||
type queryOpFlags uint32
|
||||
@@ -180,15 +182,16 @@ type requestInfo struct {
|
||||
replyFunc replyFunc
|
||||
}
|
||||
|
||||
func newSocket(server *mongoServer, conn net.Conn, timeout time.Duration) *mongoSocket {
|
||||
func newSocket(server *mongoServer, conn net.Conn, info *DialInfo) *mongoSocket {
|
||||
socket := &mongoSocket{
|
||||
conn: conn,
|
||||
addr: server.Addr,
|
||||
server: server,
|
||||
replyFuncs: make(map[uint32]replyFunc),
|
||||
dialInfo: info,
|
||||
}
|
||||
socket.gotNonce.L = &socket.Mutex
|
||||
if err := socket.InitialAcquire(server.Info(), timeout); err != nil {
|
||||
if err := socket.InitialAcquire(server.Info(), info); err != nil {
|
||||
panic("newSocket: InitialAcquire returned error: " + err.Error())
|
||||
}
|
||||
stats.socketsAlive(+1)
|
||||
@@ -222,7 +225,7 @@ func (socket *mongoSocket) ServerInfo() *mongoServerInfo {
|
||||
// InitialAcquire obtains the first reference to the socket, either
|
||||
// right after the connection is made or once a recycled socket is
|
||||
// being put back in use.
|
||||
func (socket *mongoSocket) InitialAcquire(serverInfo *mongoServerInfo, timeout time.Duration) error {
|
||||
func (socket *mongoSocket) InitialAcquire(serverInfo *mongoServerInfo, dialInfo *DialInfo) error {
|
||||
socket.Lock()
|
||||
if socket.references > 0 {
|
||||
panic("Socket acquired out of cache with references")
|
||||
@@ -234,7 +237,7 @@ func (socket *mongoSocket) InitialAcquire(serverInfo *mongoServerInfo, timeout t
|
||||
}
|
||||
socket.references++
|
||||
socket.serverInfo = serverInfo
|
||||
socket.timeout = timeout
|
||||
socket.dialInfo = dialInfo
|
||||
stats.socketsInUse(+1)
|
||||
stats.socketRefs(+1)
|
||||
socket.Unlock()
|
||||
@@ -287,7 +290,8 @@ func (socket *mongoSocket) Release() {
|
||||
// SetTimeout changes the timeout used on socket operations.
|
||||
func (socket *mongoSocket) SetTimeout(d time.Duration) {
|
||||
socket.Lock()
|
||||
socket.timeout = d
|
||||
socket.dialInfo.ReadTimeout = d
|
||||
socket.dialInfo.WriteTimeout = d
|
||||
socket.Unlock()
|
||||
}
|
||||
|
||||
@@ -300,24 +304,37 @@ const (
|
||||
|
||||
func (socket *mongoSocket) updateDeadline(which deadlineType) {
|
||||
var when time.Time
|
||||
if socket.timeout > 0 {
|
||||
when = time.Now().Add(socket.timeout)
|
||||
}
|
||||
whichstr := ""
|
||||
var whichStr string
|
||||
switch which {
|
||||
case readDeadline | writeDeadline:
|
||||
whichstr = "read/write"
|
||||
if socket.dialInfo.roundTripTimeout() == 0 {
|
||||
return
|
||||
}
|
||||
whichStr = "read/write"
|
||||
when = time.Now().Add(socket.dialInfo.roundTripTimeout())
|
||||
socket.conn.SetDeadline(when)
|
||||
|
||||
case readDeadline:
|
||||
whichstr = "read"
|
||||
if socket.dialInfo.ReadTimeout == zeroDuration {
|
||||
return
|
||||
}
|
||||
whichStr = "read"
|
||||
when = time.Now().Add(socket.dialInfo.ReadTimeout)
|
||||
socket.conn.SetReadDeadline(when)
|
||||
|
||||
case writeDeadline:
|
||||
whichstr = "write"
|
||||
if socket.dialInfo.WriteTimeout == zeroDuration {
|
||||
return
|
||||
}
|
||||
whichStr = "write"
|
||||
when = time.Now().Add(socket.dialInfo.WriteTimeout)
|
||||
socket.conn.SetWriteDeadline(when)
|
||||
|
||||
default:
|
||||
panic("invalid parameter to updateDeadline")
|
||||
}
|
||||
debugf("Socket %p to %s: updated %s deadline to %s ahead (%s)", socket, socket.addr, whichstr, socket.timeout, when)
|
||||
|
||||
debugf("Socket %p to %s: updated %s deadline to %s", socket, socket.addr, whichStr, when)
|
||||
}
|
||||
|
||||
// Close terminates the socket use.
|
||||
|
||||
+36
-9
@@ -28,6 +28,7 @@ package mgo
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var stats *Stats
|
||||
@@ -77,15 +78,19 @@ func ResetStats() {
|
||||
//
|
||||
// TODO outdated fields ?
|
||||
type Stats struct {
|
||||
Clusters int
|
||||
MasterConns int
|
||||
SlaveConns int
|
||||
SentOps int
|
||||
ReceivedOps int
|
||||
ReceivedDocs int
|
||||
SocketsAlive int
|
||||
SocketsInUse int
|
||||
SocketRefs int
|
||||
Clusters int
|
||||
MasterConns int
|
||||
SlaveConns int
|
||||
SentOps int
|
||||
ReceivedOps int
|
||||
ReceivedDocs int
|
||||
SocketsAlive int
|
||||
SocketsInUse int
|
||||
SocketRefs int
|
||||
TimesSocketAcquired int
|
||||
TimesWaitedForPool int
|
||||
TotalPoolWaitTime time.Duration
|
||||
PoolTimeouts int
|
||||
}
|
||||
|
||||
func (stats *Stats) cluster(delta int) {
|
||||
@@ -155,3 +160,25 @@ func (stats *Stats) socketRefs(delta int) {
|
||||
statsMutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (stats *Stats) noticeSocketAcquisition(waitTime time.Duration) {
|
||||
if stats != nil {
|
||||
statsMutex.Lock()
|
||||
stats.TimesSocketAcquired++
|
||||
stats.TotalPoolWaitTime += waitTime
|
||||
if waitTime > 0 {
|
||||
stats.TimesWaitedForPool++
|
||||
}
|
||||
statsMutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (stats *Stats) noticePoolTimeout(waitTime time.Duration) {
|
||||
if stats != nil {
|
||||
statsMutex.Lock()
|
||||
stats.TimesWaitedForPool++
|
||||
stats.PoolTimeouts++
|
||||
stats.TotalPoolWaitTime += waitTime
|
||||
statsMutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,6 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
@@ -13,4 +12,5 @@ script:
|
||||
- go get github.com/smartystreets/goconvey
|
||||
- mkdir -p $HOME/gopath/src/gopkg.in
|
||||
- ln -s $HOME/gopath/src/github.com/go-ini/ini $HOME/gopath/src/gopkg.in/ini.v1
|
||||
- cd $HOME/gopath/src/gopkg.in/ini.v1
|
||||
- go test -v -cover -race
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
INI [](https://travis-ci.org/go-ini/ini) [](https://sourcegraph.com/github.com/go-ini/ini?badge)
|
||||
INI [](https://travis-ci.org/go-ini/ini) [](https://sourcegraph.com/github.com/go-ini/ini)
|
||||
===
|
||||
|
||||

|
||||
|
||||
+16
-2
@@ -1,3 +1,5 @@
|
||||
// +build go1.6
|
||||
|
||||
// Copyright 2014 Unknwon
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
@@ -32,7 +34,7 @@ const (
|
||||
|
||||
// Maximum allowed depth when recursively substituing variable names.
|
||||
_DEPTH_VALUES = 99
|
||||
_VERSION = "1.35.0"
|
||||
_VERSION = "1.38.1"
|
||||
)
|
||||
|
||||
// Version returns current package version literal.
|
||||
@@ -132,6 +134,8 @@ type LoadOptions struct {
|
||||
IgnoreContinuation bool
|
||||
// IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value.
|
||||
IgnoreInlineComment bool
|
||||
// SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs.
|
||||
SkipUnrecognizableLines bool
|
||||
// AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
|
||||
// This type of keys are mostly used in my.cnf.
|
||||
AllowBooleanKeys bool
|
||||
@@ -140,6 +144,16 @@ type LoadOptions struct {
|
||||
// AllowNestedValues indicates whether to allow AWS-like nested values.
|
||||
// Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values
|
||||
AllowNestedValues bool
|
||||
// AllowPythonMultilineValues indicates whether to allow Python-like multi-line values.
|
||||
// Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure
|
||||
// Relevant quote: Values can also span multiple lines, as long as they are indented deeper
|
||||
// than the first line of the value.
|
||||
AllowPythonMultilineValues bool
|
||||
// SpaceBeforeInlineComment indicates whether to allow comment symbols (\# and \;) inside value.
|
||||
// Docs: https://docs.python.org/2/library/configparser.html
|
||||
// Quote: Comments may appear on their own in an otherwise empty line, or may be entered in lines holding values or section names.
|
||||
// In the latter case, they need to be preceded by a whitespace character to be recognized as a comment.
|
||||
SpaceBeforeInlineComment bool
|
||||
// UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format
|
||||
// when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value"
|
||||
UnescapeValueDoubleQuotes bool
|
||||
@@ -147,7 +161,7 @@ type LoadOptions struct {
|
||||
// when value is NOT surrounded by any quotes.
|
||||
// Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all.
|
||||
UnescapeValueCommentSymbols bool
|
||||
// Some INI formats allow group blocks that store a block of raw content that doesn't otherwise
|
||||
// UnparseableSections stores a list of blocks that are allowed with raw content which do not otherwise
|
||||
// conform to key/value pairs. Specify the names of those blocks here.
|
||||
UnparseableSections []string
|
||||
}
|
||||
|
||||
+114
-21
@@ -19,11 +19,14 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var pythonMultiline = regexp.MustCompile("^(\\s+)([^\n]+)")
|
||||
|
||||
type tokenType int
|
||||
|
||||
const (
|
||||
@@ -194,7 +197,8 @@ func hasSurroundedQuote(in string, quote byte) bool {
|
||||
}
|
||||
|
||||
func (p *parser) readValue(in []byte,
|
||||
ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols bool) (string, error) {
|
||||
parserBufferSize int,
|
||||
ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols, allowPythonMultilines, spaceBeforeInlineComment bool) (string, error) {
|
||||
|
||||
line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
|
||||
if len(line) == 0 {
|
||||
@@ -224,21 +228,34 @@ func (p *parser) readValue(in []byte,
|
||||
return line[startIdx : pos+startIdx], nil
|
||||
}
|
||||
|
||||
lastChar := line[len(line)-1]
|
||||
// Won't be able to reach here if value only contains whitespace
|
||||
line = strings.TrimSpace(line)
|
||||
trimmedLastChar := line[len(line)-1]
|
||||
|
||||
// Check continuation lines when desired
|
||||
if !ignoreContinuation && line[len(line)-1] == '\\' {
|
||||
if !ignoreContinuation && trimmedLastChar == '\\' {
|
||||
return p.readContinuationLines(line[:len(line)-1])
|
||||
}
|
||||
|
||||
// Check if ignore inline comment
|
||||
if !ignoreInlineComment {
|
||||
i := strings.IndexAny(line, "#;")
|
||||
var i int
|
||||
if spaceBeforeInlineComment {
|
||||
i = strings.Index(line, " #")
|
||||
if i == -1 {
|
||||
i = strings.Index(line, " ;")
|
||||
}
|
||||
|
||||
} else {
|
||||
i = strings.IndexAny(line, "#;")
|
||||
}
|
||||
|
||||
if i > -1 {
|
||||
p.comment.WriteString(line[i:])
|
||||
line = strings.TrimSpace(line[:i])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Trim single and double quotes
|
||||
@@ -252,7 +269,50 @@ func (p *parser) readValue(in []byte,
|
||||
if strings.Contains(line, `\#`) {
|
||||
line = strings.Replace(line, `\#`, "#", -1)
|
||||
}
|
||||
} else if allowPythonMultilines && lastChar == '\n' {
|
||||
parserBufferPeekResult, _ := p.buf.Peek(parserBufferSize)
|
||||
peekBuffer := bytes.NewBuffer(parserBufferPeekResult)
|
||||
|
||||
identSize := -1
|
||||
val := line
|
||||
|
||||
for {
|
||||
peekData, peekErr := peekBuffer.ReadBytes('\n')
|
||||
if peekErr != nil {
|
||||
if peekErr == io.EOF {
|
||||
return val, nil
|
||||
}
|
||||
return "", peekErr
|
||||
}
|
||||
|
||||
peekMatches := pythonMultiline.FindStringSubmatch(string(peekData))
|
||||
if len(peekMatches) != 3 {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
currentIdentSize := len(peekMatches[1])
|
||||
// NOTE: Return if not a python-ini multi-line value.
|
||||
if currentIdentSize < 0 {
|
||||
return val, nil
|
||||
}
|
||||
identSize = currentIdentSize
|
||||
|
||||
// NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer.
|
||||
_, err := p.readUntil('\n')
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
val += fmt.Sprintf("\n%s", peekMatches[2])
|
||||
}
|
||||
|
||||
// NOTE: If it was a Python multi-line value,
|
||||
// return the appended value.
|
||||
if identSize > 0 {
|
||||
return val, nil
|
||||
}
|
||||
}
|
||||
|
||||
return line, nil
|
||||
}
|
||||
|
||||
@@ -276,6 +336,28 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
|
||||
var line []byte
|
||||
var inUnparseableSection bool
|
||||
|
||||
// NOTE: Iterate and increase `currentPeekSize` until
|
||||
// the size of the parser buffer is found.
|
||||
// TODO(unknwon): When Golang 1.10 is the lowest version supported, replace with `parserBufferSize := p.buf.Size()`.
|
||||
parserBufferSize := 0
|
||||
// NOTE: Peek 1kb at a time.
|
||||
currentPeekSize := 1024
|
||||
|
||||
if f.options.AllowPythonMultilineValues {
|
||||
for {
|
||||
peekBytes, _ := p.buf.Peek(currentPeekSize)
|
||||
peekBytesLength := len(peekBytes)
|
||||
|
||||
if parserBufferSize >= peekBytesLength {
|
||||
break
|
||||
}
|
||||
|
||||
currentPeekSize *= 2
|
||||
parserBufferSize = peekBytesLength
|
||||
}
|
||||
}
|
||||
|
||||
for !p.isEOF {
|
||||
line, err = p.readUntil('\n')
|
||||
if err != nil {
|
||||
@@ -307,8 +389,7 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
// Section
|
||||
if line[0] == '[' {
|
||||
// Read to the next ']' (TODO: support quoted strings)
|
||||
// TODO(unknwon): use LastIndexByte when stop supporting Go1.4
|
||||
closeIdx := bytes.LastIndex(line, []byte("]"))
|
||||
closeIdx := bytes.LastIndexByte(line, ']')
|
||||
if closeIdx == -1 {
|
||||
return fmt.Errorf("unclosed section: %s", line)
|
||||
}
|
||||
@@ -350,22 +431,31 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
kname, offset, err := readKeyName(line)
|
||||
if err != nil {
|
||||
// Treat as boolean key when desired, and whole line is key name.
|
||||
if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys {
|
||||
kname, err := p.readValue(line,
|
||||
f.options.IgnoreContinuation,
|
||||
f.options.IgnoreInlineComment,
|
||||
f.options.UnescapeValueDoubleQuotes,
|
||||
f.options.UnescapeValueCommentSymbols)
|
||||
if err != nil {
|
||||
return err
|
||||
if IsErrDelimiterNotFound(err) {
|
||||
switch {
|
||||
case f.options.AllowBooleanKeys:
|
||||
kname, err := p.readValue(line,
|
||||
parserBufferSize,
|
||||
f.options.IgnoreContinuation,
|
||||
f.options.IgnoreInlineComment,
|
||||
f.options.UnescapeValueDoubleQuotes,
|
||||
f.options.UnescapeValueCommentSymbols,
|
||||
f.options.AllowPythonMultilineValues,
|
||||
f.options.SpaceBeforeInlineComment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, err := section.NewBooleanKey(kname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key.Comment = strings.TrimSpace(p.comment.String())
|
||||
p.comment.Reset()
|
||||
continue
|
||||
|
||||
case f.options.SkipUnrecognizableLines:
|
||||
continue
|
||||
}
|
||||
key, err := section.NewBooleanKey(kname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key.Comment = strings.TrimSpace(p.comment.String())
|
||||
p.comment.Reset()
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -379,10 +469,13 @@ func (f *File) parse(reader io.Reader) (err error) {
|
||||
}
|
||||
|
||||
value, err := p.readValue(line[offset:],
|
||||
parserBufferSize,
|
||||
f.options.IgnoreContinuation,
|
||||
f.options.IgnoreInlineComment,
|
||||
f.options.UnescapeValueDoubleQuotes,
|
||||
f.options.UnescapeValueCommentSymbols)
|
||||
f.options.UnescapeValueCommentSymbols,
|
||||
f.options.AllowPythonMultilineValues,
|
||||
f.options.SpaceBeforeInlineComment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+1
@@ -82,6 +82,7 @@ func (s *Section) NewKey(name, val string) (*Key, error) {
|
||||
}
|
||||
} else {
|
||||
s.keys[name].value = val
|
||||
s.keysHash[name] = val
|
||||
}
|
||||
return s.keys[name], nil
|
||||
}
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
Icon?
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
.idea
|
||||
|
||||
+101
-7
@@ -1,13 +1,107 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- tip
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- master
|
||||
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
before_script:
|
||||
- echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB" | sudo tee -a /etc/mysql/my.cnf
|
||||
- sudo service mysql restart
|
||||
- .travis/wait_mysql.sh
|
||||
- mysql -e 'create database gotest;'
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: DB=MYSQL8
|
||||
sudo: required
|
||||
dist: trusty
|
||||
go: 1.10.x
|
||||
services:
|
||||
- docker
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
- docker pull mysql:8.0
|
||||
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
|
||||
mysql:8.0 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
|
||||
- cp .travis/docker.cnf ~/.my.cnf
|
||||
- .travis/wait_mysql.sh
|
||||
before_script:
|
||||
- export MYSQL_TEST_USER=gotest
|
||||
- export MYSQL_TEST_PASS=secret
|
||||
- export MYSQL_TEST_ADDR=127.0.0.1:3307
|
||||
- export MYSQL_TEST_CONCURRENT=1
|
||||
|
||||
- env: DB=MYSQL57
|
||||
sudo: required
|
||||
dist: trusty
|
||||
go: 1.10.x
|
||||
services:
|
||||
- docker
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
- docker pull mysql:5.7
|
||||
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
|
||||
mysql:5.7 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
|
||||
- cp .travis/docker.cnf ~/.my.cnf
|
||||
- .travis/wait_mysql.sh
|
||||
before_script:
|
||||
- export MYSQL_TEST_USER=gotest
|
||||
- export MYSQL_TEST_PASS=secret
|
||||
- export MYSQL_TEST_ADDR=127.0.0.1:3307
|
||||
- export MYSQL_TEST_CONCURRENT=1
|
||||
|
||||
- env: DB=MARIA55
|
||||
sudo: required
|
||||
dist: trusty
|
||||
go: 1.10.x
|
||||
services:
|
||||
- docker
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
- docker pull mariadb:5.5
|
||||
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
|
||||
mariadb:5.5 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
|
||||
- cp .travis/docker.cnf ~/.my.cnf
|
||||
- .travis/wait_mysql.sh
|
||||
before_script:
|
||||
- export MYSQL_TEST_USER=gotest
|
||||
- export MYSQL_TEST_PASS=secret
|
||||
- export MYSQL_TEST_ADDR=127.0.0.1:3307
|
||||
- export MYSQL_TEST_CONCURRENT=1
|
||||
|
||||
- env: DB=MARIA10_1
|
||||
sudo: required
|
||||
dist: trusty
|
||||
go: 1.10.x
|
||||
services:
|
||||
- docker
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
- docker pull mariadb:10.1
|
||||
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
|
||||
mariadb:10.1 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB --local-infile=1
|
||||
- cp .travis/docker.cnf ~/.my.cnf
|
||||
- .travis/wait_mysql.sh
|
||||
before_script:
|
||||
- export MYSQL_TEST_USER=gotest
|
||||
- export MYSQL_TEST_PASS=secret
|
||||
- export MYSQL_TEST_ADDR=127.0.0.1:3307
|
||||
- export MYSQL_TEST_CONCURRENT=1
|
||||
|
||||
script:
|
||||
- go test -v -covermode=count -coverprofile=coverage.out
|
||||
- go vet ./...
|
||||
- .travis/gofmt.sh
|
||||
after_script:
|
||||
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci
|
||||
|
||||
+33
@@ -12,35 +12,63 @@
|
||||
# Individual Persons
|
||||
|
||||
Aaron Hopkins <go-sql-driver at die.net>
|
||||
Achille Roussel <achille.roussel at gmail.com>
|
||||
Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
|
||||
Andrew Reid <andrew.reid at tixtrack.com>
|
||||
Arne Hormann <arnehormann at gmail.com>
|
||||
Asta Xie <xiemengjun at gmail.com>
|
||||
Bulat Gaifullin <gaifullinbf at gmail.com>
|
||||
Carlos Nieto <jose.carlos at menteslibres.net>
|
||||
Chris Moos <chris at tech9computers.com>
|
||||
Craig Wilson <craiggwilson at gmail.com>
|
||||
Daniel Montoya <dsmontoyam at gmail.com>
|
||||
Daniel Nichter <nil at codenode.com>
|
||||
Daniël van Eeden <git at myname.nl>
|
||||
Dave Protasowski <dprotaso at gmail.com>
|
||||
DisposaBoy <disposaboy at dby.me>
|
||||
Egor Smolyakov <egorsmkv at gmail.com>
|
||||
Evan Shaw <evan at vendhq.com>
|
||||
Frederick Mayle <frederickmayle at gmail.com>
|
||||
Gustavo Kristic <gkristic at gmail.com>
|
||||
Hajime Nakagami <nakagami at gmail.com>
|
||||
Hanno Braun <mail at hannobraun.com>
|
||||
Henri Yandell <flamefew at gmail.com>
|
||||
Hirotaka Yamamoto <ymmt2005 at gmail.com>
|
||||
ICHINOSE Shogo <shogo82148 at gmail.com>
|
||||
INADA Naoki <songofacandy at gmail.com>
|
||||
Jacek Szwec <szwec.jacek at gmail.com>
|
||||
James Harr <james.harr at gmail.com>
|
||||
Jeff Hodges <jeff at somethingsimilar.com>
|
||||
Jeffrey Charles <jeffreycharles at gmail.com>
|
||||
Jian Zhen <zhenjl at gmail.com>
|
||||
Joshua Prunier <joshua.prunier at gmail.com>
|
||||
Julien Lefevre <julien.lefevr at gmail.com>
|
||||
Julien Schmidt <go-sql-driver at julienschmidt.com>
|
||||
Justin Li <jli at j-li.net>
|
||||
Justin Nuß <nuss.justin at gmail.com>
|
||||
Kamil Dziedzic <kamil at klecza.pl>
|
||||
Kevin Malachowski <kevin at chowski.com>
|
||||
Kieron Woodhouse <kieron.woodhouse at infosum.com>
|
||||
Lennart Rudolph <lrudolph at hmc.edu>
|
||||
Leonardo YongUk Kim <dalinaum at gmail.com>
|
||||
Linh Tran Tuan <linhduonggnu at gmail.com>
|
||||
Lion Yang <lion at aosc.xyz>
|
||||
Luca Looz <luca.looz92 at gmail.com>
|
||||
Lucas Liu <extrafliu at gmail.com>
|
||||
Luke Scott <luke at webconnex.com>
|
||||
Maciej Zimnoch <maciej.zimnoch at codilime.com>
|
||||
Michael Woolnough <michael.woolnough at gmail.com>
|
||||
Nicola Peduzzi <thenikso at gmail.com>
|
||||
Olivier Mengué <dolmen at cpan.org>
|
||||
oscarzhao <oscarzhaosl at gmail.com>
|
||||
Paul Bonser <misterpib at gmail.com>
|
||||
Peter Schultz <peter.schultz at classmarkets.com>
|
||||
Rebecca Chin <rchin at pivotal.io>
|
||||
Reed Allman <rdallman10 at gmail.com>
|
||||
Richard Wilkes <wilkes at me.com>
|
||||
Robert Russell <robert at rrbrussell.com>
|
||||
Runrioter Wung <runrioter at gmail.com>
|
||||
Shuode Li <elemount at qq.com>
|
||||
Soroush Pour <me at soroushjp.com>
|
||||
Stan Putrya <root.vagner at gmail.com>
|
||||
Stanley Gunawan <gunawan.stanley at gmail.com>
|
||||
@@ -52,5 +80,10 @@ Zhenye Xie <xiezhenye at gmail.com>
|
||||
# Organizations
|
||||
|
||||
Barracuda Networks, Inc.
|
||||
Counting Ltd.
|
||||
Google Inc.
|
||||
InfoSum Ltd.
|
||||
Keybase Inc.
|
||||
Percona LLC
|
||||
Pivotal Inc.
|
||||
Stripe Inc.
|
||||
|
||||
+48
@@ -1,3 +1,51 @@
|
||||
## Version 1.4 (2018-06-03)
|
||||
|
||||
Changes:
|
||||
|
||||
- Documentation fixes (#530, #535, #567)
|
||||
- Refactoring (#575, #579, #580, #581, #603, #615, #704)
|
||||
- Cache column names (#444)
|
||||
- Sort the DSN parameters in DSNs generated from a config (#637)
|
||||
- Allow native password authentication by default (#644)
|
||||
- Use the default port if it is missing in the DSN (#668)
|
||||
- Removed the `strict` mode (#676)
|
||||
- Do not query `max_allowed_packet` by default (#680)
|
||||
- Dropped support Go 1.6 and lower (#696)
|
||||
- Updated `ConvertValue()` to match the database/sql/driver implementation (#760)
|
||||
- Document the usage of `0000-00-00T00:00:00` as the time.Time zero value (#783)
|
||||
- Improved the compatibility of the authentication system (#807)
|
||||
|
||||
New Features:
|
||||
|
||||
- Multi-Results support (#537)
|
||||
- `rejectReadOnly` DSN option (#604)
|
||||
- `context.Context` support (#608, #612, #627, #761)
|
||||
- Transaction isolation level support (#619, #744)
|
||||
- Read-Only transactions support (#618, #634)
|
||||
- `NewConfig` function which initializes a config with default values (#679)
|
||||
- Implemented the `ColumnType` interfaces (#667, #724)
|
||||
- Support for custom string types in `ConvertValue` (#623)
|
||||
- Implemented `NamedValueChecker`, improving support for uint64 with high bit set (#690, #709, #710)
|
||||
- `caching_sha2_password` authentication plugin support (#794, #800, #801, #802)
|
||||
- Implemented `driver.SessionResetter` (#779)
|
||||
- `sha256_password` authentication plugin support (#808)
|
||||
|
||||
Bugfixes:
|
||||
|
||||
- Use the DSN hostname as TLS default ServerName if `tls=true` (#564, #718)
|
||||
- Fixed LOAD LOCAL DATA INFILE for empty files (#590)
|
||||
- Removed columns definition cache since it sometimes cached invalid data (#592)
|
||||
- Don't mutate registered TLS configs (#600)
|
||||
- Make RegisterTLSConfig concurrency-safe (#613)
|
||||
- Handle missing auth data in the handshake packet correctly (#646)
|
||||
- Do not retry queries when data was written to avoid data corruption (#302, #736)
|
||||
- Cache the connection pointer for error handling before invalidating it (#678)
|
||||
- Fixed imports for appengine/cloudsql (#700)
|
||||
- Fix sending STMT_LONG_DATA for 0 byte data (#734)
|
||||
- Set correct capacity for []bytes read from length-encoded strings (#766)
|
||||
- Make RegisterDial concurrency-safe (#773)
|
||||
|
||||
|
||||
## Version 1.3 (2016-12-01)
|
||||
|
||||
Changes:
|
||||
|
||||
+87
-40
@@ -1,6 +1,6 @@
|
||||
# Go-MySQL-Driver
|
||||
|
||||
A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) package
|
||||
A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) package
|
||||
|
||||

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

|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"appengine/cloudsql"
|
||||
"google.golang.org/appengine/cloudsql"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2018 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/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// server pub keys registry
|
||||
var (
|
||||
serverPubKeyLock sync.RWMutex
|
||||
serverPubKeyRegistry map[string]*rsa.PublicKey
|
||||
)
|
||||
|
||||
// RegisterServerPubKey registers a server RSA public key which can be used to
|
||||
// send data in a secure manner to the server without receiving the public key
|
||||
// in a potentially insecure way from the server first.
|
||||
// Registered keys can afterwards be used adding serverPubKey=<name> to the DSN.
|
||||
//
|
||||
// Note: The provided rsa.PublicKey instance is exclusively owned by the driver
|
||||
// after registering it and may not be modified.
|
||||
//
|
||||
// data, err := ioutil.ReadFile("mykey.pem")
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// block, _ := pem.Decode(data)
|
||||
// if block == nil || block.Type != "PUBLIC KEY" {
|
||||
// log.Fatal("failed to decode PEM block containing public key")
|
||||
// }
|
||||
//
|
||||
// pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// if rsaPubKey, ok := pub.(*rsa.PublicKey); ok {
|
||||
// mysql.RegisterServerPubKey("mykey", rsaPubKey)
|
||||
// } else {
|
||||
// log.Fatal("not a RSA public key")
|
||||
// }
|
||||
//
|
||||
func RegisterServerPubKey(name string, pubKey *rsa.PublicKey) {
|
||||
serverPubKeyLock.Lock()
|
||||
if serverPubKeyRegistry == nil {
|
||||
serverPubKeyRegistry = make(map[string]*rsa.PublicKey)
|
||||
}
|
||||
|
||||
serverPubKeyRegistry[name] = pubKey
|
||||
serverPubKeyLock.Unlock()
|
||||
}
|
||||
|
||||
// DeregisterServerPubKey removes the public key registered with the given name.
|
||||
func DeregisterServerPubKey(name string) {
|
||||
serverPubKeyLock.Lock()
|
||||
if serverPubKeyRegistry != nil {
|
||||
delete(serverPubKeyRegistry, name)
|
||||
}
|
||||
serverPubKeyLock.Unlock()
|
||||
}
|
||||
|
||||
func getServerPubKey(name string) (pubKey *rsa.PublicKey) {
|
||||
serverPubKeyLock.RLock()
|
||||
if v, ok := serverPubKeyRegistry[name]; ok {
|
||||
pubKey = v
|
||||
}
|
||||
serverPubKeyLock.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Hash password using pre 4.1 (old password) method
|
||||
// https://github.com/atcurtis/mariadb/blob/master/mysys/my_rnd.c
|
||||
type myRnd struct {
|
||||
seed1, seed2 uint32
|
||||
}
|
||||
|
||||
const myRndMaxVal = 0x3FFFFFFF
|
||||
|
||||
// Pseudo random number generator
|
||||
func newMyRnd(seed1, seed2 uint32) *myRnd {
|
||||
return &myRnd{
|
||||
seed1: seed1 % myRndMaxVal,
|
||||
seed2: seed2 % myRndMaxVal,
|
||||
}
|
||||
}
|
||||
|
||||
// Tested to be equivalent to MariaDB's floating point variant
|
||||
// http://play.golang.org/p/QHvhd4qved
|
||||
// http://play.golang.org/p/RG0q4ElWDx
|
||||
func (r *myRnd) NextByte() byte {
|
||||
r.seed1 = (r.seed1*3 + r.seed2) % myRndMaxVal
|
||||
r.seed2 = (r.seed1 + r.seed2 + 33) % myRndMaxVal
|
||||
|
||||
return byte(uint64(r.seed1) * 31 / myRndMaxVal)
|
||||
}
|
||||
|
||||
// Generate binary hash from byte string using insecure pre 4.1 method
|
||||
func pwHash(password []byte) (result [2]uint32) {
|
||||
var add uint32 = 7
|
||||
var tmp uint32
|
||||
|
||||
result[0] = 1345345333
|
||||
result[1] = 0x12345671
|
||||
|
||||
for _, c := range password {
|
||||
// skip spaces and tabs in password
|
||||
if c == ' ' || c == '\t' {
|
||||
continue
|
||||
}
|
||||
|
||||
tmp = uint32(c)
|
||||
result[0] ^= (((result[0] & 63) + add) * tmp) + (result[0] << 8)
|
||||
result[1] += (result[1] << 8) ^ result[0]
|
||||
add += tmp
|
||||
}
|
||||
|
||||
// Remove sign bit (1<<31)-1)
|
||||
result[0] &= 0x7FFFFFFF
|
||||
result[1] &= 0x7FFFFFFF
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Hash password using insecure pre 4.1 method
|
||||
func scrambleOldPassword(scramble []byte, password string) []byte {
|
||||
if len(password) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
scramble = scramble[:8]
|
||||
|
||||
hashPw := pwHash([]byte(password))
|
||||
hashSc := pwHash(scramble)
|
||||
|
||||
r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1])
|
||||
|
||||
var out [8]byte
|
||||
for i := range out {
|
||||
out[i] = r.NextByte() + 64
|
||||
}
|
||||
|
||||
mask := r.NextByte()
|
||||
for i := range out {
|
||||
out[i] ^= mask
|
||||
}
|
||||
|
||||
return out[:]
|
||||
}
|
||||
|
||||
// Hash password using 4.1+ method (SHA1)
|
||||
func scramblePassword(scramble []byte, password string) []byte {
|
||||
if len(password) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// stage1Hash = SHA1(password)
|
||||
crypt := sha1.New()
|
||||
crypt.Write([]byte(password))
|
||||
stage1 := crypt.Sum(nil)
|
||||
|
||||
// scrambleHash = SHA1(scramble + SHA1(stage1Hash))
|
||||
// inner Hash
|
||||
crypt.Reset()
|
||||
crypt.Write(stage1)
|
||||
hash := crypt.Sum(nil)
|
||||
|
||||
// outer Hash
|
||||
crypt.Reset()
|
||||
crypt.Write(scramble)
|
||||
crypt.Write(hash)
|
||||
scramble = crypt.Sum(nil)
|
||||
|
||||
// token = scrambleHash XOR stage1Hash
|
||||
for i := range scramble {
|
||||
scramble[i] ^= stage1[i]
|
||||
}
|
||||
return scramble
|
||||
}
|
||||
|
||||
// Hash password using MySQL 8+ method (SHA256)
|
||||
func scrambleSHA256Password(scramble []byte, password string) []byte {
|
||||
if len(password) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// XOR(SHA256(password), SHA256(SHA256(SHA256(password)), scramble))
|
||||
|
||||
crypt := sha256.New()
|
||||
crypt.Write([]byte(password))
|
||||
message1 := crypt.Sum(nil)
|
||||
|
||||
crypt.Reset()
|
||||
crypt.Write(message1)
|
||||
message1Hash := crypt.Sum(nil)
|
||||
|
||||
crypt.Reset()
|
||||
crypt.Write(message1Hash)
|
||||
crypt.Write(scramble)
|
||||
message2 := crypt.Sum(nil)
|
||||
|
||||
for i := range message1 {
|
||||
message1[i] ^= message2[i]
|
||||
}
|
||||
|
||||
return message1
|
||||
}
|
||||
|
||||
func encryptPassword(password string, seed []byte, pub *rsa.PublicKey) ([]byte, error) {
|
||||
plain := make([]byte, len(password)+1)
|
||||
copy(plain, password)
|
||||
for i := range plain {
|
||||
j := i % len(seed)
|
||||
plain[i] ^= seed[j]
|
||||
}
|
||||
sha1 := sha1.New()
|
||||
return rsa.EncryptOAEP(sha1, rand.Reader, pub, plain, nil)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) sendEncryptedPassword(seed []byte, pub *rsa.PublicKey) error {
|
||||
enc, err := encryptPassword(mc.cfg.Passwd, seed, pub)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mc.writeAuthSwitchPacket(enc, false)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, bool, error) {
|
||||
switch plugin {
|
||||
case "caching_sha2_password":
|
||||
authResp := scrambleSHA256Password(authData, mc.cfg.Passwd)
|
||||
return authResp, (authResp == nil), nil
|
||||
|
||||
case "mysql_old_password":
|
||||
if !mc.cfg.AllowOldPasswords {
|
||||
return nil, false, ErrOldPassword
|
||||
}
|
||||
// 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
|
||||
authResp := scrambleOldPassword(authData[:8], mc.cfg.Passwd)
|
||||
return authResp, true, nil
|
||||
|
||||
case "mysql_clear_password":
|
||||
if !mc.cfg.AllowCleartextPasswords {
|
||||
return nil, false, ErrCleartextPassword
|
||||
}
|
||||
// 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
|
||||
return []byte(mc.cfg.Passwd), true, nil
|
||||
|
||||
case "mysql_native_password":
|
||||
if !mc.cfg.AllowNativePasswords {
|
||||
return nil, false, ErrNativePassword
|
||||
}
|
||||
// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
|
||||
// Native password authentication only need and will need 20-byte challenge.
|
||||
authResp := scramblePassword(authData[:20], mc.cfg.Passwd)
|
||||
return authResp, false, nil
|
||||
|
||||
case "sha256_password":
|
||||
if len(mc.cfg.Passwd) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
|
||||
// write cleartext auth packet
|
||||
return []byte(mc.cfg.Passwd), true, nil
|
||||
}
|
||||
|
||||
pubKey := mc.cfg.pubKey
|
||||
if pubKey == nil {
|
||||
// request public key from server
|
||||
return []byte{1}, false, nil
|
||||
}
|
||||
|
||||
// encrypted password
|
||||
enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey)
|
||||
return enc, false, err
|
||||
|
||||
default:
|
||||
errLog.Print("unknown auth plugin:", plugin)
|
||||
return nil, false, ErrUnknownPlugin
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
|
||||
// Read Result Packet
|
||||
authData, newPlugin, err := mc.readAuthResult()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// handle auth plugin switch, if requested
|
||||
if newPlugin != "" {
|
||||
// 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 authData == nil {
|
||||
authData = oldAuthData
|
||||
} else {
|
||||
// copy data from read buffer to owned slice
|
||||
copy(oldAuthData, authData)
|
||||
}
|
||||
|
||||
plugin = newPlugin
|
||||
|
||||
authResp, addNUL, err := mc.auth(authData, plugin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = mc.writeAuthSwitchPacket(authResp, addNUL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read Result Packet
|
||||
authData, newPlugin, err = mc.readAuthResult()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Do not allow to change the auth plugin more than once
|
||||
if newPlugin != "" {
|
||||
return ErrMalformPkt
|
||||
}
|
||||
}
|
||||
|
||||
switch plugin {
|
||||
|
||||
// https://insidemysql.com/preparing-your-community-connector-for-mysql-8-part-2-sha256/
|
||||
case "caching_sha2_password":
|
||||
switch len(authData) {
|
||||
case 0:
|
||||
return nil // auth successful
|
||||
case 1:
|
||||
switch authData[0] {
|
||||
case cachingSha2PasswordFastAuthSuccess:
|
||||
if err = mc.readResultOK(); err == nil {
|
||||
return nil // auth successful
|
||||
}
|
||||
|
||||
case cachingSha2PasswordPerformFullAuthentication:
|
||||
if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
|
||||
// write cleartext auth packet
|
||||
err = mc.writeAuthSwitchPacket([]byte(mc.cfg.Passwd), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
pubKey := mc.cfg.pubKey
|
||||
if pubKey == nil {
|
||||
// request public key from server
|
||||
data := mc.buf.takeSmallBuffer(4 + 1)
|
||||
data[4] = cachingSha2PasswordRequestPublicKey
|
||||
mc.writePacket(data)
|
||||
|
||||
// parse public key
|
||||
data, err := mc.readPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(data[1:])
|
||||
pkix, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pubKey = pkix.(*rsa.PublicKey)
|
||||
}
|
||||
|
||||
// send encrypted password
|
||||
err = mc.sendEncryptedPassword(oldAuthData, pubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return mc.readResultOK()
|
||||
|
||||
default:
|
||||
return ErrMalformPkt
|
||||
}
|
||||
default:
|
||||
return ErrMalformPkt
|
||||
}
|
||||
|
||||
case "sha256_password":
|
||||
switch len(authData) {
|
||||
case 0:
|
||||
return nil // auth successful
|
||||
default:
|
||||
block, _ := pem.Decode(authData)
|
||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// send encrypted password
|
||||
err = mc.sendEncryptedPassword(oldAuthData, pub.(*rsa.PublicKey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mc.readResultOK()
|
||||
}
|
||||
|
||||
default:
|
||||
return nil // auth successful
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
+6
-6
@@ -130,18 +130,18 @@ func (b *buffer) takeBuffer(length int) []byte {
|
||||
// smaller than defaultBufSize
|
||||
// Only one buffer (total) can be used at a time.
|
||||
func (b *buffer) takeSmallBuffer(length int) []byte {
|
||||
if b.length == 0 {
|
||||
return b.buf[:length]
|
||||
if b.length > 0 {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
return b.buf[:length]
|
||||
}
|
||||
|
||||
// takeCompleteBuffer returns the complete existing buffer.
|
||||
// This can be used if the necessary buffer size is unknown.
|
||||
// Only one buffer (total) can be used at a time.
|
||||
func (b *buffer) takeCompleteBuffer() []byte {
|
||||
if b.length == 0 {
|
||||
return b.buf
|
||||
if b.length > 0 {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
return b.buf
|
||||
}
|
||||
|
||||
+1
@@ -9,6 +9,7 @@
|
||||
package mysql
|
||||
|
||||
const defaultCollation = "utf8_general_ci"
|
||||
const binaryCollation = "binary"
|
||||
|
||||
// A list of available collations mapped to the internal ID.
|
||||
// To update this map use the following MySQL query:
|
||||
|
||||
+118
-34
@@ -10,12 +10,23 @@ package mysql
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// a copy of context.Context for Go 1.7 and earlier
|
||||
type mysqlContext interface {
|
||||
Done() <-chan struct{}
|
||||
Err() error
|
||||
|
||||
// defined in context.Context, but not used in this driver:
|
||||
// Deadline() (deadline time.Time, ok bool)
|
||||
// Value(key interface{}) interface{}
|
||||
}
|
||||
|
||||
type mysqlConn struct {
|
||||
buf buffer
|
||||
netConn net.Conn
|
||||
@@ -29,7 +40,14 @@ type mysqlConn struct {
|
||||
status statusFlag
|
||||
sequence uint8
|
||||
parseTime bool
|
||||
strict bool
|
||||
|
||||
// for context support (Go 1.8+)
|
||||
watching bool
|
||||
watcher chan<- mysqlContext
|
||||
closech chan struct{}
|
||||
finished chan<- struct{}
|
||||
canceled atomicError // set non-nil if conn is canceled
|
||||
closed atomicBool // set when conn is closed, before closech is closed
|
||||
}
|
||||
|
||||
// Handles parameters set in DSN after the connection is established
|
||||
@@ -62,22 +80,41 @@ func (mc *mysqlConn) handleParams() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) markBadConn(err error) error {
|
||||
if mc == nil {
|
||||
return err
|
||||
}
|
||||
if err != errBadConnNoWrite {
|
||||
return err
|
||||
}
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Begin() (driver.Tx, error) {
|
||||
if mc.netConn == nil {
|
||||
return mc.begin(false)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
err := mc.exec("START TRANSACTION")
|
||||
var q string
|
||||
if readOnly {
|
||||
q = "START TRANSACTION READ ONLY"
|
||||
} else {
|
||||
q = "START TRANSACTION"
|
||||
}
|
||||
err := mc.exec(q)
|
||||
if err == nil {
|
||||
return &mysqlTx{mc}, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, mc.markBadConn(err)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Close() (err error) {
|
||||
// Makes Close idempotent
|
||||
if mc.netConn != nil {
|
||||
if !mc.closed.IsSet() {
|
||||
err = mc.writeCommandPacket(comQuit)
|
||||
}
|
||||
|
||||
@@ -91,26 +128,39 @@ func (mc *mysqlConn) Close() (err error) {
|
||||
// is called before auth or on auth failure because MySQL will have already
|
||||
// closed the network connection.
|
||||
func (mc *mysqlConn) cleanup() {
|
||||
// Makes cleanup idempotent
|
||||
if mc.netConn != nil {
|
||||
if err := mc.netConn.Close(); err != nil {
|
||||
errLog.Print(err)
|
||||
}
|
||||
mc.netConn = nil
|
||||
if !mc.closed.TrySet(true) {
|
||||
return
|
||||
}
|
||||
mc.cfg = nil
|
||||
mc.buf.nc = nil
|
||||
|
||||
// Makes cleanup idempotent
|
||||
close(mc.closech)
|
||||
if mc.netConn == nil {
|
||||
return
|
||||
}
|
||||
if err := mc.netConn.Close(); err != nil {
|
||||
errLog.Print(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) error() error {
|
||||
if mc.closed.IsSet() {
|
||||
if err := mc.canceled.Value(); err != nil {
|
||||
return err
|
||||
}
|
||||
return ErrInvalidConn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
|
||||
if mc.netConn == nil {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comStmtPrepare, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, mc.markBadConn(err)
|
||||
}
|
||||
|
||||
stmt := &mysqlStmt{
|
||||
@@ -144,7 +194,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
||||
if buf == nil {
|
||||
// can not take the buffer. Something must be wrong with the connection
|
||||
errLog.Print(ErrBusyBuffer)
|
||||
return "", driver.ErrBadConn
|
||||
return "", ErrInvalidConn
|
||||
}
|
||||
buf = buf[:0]
|
||||
argPos := 0
|
||||
@@ -257,7 +307,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
||||
if mc.netConn == nil {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
@@ -271,7 +321,6 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
|
||||
return nil, err
|
||||
}
|
||||
query = prepared
|
||||
args = nil
|
||||
}
|
||||
mc.affectedRows = 0
|
||||
mc.insertId = 0
|
||||
@@ -283,32 +332,43 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
|
||||
insertId: int64(mc.insertId),
|
||||
}, err
|
||||
}
|
||||
return nil, err
|
||||
return nil, mc.markBadConn(err)
|
||||
}
|
||||
|
||||
// Internal function to execute commands
|
||||
func (mc *mysqlConn) exec(query string) error {
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comQuery, query)
|
||||
if err != nil {
|
||||
return err
|
||||
if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
|
||||
return mc.markBadConn(err)
|
||||
}
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
if err == nil && resLen > 0 {
|
||||
if err = mc.readUntilEOF(); err != nil {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resLen > 0 {
|
||||
// columns
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = mc.readUntilEOF()
|
||||
// rows
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
return mc.discardResults()
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
||||
if mc.netConn == nil {
|
||||
return mc.query(query, args)
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
@@ -322,7 +382,6 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
|
||||
return nil, err
|
||||
}
|
||||
query = prepared
|
||||
args = nil
|
||||
}
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comQuery, query)
|
||||
@@ -335,15 +394,22 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
|
||||
rows.mc = mc
|
||||
|
||||
if resLen == 0 {
|
||||
// no columns, no more data
|
||||
return emptyRows{}, nil
|
||||
rows.rs.done = true
|
||||
|
||||
switch err := rows.NextResultSet(); err {
|
||||
case nil, io.EOF:
|
||||
return rows, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Columns
|
||||
rows.columns, err = mc.readColumns(resLen)
|
||||
rows.rs.columns, err = mc.readColumns(resLen)
|
||||
return rows, err
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
return nil, mc.markBadConn(err)
|
||||
}
|
||||
|
||||
// Gets the value of the given MySQL System Variable
|
||||
@@ -359,7 +425,7 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
||||
if err == nil {
|
||||
rows := new(textRows)
|
||||
rows.mc = mc
|
||||
rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
|
||||
rows.rs.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
|
||||
|
||||
if resLen > 0 {
|
||||
// Columns
|
||||
@@ -375,3 +441,21 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// finish is called when the query has canceled.
|
||||
func (mc *mysqlConn) cancel(err error) {
|
||||
mc.canceled.Set(err)
|
||||
mc.cleanup()
|
||||
}
|
||||
|
||||
// finish is called when the query has succeeded.
|
||||
func (mc *mysqlConn) finish() {
|
||||
if !mc.watching || mc.finished == nil {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case mc.finished <- struct{}{}:
|
||||
mc.watching = false
|
||||
case <-mc.closech:
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user