first commit
Этот коммит содержится в:
24
Godeps/_workspace/src/github.com/anachronistic/apns/.gitignore
сгенерированный
поставляемый
Обычный файл
24
Godeps/_workspace/src/github.com/anachronistic/apns/.gitignore
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,24 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
*.pem
|
||||
21
Godeps/_workspace/src/github.com/anachronistic/apns/LICENSE
сгенерированный
поставляемый
Обычный файл
21
Godeps/_workspace/src/github.com/anachronistic/apns/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Alan Harris
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
167
Godeps/_workspace/src/github.com/anachronistic/apns/client.go
сгенерированный
поставляемый
Обычный файл
167
Godeps/_workspace/src/github.com/anachronistic/apns/client.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,167 @@
|
||||
package apns
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ APNSClient = &Client{}
|
||||
|
||||
// APNSClient is an APNS client.
|
||||
type APNSClient interface {
|
||||
ConnectAndWrite(resp *PushNotificationResponse, payload []byte) (err error)
|
||||
Send(pn *PushNotification) (resp *PushNotificationResponse)
|
||||
}
|
||||
|
||||
// Client contains the fields necessary to communicate
|
||||
// with Apple, such as the gateway to use and your
|
||||
// certificate contents.
|
||||
//
|
||||
// You'll need to provide your own CertificateFile
|
||||
// and KeyFile to send notifications. Ideally, you'll
|
||||
// just set the CertificateFile and KeyFile fields to
|
||||
// a location on drive where the certs can be loaded,
|
||||
// but if you prefer you can use the CertificateBase64
|
||||
// and KeyBase64 fields to store the actual contents.
|
||||
type Client struct {
|
||||
Gateway string
|
||||
CertificateFile string
|
||||
CertificateBase64 string
|
||||
KeyFile string
|
||||
KeyBase64 string
|
||||
}
|
||||
|
||||
// BareClient can be used to set the contents of your
|
||||
// certificate and key blocks manually.
|
||||
func BareClient(gateway, certificateBase64, keyBase64 string) (c *Client) {
|
||||
c = new(Client)
|
||||
c.Gateway = gateway
|
||||
c.CertificateBase64 = certificateBase64
|
||||
c.KeyBase64 = keyBase64
|
||||
return
|
||||
}
|
||||
|
||||
// NewClient assumes you'll be passing in paths that
|
||||
// point to your certificate and key.
|
||||
func NewClient(gateway, certificateFile, keyFile string) (c *Client) {
|
||||
c = new(Client)
|
||||
c.Gateway = gateway
|
||||
c.CertificateFile = certificateFile
|
||||
c.KeyFile = keyFile
|
||||
return
|
||||
}
|
||||
|
||||
// Send connects to the APN service and sends your push notification.
|
||||
// Remember that if the submission is successful, Apple won't reply.
|
||||
func (client *Client) Send(pn *PushNotification) (resp *PushNotificationResponse) {
|
||||
resp = new(PushNotificationResponse)
|
||||
|
||||
payload, err := pn.ToBytes()
|
||||
if err != nil {
|
||||
resp.Success = false
|
||||
resp.Error = err
|
||||
return
|
||||
}
|
||||
|
||||
err = client.ConnectAndWrite(resp, payload)
|
||||
if err != nil {
|
||||
resp.Success = false
|
||||
resp.Error = err
|
||||
return
|
||||
}
|
||||
|
||||
resp.Success = true
|
||||
resp.Error = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ConnectAndWrite establishes the connection to Apple and handles the
|
||||
// transmission of your push notification, as well as waiting for a reply.
|
||||
//
|
||||
// In lieu of a timeout (which would be available in Go 1.1)
|
||||
// we use a timeout channel pattern instead. We start two goroutines,
|
||||
// one of which just sleeps for TimeoutSeconds seconds, while the other
|
||||
// waits for a response from the Apple servers.
|
||||
//
|
||||
// Whichever channel puts data on first is the "winner". As such, it's
|
||||
// possible to get a false positive if Apple takes a long time to respond.
|
||||
// It's probably not a deal-breaker, but something to be aware of.
|
||||
func (client *Client) ConnectAndWrite(resp *PushNotificationResponse, payload []byte) (err error) {
|
||||
var cert tls.Certificate
|
||||
|
||||
if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
|
||||
// The user did not specify raw block contents, so check the filesystem.
|
||||
cert, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
|
||||
} else {
|
||||
// The user provided the raw block contents, so use that.
|
||||
cert, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gatewayParts := strings.Split(client.Gateway, ":")
|
||||
conf := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
ServerName: gatewayParts[0],
|
||||
}
|
||||
|
||||
conn, err := net.Dial("tcp", client.Gateway)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
tlsConn := tls.Client(conn, conf)
|
||||
err = tlsConn.Handshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tlsConn.Close()
|
||||
|
||||
_, err = tlsConn.Write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create one channel that will serve to handle
|
||||
// timeouts when the notification succeeds.
|
||||
timeoutChannel := make(chan bool, 1)
|
||||
go func() {
|
||||
time.Sleep(time.Second * TimeoutSeconds)
|
||||
timeoutChannel <- true
|
||||
}()
|
||||
|
||||
// This channel will contain the binary response
|
||||
// from Apple in the event of a failure.
|
||||
responseChannel := make(chan []byte, 1)
|
||||
go func() {
|
||||
buffer := make([]byte, 6, 6)
|
||||
tlsConn.Read(buffer)
|
||||
responseChannel <- buffer
|
||||
}()
|
||||
|
||||
// First one back wins!
|
||||
// The data structure for an APN response is as follows:
|
||||
//
|
||||
// command -> 1 byte
|
||||
// status -> 1 byte
|
||||
// identifier -> 4 bytes
|
||||
//
|
||||
// The first byte will always be set to 8.
|
||||
select {
|
||||
case r := <-responseChannel:
|
||||
resp.Success = false
|
||||
resp.AppleResponse = ApplePushResponses[r[1]]
|
||||
err = errors.New(resp.AppleResponse)
|
||||
case <-timeoutChannel:
|
||||
resp.Success = true
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
21
Godeps/_workspace/src/github.com/anachronistic/apns/client_mock.go
сгенерированный
поставляемый
Обычный файл
21
Godeps/_workspace/src/github.com/anachronistic/apns/client_mock.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,21 @@
|
||||
package apns
|
||||
|
||||
import "github.com/stretchr/testify/mock"
|
||||
|
||||
type MockClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockClient) ConnectAndWrite(resp *PushNotificationResponse, payload []byte) (err error) {
|
||||
return m.Called(resp, payload).Error(0)
|
||||
}
|
||||
|
||||
func (m *MockClient) Send(pn *PushNotification) (resp *PushNotificationResponse) {
|
||||
r := m.Called(pn).Get(0)
|
||||
if r != nil {
|
||||
if r, ok := r.(*PushNotificationResponse); ok {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
24
Godeps/_workspace/src/github.com/anachronistic/apns/client_mock_test.go
сгенерированный
поставляемый
Обычный файл
24
Godeps/_workspace/src/github.com/anachronistic/apns/client_mock_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,24 @@
|
||||
package apns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMockClientConnectAndWrite(t *testing.T) {
|
||||
m := &MockClient{}
|
||||
m.On("ConnectAndWrite", (*PushNotificationResponse)(nil), []byte(nil)).Return(nil)
|
||||
assert.Nil(t, m.ConnectAndWrite(nil, nil))
|
||||
m.On("ConnectAndWrite", &PushNotificationResponse{}, []byte{}).Return(errors.New("test"))
|
||||
assert.Equal(t, errors.New("test"), m.ConnectAndWrite(&PushNotificationResponse{}, []byte{}))
|
||||
}
|
||||
|
||||
func TestMockClientSend(t *testing.T) {
|
||||
m := &MockClient{}
|
||||
m.On("Send", (*PushNotification)(nil)).Return(nil)
|
||||
assert.Nil(t, m.Send(nil))
|
||||
m.On("Send", &PushNotification{}).Return(&PushNotificationResponse{})
|
||||
assert.Equal(t, &PushNotificationResponse{}, m.Send(&PushNotification{}))
|
||||
}
|
||||
102
Godeps/_workspace/src/github.com/anachronistic/apns/feedback.go
сгенерированный
поставляемый
Обычный файл
102
Godeps/_workspace/src/github.com/anachronistic/apns/feedback.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,102 @@
|
||||
package apns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Wait at most this many seconds for feedback data from Apple.
|
||||
const FeedbackTimeoutSeconds = 5
|
||||
|
||||
// FeedbackChannel will receive individual responses from Apple.
|
||||
var FeedbackChannel = make(chan (*FeedbackResponse))
|
||||
|
||||
// If there's nothing to read, ShutdownChannel gets a true.
|
||||
var ShutdownChannel = make(chan bool)
|
||||
|
||||
// FeedbackResponse represents a device token that Apple has
|
||||
// indicated should not be sent to in the future.
|
||||
type FeedbackResponse struct {
|
||||
Timestamp uint32
|
||||
DeviceToken string
|
||||
}
|
||||
|
||||
// NewFeedbackResponse creates and returns a FeedbackResponse structure.
|
||||
func NewFeedbackResponse() (resp *FeedbackResponse) {
|
||||
resp = new(FeedbackResponse)
|
||||
return
|
||||
}
|
||||
|
||||
// ListenForFeedback connects to the Apple Feedback Service
|
||||
// and checks for device tokens.
|
||||
//
|
||||
// Feedback consists of device tokens that should
|
||||
// not be sent to in the future; Apple *does* monitor that
|
||||
// you respect this so you should be checking it ;)
|
||||
func (client *Client) ListenForFeedback() (err error) {
|
||||
var cert tls.Certificate
|
||||
|
||||
if len(client.CertificateBase64) == 0 && len(client.KeyBase64) == 0 {
|
||||
// The user did not specify raw block contents, so check the filesystem.
|
||||
cert, err = tls.LoadX509KeyPair(client.CertificateFile, client.KeyFile)
|
||||
} else {
|
||||
// The user provided the raw block contents, so use that.
|
||||
cert, err = tls.X509KeyPair([]byte(client.CertificateBase64), []byte(client.KeyBase64))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gatewayParts := strings.Split(client.Gateway, ":")
|
||||
conf := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
ServerName: gatewayParts[0],
|
||||
}
|
||||
|
||||
conn, err := net.Dial("tcp", client.Gateway)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
conn.SetReadDeadline(time.Now().Add(FeedbackTimeoutSeconds * time.Second))
|
||||
|
||||
tlsConn := tls.Client(conn, conf)
|
||||
err = tlsConn.Handshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tokenLength uint16
|
||||
buffer := make([]byte, 38, 38)
|
||||
deviceToken := make([]byte, 32, 32)
|
||||
|
||||
for {
|
||||
_, err := tlsConn.Read(buffer)
|
||||
if err != nil {
|
||||
ShutdownChannel <- true
|
||||
break
|
||||
}
|
||||
|
||||
resp := NewFeedbackResponse()
|
||||
|
||||
r := bytes.NewReader(buffer)
|
||||
binary.Read(r, binary.BigEndian, &resp.Timestamp)
|
||||
binary.Read(r, binary.BigEndian, &tokenLength)
|
||||
binary.Read(r, binary.BigEndian, &deviceToken)
|
||||
if tokenLength != 32 {
|
||||
return errors.New("token length should be equal to 32, but isn't")
|
||||
}
|
||||
resp.DeviceToken = hex.EncodeToString(deviceToken)
|
||||
|
||||
FeedbackChannel <- resp
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
15
Godeps/_workspace/src/github.com/anachronistic/apns/legacy.go
сгенерированный
поставляемый
Обычный файл
15
Godeps/_workspace/src/github.com/anachronistic/apns/legacy.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,15 @@
|
||||
package apns
|
||||
|
||||
// This file exists to support backward-compatibility
|
||||
// as I gradually refactor and overhaul. Ideally, golint
|
||||
// should only complain about this file (and we should
|
||||
// try to keep its complaints to a minimum).
|
||||
|
||||
// These variables map old identifiers to their current format.
|
||||
var (
|
||||
APPLE_PUSH_RESPONSES = ApplePushResponses
|
||||
FEEDBACK_TIMEOUT_SECONDS = FeedbackTimeoutSeconds
|
||||
IDENTIFIER_UBOUND = IdentifierUbound
|
||||
MAX_PAYLOAD_SIZE_BYTES = MaxPayloadSizeBytes
|
||||
TIMEOUT_SECONDS = TimeoutSeconds
|
||||
)
|
||||
27
Godeps/_workspace/src/github.com/anachronistic/apns/legacy_test.go
сгенерированный
поставляемый
Обычный файл
27
Godeps/_workspace/src/github.com/anachronistic/apns/legacy_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,27 @@
|
||||
package apns
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// These identifiers were changed to resolve golint violations.
|
||||
// However, it's possible that legacy code may rely on them. This
|
||||
// will help avoid springing a breaking change on people.
|
||||
func TestLegacyConstants(t *testing.T) {
|
||||
if !reflect.DeepEqual(APPLE_PUSH_RESPONSES, ApplePushResponses) {
|
||||
t.Error("expected APPLE_PUSH_RESPONSES to equal ApplePushResponses")
|
||||
}
|
||||
if !reflect.DeepEqual(FEEDBACK_TIMEOUT_SECONDS, FeedbackTimeoutSeconds) {
|
||||
t.Error("expected FEEDBACK_TIMEOUT_SECONDS to equal FeedbackTimeoutSeconds")
|
||||
}
|
||||
if !reflect.DeepEqual(IDENTIFIER_UBOUND, IdentifierUbound) {
|
||||
t.Error("expected IDENTIFIER_UBOUND to equal IdentifierUbound")
|
||||
}
|
||||
if !reflect.DeepEqual(MAX_PAYLOAD_SIZE_BYTES, MaxPayloadSizeBytes) {
|
||||
t.Error("expected MAX_PAYLOAD_SIZE_BYTES to equal MaxPayloadSizeBytes")
|
||||
}
|
||||
if !reflect.DeepEqual(TIMEOUT_SECONDS, TimeoutSeconds) {
|
||||
t.Error("expected TIMEOUT_SECONDS to equal TimeoutSeconds")
|
||||
}
|
||||
}
|
||||
53
Godeps/_workspace/src/github.com/anachronistic/apns/mock_feedback_server.go
сгенерированный
поставляемый
Обычный файл
53
Godeps/_workspace/src/github.com/anachronistic/apns/mock_feedback_server.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,53 @@
|
||||
package apns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StartMockFeedbackServer spins up a simple stand-in for the Apple
|
||||
// feedback service that can be used for testing purposes. Doesn't
|
||||
// handle many errors, etc. Just for the sake of having something "live"
|
||||
// to hit.
|
||||
func StartMockFeedbackServer(certFile, keyFile string) {
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
config := tls.Config{Certificates: []tls.Certificate{cert}, ClientAuth: tls.RequireAnyClientCert}
|
||||
log.Print("- starting Mock Apple Feedback TCP server at 0.0.0.0:5555")
|
||||
|
||||
srv, _ := tls.Listen("tcp", "0.0.0.0:5555", &config)
|
||||
for {
|
||||
conn, err := srv.Accept()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
go loop(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// Writes binary data to the client in the same
|
||||
// manner as the Apple service would.
|
||||
//
|
||||
// [4 bytes, 2 bytes, 32 bytes] = 38 bytes total
|
||||
func loop(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
for {
|
||||
timeT := uint32(1368809290) // 2013-05-17 12:48:10 -0400
|
||||
token := "abcd1234efab5678abcd1234efab5678"
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, binary.BigEndian, timeT)
|
||||
binary.Write(buf, binary.BigEndian, uint16(len(token)))
|
||||
binary.Write(buf, binary.BigEndian, []byte(token))
|
||||
conn.Write(buf.Bytes())
|
||||
|
||||
dur, _ := time.ParseDuration("1s")
|
||||
time.Sleep(dur)
|
||||
}
|
||||
}
|
||||
175
Godeps/_workspace/src/github.com/anachronistic/apns/push_notification.go
сгенерированный
поставляемый
Обычный файл
175
Godeps/_workspace/src/github.com/anachronistic/apns/push_notification.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,175 @@
|
||||
package apns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Push commands always start with command value 2.
|
||||
const pushCommandValue = 2
|
||||
|
||||
// Your total notification payload cannot exceed 2 KB.
|
||||
const MaxPayloadSizeBytes = 2048
|
||||
|
||||
// Every push notification gets a pseudo-unique identifier;
|
||||
// this establishes the upper boundary for it. Apple will return
|
||||
// this identifier if there is an issue sending your notification.
|
||||
const IdentifierUbound = 9999
|
||||
|
||||
// Constants related to the payload fields and their lengths.
|
||||
const (
|
||||
deviceTokenItemid = 1
|
||||
payloadItemid = 2
|
||||
notificationIdentifierItemid = 3
|
||||
expirationDateItemid = 4
|
||||
priorityItemid = 5
|
||||
deviceTokenLength = 32
|
||||
notificationIdentifierLength = 4
|
||||
expirationDateLength = 4
|
||||
priorityLength = 1
|
||||
)
|
||||
|
||||
// Payload contains the notification data for your request.
|
||||
//
|
||||
// Alert is an interface here because it supports either a string
|
||||
// or a dictionary, represented within by an AlertDictionary struct.
|
||||
type Payload struct {
|
||||
Alert interface{} `json:"alert,omitempty"`
|
||||
Badge int `json:"badge,omitempty"`
|
||||
Sound string `json:"sound,omitempty"`
|
||||
ContentAvailable int `json:"content-available,omitempty"`
|
||||
Category string `json:"category,omitempty"`
|
||||
}
|
||||
|
||||
// NewPayload creates and returns a Payload structure.
|
||||
func NewPayload() *Payload {
|
||||
return new(Payload)
|
||||
}
|
||||
|
||||
// AlertDictionary is a more complex notification payload.
|
||||
//
|
||||
// From the APN docs: "Use the ... alert dictionary in general only if you absolutely need to."
|
||||
// The AlertDictionary is suitable for specific localization needs.
|
||||
type AlertDictionary struct {
|
||||
Body string `json:"body,omitempty"`
|
||||
ActionLocKey string `json:"action-loc-key,omitempty"`
|
||||
LocKey string `json:"loc-key,omitempty"`
|
||||
LocArgs []string `json:"loc-args,omitempty"`
|
||||
LaunchImage string `json:"launch-image,omitempty"`
|
||||
}
|
||||
|
||||
// NewAlertDictionary creates and returns an AlertDictionary structure.
|
||||
func NewAlertDictionary() *AlertDictionary {
|
||||
return new(AlertDictionary)
|
||||
}
|
||||
|
||||
// PushNotification is the wrapper for the Payload.
|
||||
// The length fields are computed in ToBytes() and aren't represented here.
|
||||
type PushNotification struct {
|
||||
Identifier int32
|
||||
Expiry uint32
|
||||
DeviceToken string
|
||||
payload map[string]interface{}
|
||||
Priority uint8
|
||||
}
|
||||
|
||||
// NewPushNotification creates and returns a PushNotification structure.
|
||||
// It also initializes the pseudo-random identifier.
|
||||
func NewPushNotification() (pn *PushNotification) {
|
||||
pn = new(PushNotification)
|
||||
pn.payload = make(map[string]interface{})
|
||||
pn.Identifier = rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(IdentifierUbound)
|
||||
pn.Priority = 10
|
||||
return
|
||||
}
|
||||
|
||||
// AddPayload sets the "aps" payload section of the request. It also
|
||||
// has a hack described within to deal with specific zero values.
|
||||
func (pn *PushNotification) AddPayload(p *Payload) {
|
||||
// This deserves some explanation.
|
||||
//
|
||||
// Setting an exported field of type int to 0
|
||||
// triggers the omitempty behavior if you've set it.
|
||||
// Since the badge is optional, we should omit it if
|
||||
// it's not set. However, we want to include it if the
|
||||
// value is 0, so there's a hack in push_notification.go
|
||||
// that exploits the fact that Apple treats -1 for a
|
||||
// badge value as though it were 0 (i.e. it clears the
|
||||
// badge but doesn't stop the notification from going
|
||||
// through successfully.)
|
||||
//
|
||||
// Still a hack though :)
|
||||
if p.Badge == 0 {
|
||||
p.Badge = -1
|
||||
}
|
||||
pn.Set("aps", p)
|
||||
}
|
||||
|
||||
// Get returns the value of a payload key, if it exists.
|
||||
func (pn *PushNotification) Get(key string) interface{} {
|
||||
return pn.payload[key]
|
||||
}
|
||||
|
||||
// Set defines the value of a payload key.
|
||||
func (pn *PushNotification) Set(key string, value interface{}) {
|
||||
pn.payload[key] = value
|
||||
}
|
||||
|
||||
// PayloadJSON returns the current payload in JSON format.
|
||||
func (pn *PushNotification) PayloadJSON() ([]byte, error) {
|
||||
return json.Marshal(pn.payload)
|
||||
}
|
||||
|
||||
// PayloadString returns the current payload in string format.
|
||||
func (pn *PushNotification) PayloadString() (string, error) {
|
||||
j, err := pn.PayloadJSON()
|
||||
return string(j), err
|
||||
}
|
||||
|
||||
// ToBytes returns a byte array of the complete PushNotification
|
||||
// struct. This array is what should be transmitted to the APN Service.
|
||||
func (pn *PushNotification) ToBytes() ([]byte, error) {
|
||||
token, err := hex.DecodeString(pn.DeviceToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(token) != deviceTokenLength {
|
||||
return nil, errors.New("device token has incorrect length")
|
||||
}
|
||||
payload, err := pn.PayloadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(payload) > MaxPayloadSizeBytes {
|
||||
return nil, errors.New("payload is larger than the " + strconv.Itoa(MaxPayloadSizeBytes) + " byte limit")
|
||||
}
|
||||
|
||||
frameBuffer := new(bytes.Buffer)
|
||||
binary.Write(frameBuffer, binary.BigEndian, uint8(deviceTokenItemid))
|
||||
binary.Write(frameBuffer, binary.BigEndian, uint16(deviceTokenLength))
|
||||
binary.Write(frameBuffer, binary.BigEndian, token)
|
||||
binary.Write(frameBuffer, binary.BigEndian, uint8(payloadItemid))
|
||||
binary.Write(frameBuffer, binary.BigEndian, uint16(len(payload)))
|
||||
binary.Write(frameBuffer, binary.BigEndian, payload)
|
||||
binary.Write(frameBuffer, binary.BigEndian, uint8(notificationIdentifierItemid))
|
||||
binary.Write(frameBuffer, binary.BigEndian, uint16(notificationIdentifierLength))
|
||||
binary.Write(frameBuffer, binary.BigEndian, pn.Identifier)
|
||||
binary.Write(frameBuffer, binary.BigEndian, uint8(expirationDateItemid))
|
||||
binary.Write(frameBuffer, binary.BigEndian, uint16(expirationDateLength))
|
||||
binary.Write(frameBuffer, binary.BigEndian, pn.Expiry)
|
||||
binary.Write(frameBuffer, binary.BigEndian, uint8(priorityItemid))
|
||||
binary.Write(frameBuffer, binary.BigEndian, uint16(priorityLength))
|
||||
binary.Write(frameBuffer, binary.BigEndian, pn.Priority)
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
binary.Write(buffer, binary.BigEndian, uint8(pushCommandValue))
|
||||
binary.Write(buffer, binary.BigEndian, uint32(frameBuffer.Len()))
|
||||
binary.Write(buffer, binary.BigEndian, frameBuffer.Bytes())
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
36
Godeps/_workspace/src/github.com/anachronistic/apns/push_notification_response.go
сгенерированный
поставляемый
Обычный файл
36
Godeps/_workspace/src/github.com/anachronistic/apns/push_notification_response.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,36 @@
|
||||
package apns
|
||||
|
||||
// The maximum number of seconds we're willing to wait for a response
|
||||
// from the Apple Push Notification Service.
|
||||
const TimeoutSeconds = 5
|
||||
|
||||
// This enumerates the response codes that Apple defines
|
||||
// for push notification attempts.
|
||||
var ApplePushResponses = map[uint8]string{
|
||||
0: "NO_ERRORS",
|
||||
1: "PROCESSING_ERROR",
|
||||
2: "MISSING_DEVICE_TOKEN",
|
||||
3: "MISSING_TOPIC",
|
||||
4: "MISSING_PAYLOAD",
|
||||
5: "INVALID_TOKEN_SIZE",
|
||||
6: "INVALID_TOPIC_SIZE",
|
||||
7: "INVALID_PAYLOAD_SIZE",
|
||||
8: "INVALID_TOKEN",
|
||||
10: "SHUTDOWN",
|
||||
255: "UNKNOWN",
|
||||
}
|
||||
|
||||
// PushNotificationResponse details what Apple had to say, if anything.
|
||||
type PushNotificationResponse struct {
|
||||
Success bool
|
||||
AppleResponse string
|
||||
Error error
|
||||
}
|
||||
|
||||
// NewPushNotificationResponse creates and returns a new PushNotificationResponse
|
||||
// structure; it defaults to being unsuccessful at first.
|
||||
func NewPushNotificationResponse() (resp *PushNotificationResponse) {
|
||||
resp = new(PushNotificationResponse)
|
||||
resp.Success = false
|
||||
return
|
||||
}
|
||||
111
Godeps/_workspace/src/github.com/anachronistic/apns/push_notification_test.go
сгенерированный
поставляемый
Обычный файл
111
Godeps/_workspace/src/github.com/anachronistic/apns/push_notification_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,111 @@
|
||||
package apns
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testDeviceToken = "e93b7686988b4b5fd334298e60e73d90035f6d12628a80b4029bde0dec514df9"
|
||||
|
||||
// Create a new Payload that specifies simple text,
|
||||
// a badge counter, and a custom notification sound.
|
||||
func mockPayload() (payload *Payload) {
|
||||
payload = NewPayload()
|
||||
payload.Alert = "You have mail!"
|
||||
payload.Badge = 42
|
||||
payload.Sound = "bingbong.aiff"
|
||||
return
|
||||
}
|
||||
|
||||
// See the commentary in push_notification.go for information
|
||||
// on why we're testing a badge of value 0.
|
||||
func mockZeroBadgePayload() (payload *Payload) {
|
||||
payload = mockPayload()
|
||||
payload.Badge = 0
|
||||
return
|
||||
}
|
||||
|
||||
// Create a new AlertDictionary. Apple recommends you not use
|
||||
// the more complex alert style unless absolutely necessary.
|
||||
func mockAlertDictionary() (dict *AlertDictionary) {
|
||||
args := make([]string, 1)
|
||||
args[0] = "localized args"
|
||||
|
||||
dict = NewAlertDictionary()
|
||||
dict.Body = "Complex Message"
|
||||
dict.ActionLocKey = "Play a Game!"
|
||||
dict.LocKey = "localized key"
|
||||
dict.LocArgs = args
|
||||
dict.LaunchImage = "image.jpg"
|
||||
return
|
||||
}
|
||||
|
||||
func TestBasicAlert(t *testing.T) {
|
||||
payload := mockPayload()
|
||||
pn := NewPushNotification()
|
||||
|
||||
pn.DeviceToken = testDeviceToken
|
||||
pn.AddPayload(payload)
|
||||
|
||||
bytes, _ := pn.ToBytes()
|
||||
json, _ := pn.PayloadJSON()
|
||||
if len(bytes) != 130 {
|
||||
t.Error("expected 130 bytes; got", len(bytes))
|
||||
}
|
||||
if len(json) != 69 {
|
||||
t.Error("expected 69 bytes; got", len(json))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlertDictionary(t *testing.T) {
|
||||
dict := mockAlertDictionary()
|
||||
payload := mockPayload()
|
||||
payload.Alert = dict
|
||||
|
||||
pn := NewPushNotification()
|
||||
pn.DeviceToken = testDeviceToken
|
||||
pn.AddPayload(payload)
|
||||
|
||||
bytes, _ := pn.ToBytes()
|
||||
json, _ := pn.PayloadJSON()
|
||||
if len(bytes) != 255 {
|
||||
t.Error("expected 255 bytes; got", len(bytes))
|
||||
}
|
||||
if len(json) != 194 {
|
||||
t.Error("expected 194 bytes; got", len(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomParameters(t *testing.T) {
|
||||
payload := mockPayload()
|
||||
pn := NewPushNotification()
|
||||
|
||||
pn.DeviceToken = testDeviceToken
|
||||
pn.AddPayload(payload)
|
||||
pn.Set("foo", "bar")
|
||||
|
||||
if pn.Get("foo") != "bar" {
|
||||
t.Error("unable to set a custom property")
|
||||
}
|
||||
if pn.Get("not_set") != nil {
|
||||
t.Error("expected a missing key to return nil")
|
||||
}
|
||||
|
||||
bytes, _ := pn.ToBytes()
|
||||
json, _ := pn.PayloadJSON()
|
||||
if len(bytes) != 142 {
|
||||
t.Error("expected 110 bytes; got", len(bytes))
|
||||
}
|
||||
if len(json) != 81 {
|
||||
t.Error("expected 81 bytes; got", len(json))
|
||||
}
|
||||
}
|
||||
|
||||
func TestZeroBadgeChangesToNegativeOne(t *testing.T) {
|
||||
payload := mockZeroBadgePayload()
|
||||
pn := NewPushNotification()
|
||||
pn.AddPayload(payload)
|
||||
|
||||
if payload.Badge != -1 {
|
||||
t.Error("expected 0 badge value to be converted to -1; got", payload.Badge)
|
||||
}
|
||||
}
|
||||
142
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value.go
сгенерированный
поставляемый
Обычный файл
142
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,142 @@
|
||||
package awsutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`)
|
||||
|
||||
func rValuesAtPath(v interface{}, path string, create bool) []reflect.Value {
|
||||
pathparts := strings.Split(path, "||")
|
||||
if len(pathparts) > 1 {
|
||||
for _, pathpart := range pathparts {
|
||||
vals := rValuesAtPath(v, pathpart, create)
|
||||
if vals != nil && len(vals) > 0 {
|
||||
return vals
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
values := []reflect.Value{reflect.Indirect(reflect.ValueOf(v))}
|
||||
components := strings.Split(path, ".")
|
||||
for len(values) > 0 && len(components) > 0 {
|
||||
var index *int64
|
||||
var indexStar bool
|
||||
c := strings.TrimSpace(components[0])
|
||||
if c == "" { // no actual component, illegal syntax
|
||||
return nil
|
||||
} else if c != "*" && strings.ToLower(c[0:1]) == c[0:1] {
|
||||
// TODO normalize case for user
|
||||
return nil // don't support unexported fields
|
||||
}
|
||||
|
||||
// parse this component
|
||||
if m := indexRe.FindStringSubmatch(c); m != nil {
|
||||
c = m[1]
|
||||
if m[2] == "" {
|
||||
index = nil
|
||||
indexStar = true
|
||||
} else {
|
||||
i, _ := strconv.ParseInt(m[2], 10, 32)
|
||||
index = &i
|
||||
indexStar = false
|
||||
}
|
||||
}
|
||||
|
||||
nextvals := []reflect.Value{}
|
||||
for _, value := range values {
|
||||
// pull component name out of struct member
|
||||
if value.Kind() != reflect.Struct {
|
||||
continue
|
||||
}
|
||||
|
||||
if c == "*" { // pull all members
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
if f := reflect.Indirect(value.Field(i)); f.IsValid() {
|
||||
nextvals = append(nextvals, f)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
value = value.FieldByName(c)
|
||||
if create && value.Kind() == reflect.Ptr && value.IsNil() {
|
||||
value.Set(reflect.New(value.Type().Elem()))
|
||||
value = value.Elem()
|
||||
} else {
|
||||
value = reflect.Indirect(value)
|
||||
}
|
||||
|
||||
if value.IsValid() {
|
||||
nextvals = append(nextvals, value)
|
||||
}
|
||||
}
|
||||
values = nextvals
|
||||
|
||||
if indexStar || index != nil {
|
||||
nextvals = []reflect.Value{}
|
||||
for _, value := range values {
|
||||
value := reflect.Indirect(value)
|
||||
if value.Kind() != reflect.Slice {
|
||||
continue
|
||||
}
|
||||
|
||||
if indexStar { // grab all indices
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
idx := reflect.Indirect(value.Index(i))
|
||||
if idx.IsValid() {
|
||||
nextvals = append(nextvals, idx)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// pull out index
|
||||
i := int(*index)
|
||||
if i >= value.Len() { // check out of bounds
|
||||
if create {
|
||||
// TODO resize slice
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if i < 0 { // support negative indexing
|
||||
i = value.Len() + i
|
||||
}
|
||||
value = reflect.Indirect(value.Index(i))
|
||||
|
||||
if value.IsValid() {
|
||||
nextvals = append(nextvals, value)
|
||||
}
|
||||
}
|
||||
values = nextvals
|
||||
}
|
||||
|
||||
components = components[1:]
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// ValuesAtPath returns a list of objects at the lexical path inside of a structure
|
||||
func ValuesAtPath(i interface{}, path string) []interface{} {
|
||||
if rvals := rValuesAtPath(i, path, false); rvals != nil {
|
||||
vals := make([]interface{}, len(rvals))
|
||||
for i, rval := range rvals {
|
||||
vals[i] = rval.Interface()
|
||||
}
|
||||
return vals
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetValueAtPath sets an object at the lexical path inside of a structure
|
||||
func SetValueAtPath(i interface{}, path string, v interface{}) {
|
||||
if rvals := rValuesAtPath(i, path, true); rvals != nil {
|
||||
for _, rval := range rvals {
|
||||
rval.Set(reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value_test.go
сгенерированный
поставляемый
Обычный файл
60
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,60 @@
|
||||
package awsutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws/awsutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type Struct struct {
|
||||
A []Struct
|
||||
a []Struct
|
||||
B *Struct
|
||||
D *Struct
|
||||
C string
|
||||
}
|
||||
|
||||
var data = Struct{
|
||||
A: []Struct{Struct{C: "value1"}, Struct{C: "value2"}, Struct{C: "value3"}},
|
||||
a: []Struct{Struct{C: "value1"}, Struct{C: "value2"}, Struct{C: "value3"}},
|
||||
B: &Struct{B: &Struct{C: "terminal"}, D: &Struct{C: "terminal2"}},
|
||||
C: "initial",
|
||||
}
|
||||
|
||||
func TestValueAtPathSuccess(t *testing.T) {
|
||||
assert.Equal(t, []interface{}{"initial"}, awsutil.ValuesAtPath(data, "C"))
|
||||
assert.Equal(t, []interface{}{"value1"}, awsutil.ValuesAtPath(data, "A[0].C"))
|
||||
assert.Equal(t, []interface{}{"value2"}, awsutil.ValuesAtPath(data, "A[1].C"))
|
||||
assert.Equal(t, []interface{}{"value3"}, awsutil.ValuesAtPath(data, "A[2].C"))
|
||||
assert.Equal(t, []interface{}{"value3"}, awsutil.ValuesAtPath(data, "A[-1].C"))
|
||||
assert.Equal(t, []interface{}{"value1", "value2", "value3"}, awsutil.ValuesAtPath(data, "A[].C"))
|
||||
assert.Equal(t, []interface{}{"terminal"}, awsutil.ValuesAtPath(data, "B . B . C"))
|
||||
assert.Equal(t, []interface{}{"terminal", "terminal2"}, awsutil.ValuesAtPath(data, "B.*.C"))
|
||||
assert.Equal(t, []interface{}{"initial"}, awsutil.ValuesAtPath(data, "A.D.X || C"))
|
||||
}
|
||||
|
||||
func TestValueAtPathFailure(t *testing.T) {
|
||||
assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, "C.x"))
|
||||
assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, ".x"))
|
||||
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "X.Y.Z"))
|
||||
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "A[100].C"))
|
||||
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "A[3].C"))
|
||||
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "B.B.C.Z"))
|
||||
assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, "a[-1].C"))
|
||||
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(nil, "A.B.C"))
|
||||
}
|
||||
|
||||
func TestSetValueAtPathSuccess(t *testing.T) {
|
||||
var s Struct
|
||||
awsutil.SetValueAtPath(&s, "C", "test1")
|
||||
awsutil.SetValueAtPath(&s, "B.B.C", "test2")
|
||||
awsutil.SetValueAtPath(&s, "B.D.C", "test3")
|
||||
assert.Equal(t, "test1", s.C)
|
||||
assert.Equal(t, "test2", s.B.B.C)
|
||||
assert.Equal(t, "test3", s.B.D.C)
|
||||
|
||||
awsutil.SetValueAtPath(&s, "B.*.C", "test0")
|
||||
assert.Equal(t, "test0", s.B.B.C)
|
||||
assert.Equal(t, "test0", s.B.D.C)
|
||||
}
|
||||
88
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/string_value.go
сгенерированный
поставляемый
Обычный файл
88
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/string_value.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,88 @@
|
||||
package awsutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func StringValue(i interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
stringValue(reflect.ValueOf(i), 0, &buf)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func stringValue(v reflect.Value, indent int, buf *bytes.Buffer) {
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
buf.WriteString("{\n")
|
||||
|
||||
names := []string{}
|
||||
for i := 0; i < v.Type().NumField(); i++ {
|
||||
name := v.Type().Field(i).Name
|
||||
f := v.Field(i)
|
||||
if name[0:1] == strings.ToLower(name[0:1]) {
|
||||
continue // ignore unexported fields
|
||||
}
|
||||
if (f.Kind() == reflect.Ptr || f.Kind() == reflect.Slice) && f.IsNil() {
|
||||
continue // ignore unset fields
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
for i, n := range names {
|
||||
val := v.FieldByName(n)
|
||||
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||
buf.WriteString(n + ": ")
|
||||
stringValue(val, indent+2, buf)
|
||||
|
||||
if i < len(names)-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||
case reflect.Slice:
|
||||
nl, id, id2 := "", "", ""
|
||||
if v.Len() > 3 {
|
||||
nl, id, id2 = "\n", strings.Repeat(" ", indent), strings.Repeat(" ", indent+2)
|
||||
}
|
||||
buf.WriteString("[" + nl)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
buf.WriteString(id2)
|
||||
stringValue(v.Index(i), indent+2, buf)
|
||||
|
||||
if i < v.Len()-1 {
|
||||
buf.WriteString("," + nl)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString(nl + id + "]")
|
||||
case reflect.Map:
|
||||
buf.WriteString("{\n")
|
||||
|
||||
for i, k := range v.MapKeys() {
|
||||
buf.WriteString(strings.Repeat(" ", indent+2))
|
||||
buf.WriteString(k.String() + ": ")
|
||||
stringValue(v.MapIndex(k), indent+2, buf)
|
||||
|
||||
if i < v.Len()-1 {
|
||||
buf.WriteString(",\n")
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
|
||||
default:
|
||||
format := "%v"
|
||||
switch v.Interface().(type) {
|
||||
case string:
|
||||
format = "%q"
|
||||
}
|
||||
fmt.Fprintf(buf, format, v.Interface())
|
||||
}
|
||||
}
|
||||
101
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/config.go
сгенерированный
поставляемый
Обычный файл
101
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/config.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,101 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
const DEFAULT_RETRIES = -1
|
||||
|
||||
var DefaultConfig = &Config{
|
||||
Credentials: DefaultCreds(),
|
||||
Endpoint: "",
|
||||
Region: os.Getenv("AWS_REGION"),
|
||||
DisableSSL: false,
|
||||
ManualSend: false,
|
||||
HTTPClient: http.DefaultClient,
|
||||
LogLevel: 0,
|
||||
Logger: os.Stdout,
|
||||
MaxRetries: DEFAULT_RETRIES,
|
||||
DisableParamValidation: false,
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Credentials CredentialsProvider
|
||||
Endpoint string
|
||||
Region string
|
||||
DisableSSL bool
|
||||
ManualSend bool
|
||||
HTTPClient *http.Client
|
||||
LogLevel uint
|
||||
Logger io.Writer
|
||||
MaxRetries int
|
||||
DisableParamValidation bool
|
||||
}
|
||||
|
||||
func (c Config) Merge(newcfg *Config) *Config {
|
||||
cfg := Config{}
|
||||
|
||||
if newcfg != nil && newcfg.Credentials != nil {
|
||||
cfg.Credentials = newcfg.Credentials
|
||||
} else {
|
||||
cfg.Credentials = c.Credentials
|
||||
}
|
||||
|
||||
if newcfg != nil && newcfg.Endpoint != "" {
|
||||
cfg.Endpoint = newcfg.Endpoint
|
||||
} else {
|
||||
cfg.Endpoint = c.Endpoint
|
||||
}
|
||||
|
||||
if newcfg != nil && newcfg.Region != "" {
|
||||
cfg.Region = newcfg.Region
|
||||
} else {
|
||||
cfg.Region = c.Region
|
||||
}
|
||||
|
||||
if newcfg != nil && newcfg.DisableSSL {
|
||||
cfg.DisableSSL = newcfg.DisableSSL
|
||||
} else {
|
||||
cfg.DisableSSL = c.DisableSSL
|
||||
}
|
||||
|
||||
if newcfg != nil && newcfg.ManualSend {
|
||||
cfg.ManualSend = newcfg.ManualSend
|
||||
} else {
|
||||
cfg.ManualSend = c.ManualSend
|
||||
}
|
||||
|
||||
if newcfg != nil && newcfg.HTTPClient != nil {
|
||||
cfg.HTTPClient = newcfg.HTTPClient
|
||||
} else {
|
||||
cfg.HTTPClient = c.HTTPClient
|
||||
}
|
||||
|
||||
if newcfg != nil && newcfg.LogLevel != 0 {
|
||||
cfg.LogLevel = newcfg.LogLevel
|
||||
} else {
|
||||
cfg.LogLevel = c.LogLevel
|
||||
}
|
||||
|
||||
if newcfg != nil && newcfg.Logger != nil {
|
||||
cfg.Logger = newcfg.Logger
|
||||
} else {
|
||||
cfg.Logger = c.Logger
|
||||
}
|
||||
|
||||
if newcfg != nil && newcfg.MaxRetries != DEFAULT_RETRIES {
|
||||
cfg.MaxRetries = newcfg.MaxRetries
|
||||
} else {
|
||||
cfg.MaxRetries = c.MaxRetries
|
||||
}
|
||||
|
||||
if newcfg != nil && newcfg.DisableParamValidation {
|
||||
cfg.DisableParamValidation = newcfg.DisableParamValidation
|
||||
} else {
|
||||
cfg.DisableParamValidation = c.DisableParamValidation
|
||||
}
|
||||
|
||||
return &cfg
|
||||
}
|
||||
288
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/credentials.go
сгенерированный
поставляемый
Обычный файл
288
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/credentials.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,288 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/vaughan0/go-ini"
|
||||
)
|
||||
|
||||
var currentTime = time.Now
|
||||
|
||||
// Credentials are used to authenticate and authorize calls that you make to
|
||||
// AWS.
|
||||
type Credentials struct {
|
||||
AccessKeyID string
|
||||
SecretAccessKey string
|
||||
SessionToken string
|
||||
}
|
||||
|
||||
// A CredentialsProvider is a provider of credentials.
|
||||
type CredentialsProvider interface {
|
||||
// Credentials returns a set of credentials (or an error if no credentials
|
||||
// could be provided).
|
||||
Credentials() (*Credentials, error)
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrAccessKeyIDNotFound is returned when the AWS Access Key ID can't be
|
||||
// found in the process's environment.
|
||||
ErrAccessKeyIDNotFound = fmt.Errorf("AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment")
|
||||
// ErrSecretAccessKeyNotFound is returned when the AWS Secret Access Key
|
||||
// can't be found in the process's environment.
|
||||
ErrSecretAccessKeyNotFound = fmt.Errorf("AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment")
|
||||
)
|
||||
|
||||
type DefaultCredentialsProvider struct {
|
||||
}
|
||||
|
||||
func (p *DefaultCredentialsProvider) Credentials() (*Credentials, error) {
|
||||
env, err := EnvCreds()
|
||||
if err == nil {
|
||||
return env.Credentials()
|
||||
}
|
||||
|
||||
profile, err := ProfileCreds("", "", 10*time.Minute)
|
||||
if err == nil {
|
||||
profileCreds, err := profile.Credentials()
|
||||
if err == nil {
|
||||
return profileCreds, nil
|
||||
}
|
||||
}
|
||||
|
||||
return IAMCreds().Credentials()
|
||||
}
|
||||
|
||||
func DefaultCreds() CredentialsProvider {
|
||||
return &DefaultCredentialsProvider{}
|
||||
}
|
||||
|
||||
// DetectCreds returns a CredentialsProvider based on the available information.
|
||||
//
|
||||
// If the access key ID and secret access key are provided, it returns a basic
|
||||
// provider.
|
||||
//
|
||||
// If credentials are available via environment variables, it returns an
|
||||
// environment provider.
|
||||
//
|
||||
// If a profile configuration file is available in the default location and has
|
||||
// a default profile configured, it returns a profile provider.
|
||||
//
|
||||
// Otherwise, it returns an IAM instance provider.
|
||||
func DetectCreds(accessKeyID, secretAccessKey, sessionToken string) CredentialsProvider {
|
||||
if accessKeyID != "" && secretAccessKey != "" {
|
||||
return Creds(accessKeyID, secretAccessKey, sessionToken)
|
||||
}
|
||||
|
||||
env, err := EnvCreds()
|
||||
if err == nil {
|
||||
return env
|
||||
}
|
||||
|
||||
profile, err := ProfileCreds("", "", 10*time.Minute)
|
||||
if err != nil {
|
||||
return IAMCreds()
|
||||
}
|
||||
|
||||
_, err = profile.Credentials()
|
||||
if err != nil {
|
||||
return IAMCreds()
|
||||
}
|
||||
|
||||
return profile
|
||||
}
|
||||
|
||||
// EnvCreds returns a static provider of AWS credentials from the process's
|
||||
// environment, or an error if none are found.
|
||||
func EnvCreds() (CredentialsProvider, error) {
|
||||
id := os.Getenv("AWS_ACCESS_KEY_ID")
|
||||
if id == "" {
|
||||
id = os.Getenv("AWS_ACCESS_KEY")
|
||||
}
|
||||
|
||||
secret := os.Getenv("AWS_SECRET_ACCESS_KEY")
|
||||
if secret == "" {
|
||||
secret = os.Getenv("AWS_SECRET_KEY")
|
||||
}
|
||||
|
||||
if id == "" {
|
||||
return nil, ErrAccessKeyIDNotFound
|
||||
}
|
||||
|
||||
if secret == "" {
|
||||
return nil, ErrSecretAccessKeyNotFound
|
||||
}
|
||||
|
||||
return Creds(id, secret, os.Getenv("AWS_SESSION_TOKEN")), nil
|
||||
}
|
||||
|
||||
// Creds returns a static provider of credentials.
|
||||
func Creds(accessKeyID, secretAccessKey, sessionToken string) CredentialsProvider {
|
||||
return staticCredentialsProvider{
|
||||
creds: Credentials{
|
||||
AccessKeyID: accessKeyID,
|
||||
SecretAccessKey: secretAccessKey,
|
||||
SessionToken: sessionToken,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// IAMCreds returns a provider which pulls credentials from the local EC2
|
||||
// instance's IAM roles.
|
||||
func IAMCreds() CredentialsProvider {
|
||||
return &iamProvider{}
|
||||
}
|
||||
|
||||
// ProfileCreds returns a provider which pulls credentials from the profile
|
||||
// configuration file.
|
||||
func ProfileCreds(filename, profile string, expiry time.Duration) (CredentialsProvider, error) {
|
||||
if filename == "" {
|
||||
homeDir := os.Getenv("HOME") // *nix
|
||||
if homeDir == "" { // Windows
|
||||
homeDir = os.Getenv("USERPROFILE")
|
||||
}
|
||||
if homeDir == "" {
|
||||
return nil, errors.New("User home directory not found.")
|
||||
}
|
||||
|
||||
filename = filepath.Join(homeDir, ".aws", "credentials")
|
||||
}
|
||||
|
||||
if profile == "" {
|
||||
profile = "default"
|
||||
}
|
||||
|
||||
return &profileProvider{
|
||||
filename: filename,
|
||||
profile: profile,
|
||||
expiry: expiry,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type profileProvider struct {
|
||||
filename string
|
||||
profile string
|
||||
expiry time.Duration
|
||||
|
||||
creds Credentials
|
||||
m sync.Mutex
|
||||
expiration time.Time
|
||||
}
|
||||
|
||||
func (p *profileProvider) Credentials() (*Credentials, error) {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
|
||||
if p.expiration.After(currentTime()) {
|
||||
return &p.creds, nil
|
||||
}
|
||||
|
||||
config, err := ini.LoadFile(p.filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
profile := config.Section(p.profile)
|
||||
|
||||
accessKeyID, ok := profile["aws_access_key_id"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("profile %s in %s did not contain aws_access_key_id", p.profile, p.filename)
|
||||
}
|
||||
|
||||
secretAccessKey, ok := profile["aws_secret_access_key"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("profile %s in %s did not contain aws_secret_access_key", p.profile, p.filename)
|
||||
}
|
||||
|
||||
sessionToken := profile["aws_session_token"]
|
||||
|
||||
p.creds = Credentials{
|
||||
AccessKeyID: accessKeyID,
|
||||
SecretAccessKey: secretAccessKey,
|
||||
SessionToken: sessionToken,
|
||||
}
|
||||
p.expiration = currentTime().Add(p.expiry)
|
||||
|
||||
return &p.creds, nil
|
||||
}
|
||||
|
||||
type iamProvider struct {
|
||||
creds Credentials
|
||||
m sync.Mutex
|
||||
expiration time.Time
|
||||
}
|
||||
|
||||
var metadataCredentialsEndpoint = "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
|
||||
|
||||
// IAMClient is the HTTP client used to query the metadata endpoint for IAM
|
||||
// credentials.
|
||||
var IAMClient = http.Client{
|
||||
Timeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
func (p *iamProvider) Credentials() (*Credentials, error) {
|
||||
p.m.Lock()
|
||||
defer p.m.Unlock()
|
||||
|
||||
if p.expiration.After(currentTime()) {
|
||||
return &p.creds, nil
|
||||
}
|
||||
|
||||
var body struct {
|
||||
Expiration time.Time
|
||||
AccessKeyID string
|
||||
SecretAccessKey string
|
||||
Token string
|
||||
}
|
||||
|
||||
resp, err := IAMClient.Get(metadataCredentialsEndpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing IAM credentials")
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
// Take the first line of the body of the metadata endpoint
|
||||
s := bufio.NewScanner(resp.Body)
|
||||
if !s.Scan() {
|
||||
return nil, fmt.Errorf("unable to find default IAM credentials")
|
||||
} else if s.Err() != nil {
|
||||
return nil, fmt.Errorf("%s listing IAM credentials", s.Err())
|
||||
}
|
||||
|
||||
resp, err = IAMClient.Get(metadataCredentialsEndpoint + s.Text())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting %s IAM credentials", s.Text())
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
return nil, fmt.Errorf("decoding %s IAM credentials", s.Text())
|
||||
}
|
||||
|
||||
p.creds = Credentials{
|
||||
AccessKeyID: body.AccessKeyID,
|
||||
SecretAccessKey: body.SecretAccessKey,
|
||||
SessionToken: body.Token,
|
||||
}
|
||||
p.expiration = body.Expiration
|
||||
|
||||
return &p.creds, nil
|
||||
}
|
||||
|
||||
type staticCredentialsProvider struct {
|
||||
creds Credentials
|
||||
}
|
||||
|
||||
func (p staticCredentialsProvider) Credentials() (*Credentials, error) {
|
||||
return &p.creds, nil
|
||||
}
|
||||
236
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/credentials_test.go
сгенерированный
поставляемый
Обычный файл
236
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/credentials_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,236 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestEnvCreds(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||
os.Setenv("AWS_SESSION_TOKEN", "token")
|
||||
|
||||
prov, err := EnvCreds()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
creds, err := prov.Credentials()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v, want := creds.AccessKeyID, "access"; v != want {
|
||||
t.Errorf("Access key ID was %v, expected %v", v, want)
|
||||
}
|
||||
|
||||
if v, want := creds.SecretAccessKey, "secret"; v != want {
|
||||
t.Errorf("Secret access key was %v, expected %v", v, want)
|
||||
}
|
||||
|
||||
if v, want := creds.SessionToken, "token"; v != want {
|
||||
t.Errorf("Security token was %v, expected %v", v, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvCredsNoAccessKeyID(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||
|
||||
prov, err := EnvCreds()
|
||||
if err != ErrAccessKeyIDNotFound {
|
||||
t.Fatalf("ErrAccessKeyIDNotFound expected, but was %#v/%#v", prov, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvCredsNoSecretAccessKey(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||
|
||||
prov, err := EnvCreds()
|
||||
if err != ErrSecretAccessKeyNotFound {
|
||||
t.Fatalf("ErrSecretAccessKeyNotFound expected, but was %#v/%#v", prov, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvCredsAlternateNames(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_ACCESS_KEY", "access")
|
||||
os.Setenv("AWS_SECRET_KEY", "secret")
|
||||
|
||||
prov, err := EnvCreds()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
creds, err := prov.Credentials()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v, want := creds.AccessKeyID, "access"; v != want {
|
||||
t.Errorf("Access key ID was %v, expected %v", v, want)
|
||||
}
|
||||
|
||||
if v, want := creds.SecretAccessKey, "secret"; v != want {
|
||||
t.Errorf("Secret access key was %v, expected %v", v, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIAMCreds(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.RequestURI == "/" {
|
||||
fmt.Fprintln(w, "/creds")
|
||||
} else {
|
||||
fmt.Fprintln(w, `{
|
||||
"AccessKeyId" : "accessKey",
|
||||
"SecretAccessKey" : "secret",
|
||||
"Token" : "token",
|
||||
"Expiration" : "2014-12-16T01:51:37Z"
|
||||
}`)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
defer func(s string) {
|
||||
metadataCredentialsEndpoint = s
|
||||
}(metadataCredentialsEndpoint)
|
||||
metadataCredentialsEndpoint = server.URL
|
||||
|
||||
defer func() {
|
||||
currentTime = time.Now
|
||||
}()
|
||||
currentTime = func() time.Time {
|
||||
return time.Date(2014, 12, 15, 21, 26, 0, 0, time.UTC)
|
||||
}
|
||||
|
||||
prov := IAMCreds()
|
||||
creds, err := prov.Credentials()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v, want := creds.AccessKeyID, "accessKey"; v != want {
|
||||
t.Errorf("AcccessKeyID was %v, but expected %v", v, want)
|
||||
}
|
||||
|
||||
if v, want := creds.SecretAccessKey, "secret"; v != want {
|
||||
t.Errorf("SecretAccessKey was %v, but expected %v", v, want)
|
||||
}
|
||||
|
||||
if v, want := creds.SessionToken, "token"; v != want {
|
||||
t.Errorf("SessionToken was %v, but expected %v", v, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfileCreds(t *testing.T) {
|
||||
prov, err := ProfileCreds("example.ini", "", 10*time.Minute)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
creds, err := prov.Credentials()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v, want := creds.AccessKeyID, "accessKey"; v != want {
|
||||
t.Errorf("AcccessKeyID was %v, but expected %v", v, want)
|
||||
}
|
||||
|
||||
if v, want := creds.SecretAccessKey, "secret"; v != want {
|
||||
t.Errorf("SecretAccessKey was %v, but expected %v", v, want)
|
||||
}
|
||||
|
||||
if v, want := creds.SessionToken, "token"; v != want {
|
||||
t.Errorf("SessionToken was %v, but expected %v", v, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfileCredsWithoutToken(t *testing.T) {
|
||||
prov, err := ProfileCreds("example.ini", "no_token", 10*time.Minute)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
creds, err := prov.Credentials()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v, want := creds.AccessKeyID, "accessKey"; v != want {
|
||||
t.Errorf("AcccessKeyID was %v, but expected %v", v, want)
|
||||
}
|
||||
|
||||
if v, want := creds.SecretAccessKey, "secret"; v != want {
|
||||
t.Errorf("SecretAccessKey was %v, but expected %v", v, want)
|
||||
}
|
||||
|
||||
if v, want := creds.SessionToken, ""; v != want {
|
||||
t.Errorf("SessionToken was %v, but expected %v", v, want)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkProfileCreds(b *testing.B) {
|
||||
prov, err := ProfileCreds("example.ini", "", 10*time.Minute)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_, err := prov.Credentials()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkIAMCreds(b *testing.B) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.RequestURI == "/" {
|
||||
fmt.Fprintln(w, "/creds")
|
||||
} else {
|
||||
fmt.Fprintln(w, `{
|
||||
"AccessKeyId" : "accessKey",
|
||||
"SecretAccessKey" : "secret",
|
||||
"Token" : "token",
|
||||
"Expiration" : "2014-12-16T01:51:37Z"
|
||||
}`)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
defer func(s string) {
|
||||
metadataCredentialsEndpoint = s
|
||||
}(metadataCredentialsEndpoint)
|
||||
metadataCredentialsEndpoint = server.URL
|
||||
|
||||
defer func() {
|
||||
currentTime = time.Now
|
||||
}()
|
||||
currentTime = func() time.Time {
|
||||
return time.Date(2014, 12, 15, 21, 26, 0, 0, time.UTC)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
prov := IAMCreds()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_, err := prov.Credentials()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
26
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/error.go
сгенерированный
поставляемый
Обычный файл
26
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/error.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,26 @@
|
||||
package aws
|
||||
|
||||
import "time"
|
||||
|
||||
// An APIError is an error returned by an AWS API.
|
||||
type APIError struct {
|
||||
StatusCode int // HTTP status code e.g. 200
|
||||
Code string
|
||||
Message string
|
||||
RequestID string
|
||||
Retryable bool
|
||||
RetryDelay time.Duration
|
||||
RetryCount uint
|
||||
}
|
||||
|
||||
func (e APIError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func Error(e error) *APIError {
|
||||
if err, ok := e.(APIError); ok {
|
||||
return &err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
8
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/example.ini
сгенерированный
поставляемый
Обычный файл
8
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/example.ini
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,8 @@
|
||||
[default]
|
||||
aws_access_key_id = accessKey
|
||||
aws_secret_access_key = secret
|
||||
aws_session_token = token
|
||||
|
||||
[no_token]
|
||||
aws_access_key_id = accessKey
|
||||
aws_secret_access_key = secret
|
||||
78
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handler_functions.go
сгенерированный
поставляемый
Обычный файл
78
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handler_functions.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,78 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
var sleepDelay = func(delay time.Duration) {
|
||||
time.Sleep(delay)
|
||||
}
|
||||
|
||||
type lener interface {
|
||||
Len() int
|
||||
}
|
||||
|
||||
func BuildContentLength(r *Request) {
|
||||
if r.HTTPRequest.Header.Get("Content-Length") != "" {
|
||||
return
|
||||
}
|
||||
|
||||
var length int64
|
||||
switch body := r.Body.(type) {
|
||||
case nil:
|
||||
length = 0
|
||||
case lener:
|
||||
length = int64(body.Len())
|
||||
case io.Seeker:
|
||||
cur, _ := body.Seek(0, 1)
|
||||
end, _ := body.Seek(0, 2)
|
||||
body.Seek(cur, 0) // make sure to seek back to original location
|
||||
length = end - cur
|
||||
default:
|
||||
panic("Cannot get length of body, must provide `ContentLength`")
|
||||
}
|
||||
|
||||
r.HTTPRequest.ContentLength = length
|
||||
r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
|
||||
}
|
||||
|
||||
func UserAgentHandler(r *Request) {
|
||||
r.HTTPRequest.Header.Set("User-Agent", SDKName+"/"+SDKVersion)
|
||||
}
|
||||
|
||||
func SendHandler(r *Request) {
|
||||
r.HTTPResponse, r.Error = r.Service.Config.HTTPClient.Do(r.HTTPRequest)
|
||||
}
|
||||
|
||||
func ValidateResponseHandler(r *Request) {
|
||||
if r.HTTPResponse.StatusCode == 0 || r.HTTPResponse.StatusCode >= 400 {
|
||||
err := APIError{
|
||||
StatusCode: r.HTTPResponse.StatusCode,
|
||||
RetryCount: r.RetryCount,
|
||||
}
|
||||
r.Error = err
|
||||
err.Retryable = r.Service.ShouldRetry(r)
|
||||
err.RetryDelay = r.Service.RetryRules(r)
|
||||
r.Error = err
|
||||
}
|
||||
}
|
||||
|
||||
func AfterRetryHandler(r *Request) {
|
||||
delay := 0 * time.Second
|
||||
willRetry := false
|
||||
|
||||
if err := Error(r.Error); err != nil {
|
||||
delay = err.RetryDelay
|
||||
if err.Retryable && r.RetryCount < r.Service.MaxRetries() {
|
||||
r.RetryCount++
|
||||
willRetry = true
|
||||
}
|
||||
}
|
||||
|
||||
if willRetry {
|
||||
r.Error = nil
|
||||
sleepDelay(delay)
|
||||
}
|
||||
}
|
||||
65
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handlers.go
сгенерированный
поставляемый
Обычный файл
65
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handlers.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,65 @@
|
||||
package aws
|
||||
|
||||
import "container/list"
|
||||
|
||||
type Handlers struct {
|
||||
Validate HandlerList
|
||||
Build HandlerList
|
||||
Sign HandlerList
|
||||
Send HandlerList
|
||||
ValidateResponse HandlerList
|
||||
Unmarshal HandlerList
|
||||
UnmarshalMeta HandlerList
|
||||
UnmarshalError HandlerList
|
||||
Retry HandlerList
|
||||
AfterRetry HandlerList
|
||||
}
|
||||
|
||||
func (h *Handlers) copy() Handlers {
|
||||
return Handlers{
|
||||
Validate: h.Validate.copy(),
|
||||
Build: h.Build.copy(),
|
||||
Sign: h.Sign.copy(),
|
||||
Send: h.Send.copy(),
|
||||
ValidateResponse: h.ValidateResponse.copy(),
|
||||
Unmarshal: h.Unmarshal.copy(),
|
||||
UnmarshalError: h.UnmarshalError.copy(),
|
||||
UnmarshalMeta: h.UnmarshalMeta.copy(),
|
||||
Retry: h.Retry.copy(),
|
||||
AfterRetry: h.AfterRetry.copy(),
|
||||
}
|
||||
}
|
||||
|
||||
// Clear removes callback functions for all handlers
|
||||
func (h *Handlers) Clear() {
|
||||
h.Validate.Init()
|
||||
h.Build.Init()
|
||||
h.Send.Init()
|
||||
h.Sign.Init()
|
||||
h.Unmarshal.Init()
|
||||
h.UnmarshalMeta.Init()
|
||||
h.UnmarshalError.Init()
|
||||
h.ValidateResponse.Init()
|
||||
h.Retry.Init()
|
||||
h.AfterRetry.Init()
|
||||
}
|
||||
|
||||
type HandlerList struct {
|
||||
list.List
|
||||
}
|
||||
|
||||
func (l HandlerList) copy() HandlerList {
|
||||
var n HandlerList
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
h := e.Value.(func(*Request))
|
||||
n.PushBack(h)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (l *HandlerList) Run(r *Request) {
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
h := e.Value.(func(*Request))
|
||||
h(r)
|
||||
}
|
||||
}
|
||||
24
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handlers_test.go
сгенерированный
поставляемый
Обычный файл
24
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/handlers_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,24 @@
|
||||
package aws
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHandlerList(t *testing.T) {
|
||||
r := &Request{}
|
||||
l := HandlerList{}
|
||||
l.PushBack(func(r *Request) { r.Data = Boolean(true) })
|
||||
l.Run(r)
|
||||
if r.Data == nil {
|
||||
t.Error("Expected handler to execute")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleHandlers(t *testing.T) {
|
||||
r := &Request{}
|
||||
l := HandlerList{}
|
||||
l.PushBack(func(r *Request) { r.Data = Boolean(true) })
|
||||
l.PushBack(func(r *Request) { r.Data = nil })
|
||||
l.Run(r)
|
||||
if r.Data != nil {
|
||||
t.Error("Expected handler to execute")
|
||||
}
|
||||
}
|
||||
80
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/param_validator.go
сгенерированный
поставляемый
Обычный файл
80
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/param_validator.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,80 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ValidateParameters(r *Request) {
|
||||
if r.ParamsFilled() {
|
||||
v := validator{errors: []string{}}
|
||||
v.validateAny(reflect.ValueOf(r.Params), "")
|
||||
|
||||
if count := len(v.errors); count > 0 {
|
||||
format := "%d validation errors:\n- %s"
|
||||
msg := fmt.Sprintf(format, count, strings.Join(v.errors, "\n- "))
|
||||
r.Error = APIError{Code: "InvalidParameter", Message: msg}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type validator struct {
|
||||
errors []string
|
||||
}
|
||||
|
||||
func (v *validator) validateAny(value reflect.Value, path string) {
|
||||
value = reflect.Indirect(value)
|
||||
if !value.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Struct:
|
||||
v.validateStruct(value, path)
|
||||
case reflect.Slice:
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
v.validateAny(value.Index(i), path+fmt.Sprintf("[%d]", i))
|
||||
}
|
||||
case reflect.Map:
|
||||
for _, n := range value.MapKeys() {
|
||||
v.validateAny(value.MapIndex(n), path+fmt.Sprintf("[%q]", n.String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *validator) validateStruct(value reflect.Value, path string) {
|
||||
prefix := "."
|
||||
if path == "" {
|
||||
prefix = ""
|
||||
}
|
||||
|
||||
for i := 0; i < value.Type().NumField(); i++ {
|
||||
f := value.Type().Field(i)
|
||||
if strings.ToLower(f.Name[0:1]) == f.Name[0:1] {
|
||||
continue
|
||||
}
|
||||
fvalue := value.FieldByName(f.Name)
|
||||
|
||||
notset := false
|
||||
if f.Tag.Get("required") != "" {
|
||||
switch fvalue.Kind() {
|
||||
case reflect.Ptr, reflect.Slice:
|
||||
if fvalue.IsNil() {
|
||||
notset = true
|
||||
}
|
||||
default:
|
||||
if !fvalue.IsValid() {
|
||||
notset = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if notset {
|
||||
msg := "missing required parameter: " + path + prefix + f.Name
|
||||
v.errors = append(v.errors, msg)
|
||||
} else {
|
||||
v.validateAny(fvalue, path+prefix+f.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
85
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/param_validator_test.go
сгенерированный
поставляемый
Обычный файл
85
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/param_validator_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,85 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var service = func() *aws.Service {
|
||||
s := &aws.Service{
|
||||
Config: &aws.Config{},
|
||||
ServiceName: "mock-service",
|
||||
APIVersion: "2015-01-01",
|
||||
}
|
||||
return s
|
||||
}()
|
||||
|
||||
type StructShape struct {
|
||||
RequiredList []*ConditionalStructShape `required:"true"`
|
||||
RequiredMap *map[string]*ConditionalStructShape `required:"true"`
|
||||
RequiredBool *bool `required:"true"`
|
||||
OptionalStruct *ConditionalStructShape
|
||||
|
||||
hiddenParameter *string
|
||||
|
||||
metadataStructureShape
|
||||
}
|
||||
|
||||
type metadataStructureShape struct {
|
||||
SDKShapeTraits bool
|
||||
}
|
||||
|
||||
type ConditionalStructShape struct {
|
||||
Name *string `required:"true"`
|
||||
SDKShapeTraits bool
|
||||
}
|
||||
|
||||
func TestNoErrors(t *testing.T) {
|
||||
input := &StructShape{
|
||||
RequiredList: []*ConditionalStructShape{},
|
||||
RequiredMap: &map[string]*ConditionalStructShape{
|
||||
"key1": &ConditionalStructShape{Name: aws.String("Name")},
|
||||
"key2": &ConditionalStructShape{Name: aws.String("Name")},
|
||||
},
|
||||
RequiredBool: aws.Boolean(true),
|
||||
OptionalStruct: &ConditionalStructShape{Name: aws.String("Name")},
|
||||
}
|
||||
|
||||
req := aws.NewRequest(service, &aws.Operation{}, input, nil)
|
||||
aws.ValidateParameters(req)
|
||||
assert.NoError(t, req.Error)
|
||||
}
|
||||
|
||||
func TestMissingRequiredParameters(t *testing.T) {
|
||||
input := &StructShape{}
|
||||
req := aws.NewRequest(service, &aws.Operation{}, input, nil)
|
||||
aws.ValidateParameters(req)
|
||||
err := aws.Error(req.Error)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "InvalidParameter", err.Code)
|
||||
assert.Equal(t, "3 validation errors:\n- missing required parameter: RequiredList\n- missing required parameter: RequiredMap\n- missing required parameter: RequiredBool", err.Message)
|
||||
}
|
||||
|
||||
func TestNestedMissingRequiredParameters(t *testing.T) {
|
||||
input := &StructShape{
|
||||
RequiredList: []*ConditionalStructShape{&ConditionalStructShape{}},
|
||||
RequiredMap: &map[string]*ConditionalStructShape{
|
||||
"key1": &ConditionalStructShape{Name: aws.String("Name")},
|
||||
"key2": &ConditionalStructShape{},
|
||||
},
|
||||
RequiredBool: aws.Boolean(true),
|
||||
OptionalStruct: &ConditionalStructShape{},
|
||||
}
|
||||
|
||||
req := aws.NewRequest(service, &aws.Operation{}, input, nil)
|
||||
aws.ValidateParameters(req)
|
||||
err := aws.Error(req.Error)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "InvalidParameter", err.Code)
|
||||
assert.Equal(t, "3 validation errors:\n- missing required parameter: RequiredList[0].Name\n- missing required parameter: RequiredMap[\"key2\"].Name\n- missing required parameter: OptionalStruct.Name", err.Message)
|
||||
|
||||
}
|
||||
149
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/request.go
сгенерированный
поставляемый
Обычный файл
149
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/request.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,149 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
*Service
|
||||
Handlers Handlers
|
||||
Time time.Time
|
||||
ExpireTime time.Duration
|
||||
Operation *Operation
|
||||
HTTPRequest *http.Request
|
||||
HTTPResponse *http.Response
|
||||
Body io.ReadSeeker
|
||||
Params interface{}
|
||||
Error error
|
||||
Data interface{}
|
||||
RequestID string
|
||||
RetryCount uint
|
||||
|
||||
built bool
|
||||
}
|
||||
|
||||
type Operation struct {
|
||||
Name string
|
||||
HTTPMethod string
|
||||
HTTPPath string
|
||||
}
|
||||
|
||||
func NewRequest(service *Service, operation *Operation, params interface{}, data interface{}) *Request {
|
||||
method := operation.HTTPMethod
|
||||
if method == "" {
|
||||
method = "POST"
|
||||
}
|
||||
p := operation.HTTPPath
|
||||
if p == "" {
|
||||
p = "/"
|
||||
}
|
||||
|
||||
httpReq, _ := http.NewRequest(method, "", nil)
|
||||
httpReq.URL, _ = url.Parse(service.Endpoint + p)
|
||||
|
||||
r := &Request{
|
||||
Service: service,
|
||||
Handlers: service.Handlers.copy(),
|
||||
Time: time.Now(),
|
||||
ExpireTime: 0,
|
||||
Operation: operation,
|
||||
HTTPRequest: httpReq,
|
||||
Body: nil,
|
||||
Params: params,
|
||||
Error: nil,
|
||||
Data: data,
|
||||
}
|
||||
r.SetBufferBody([]byte{})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) ParamsFilled() bool {
|
||||
return r.Params != nil && reflect.ValueOf(r.Params).Elem().IsValid()
|
||||
}
|
||||
|
||||
func (r *Request) DataFilled() bool {
|
||||
return r.Data != nil && reflect.ValueOf(r.Data).Elem().IsValid()
|
||||
}
|
||||
|
||||
func (r *Request) SetBufferBody(buf []byte) {
|
||||
r.SetReaderBody(bytes.NewReader(buf))
|
||||
}
|
||||
|
||||
func (r *Request) SetReaderBody(reader io.ReadSeeker) {
|
||||
r.HTTPRequest.Body = ioutil.NopCloser(reader)
|
||||
r.Body = reader
|
||||
}
|
||||
|
||||
func (r *Request) Presign(expireTime time.Duration) (string, error) {
|
||||
r.ExpireTime = expireTime
|
||||
r.Sign()
|
||||
if r.Error != nil {
|
||||
return "", r.Error
|
||||
} else {
|
||||
return r.HTTPRequest.URL.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Request) Build() error {
|
||||
if !r.built {
|
||||
r.Error = nil
|
||||
r.Handlers.Validate.Run(r)
|
||||
if r.Error != nil {
|
||||
return r.Error
|
||||
}
|
||||
r.Handlers.Build.Run(r)
|
||||
r.built = true
|
||||
}
|
||||
|
||||
return r.Error
|
||||
}
|
||||
|
||||
func (r *Request) Sign() error {
|
||||
r.Build()
|
||||
if r.Error != nil {
|
||||
return r.Error
|
||||
}
|
||||
|
||||
r.Handlers.Sign.Run(r)
|
||||
return r.Error
|
||||
}
|
||||
|
||||
func (r *Request) Send() error {
|
||||
r.Sign()
|
||||
if r.Error != nil {
|
||||
return r.Error
|
||||
}
|
||||
|
||||
for {
|
||||
r.Handlers.Send.Run(r)
|
||||
if r.Error != nil {
|
||||
return r.Error
|
||||
}
|
||||
|
||||
r.Handlers.UnmarshalMeta.Run(r)
|
||||
r.Handlers.ValidateResponse.Run(r)
|
||||
if r.Error != nil {
|
||||
r.Handlers.Retry.Run(r)
|
||||
r.Handlers.AfterRetry.Run(r)
|
||||
if r.Error != nil {
|
||||
r.Handlers.UnmarshalError.Run(r)
|
||||
return r.Error
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
r.Handlers.Unmarshal.Run(r)
|
||||
if r.Error != nil {
|
||||
return r.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
118
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/request_test.go
сгенерированный
поставляемый
Обычный файл
118
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/request_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,118 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type testData struct {
|
||||
Data string
|
||||
}
|
||||
|
||||
func body(str string) io.ReadCloser {
|
||||
return ioutil.NopCloser(bytes.NewReader([]byte(str)))
|
||||
}
|
||||
|
||||
func unmarshal(req *Request) {
|
||||
defer req.HTTPResponse.Body.Close()
|
||||
if req.Data != nil {
|
||||
json.NewDecoder(req.HTTPResponse.Body).Decode(req.Data)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func unmarshalError(req *Request) {
|
||||
bodyBytes, err := ioutil.ReadAll(req.HTTPResponse.Body)
|
||||
if err != nil {
|
||||
req.Error = err
|
||||
return
|
||||
}
|
||||
if len(bodyBytes) == 0 {
|
||||
req.Error = APIError{
|
||||
StatusCode: req.HTTPResponse.StatusCode,
|
||||
Message: req.HTTPResponse.Status,
|
||||
}
|
||||
return
|
||||
}
|
||||
var jsonErr jsonErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &jsonErr); err != nil {
|
||||
req.Error = err
|
||||
return
|
||||
}
|
||||
req.Error = APIError{
|
||||
StatusCode: req.HTTPResponse.StatusCode,
|
||||
Code: jsonErr.Code,
|
||||
Message: jsonErr.Message,
|
||||
}
|
||||
}
|
||||
|
||||
type jsonErrorResponse struct {
|
||||
Code string `json:"__type"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func TestRequestRecoverRetry(t *testing.T) {
|
||||
reqNum := 0
|
||||
reqs := []http.Response{
|
||||
http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||
http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||
http.Response{StatusCode: 200, Body: body(`{"data":"valid"}`)},
|
||||
}
|
||||
|
||||
s := NewService(&Config{MaxRetries: -1})
|
||||
s.Handlers.Unmarshal.PushBack(unmarshal)
|
||||
s.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||
s.Handlers.Send.Init() // mock sending
|
||||
s.Handlers.Send.PushBack(func(r *Request) {
|
||||
r.HTTPResponse = &reqs[reqNum]
|
||||
reqNum++
|
||||
})
|
||||
out := &testData{}
|
||||
r := NewRequest(s, &Operation{Name: "Operation"}, nil, out)
|
||||
err := r.Send()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, int(r.RetryCount))
|
||||
assert.Equal(t, "valid", out.Data)
|
||||
}
|
||||
|
||||
func TestRequestExhaustRetries(t *testing.T) {
|
||||
delays := []time.Duration{}
|
||||
sleepDelay = func(delay time.Duration) {
|
||||
delays = append(delays, delay)
|
||||
}
|
||||
|
||||
reqNum := 0
|
||||
reqs := []http.Response{
|
||||
http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||
http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||
http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||
http.Response{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
|
||||
}
|
||||
|
||||
s := NewService(&Config{MaxRetries: -1})
|
||||
s.Handlers.Unmarshal.PushBack(unmarshal)
|
||||
s.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||
s.Handlers.Send.Init() // mock sending
|
||||
s.Handlers.Send.PushBack(func(r *Request) {
|
||||
r.HTTPResponse = &reqs[reqNum]
|
||||
reqNum++
|
||||
})
|
||||
r := NewRequest(s, &Operation{Name: "Operation"}, nil, nil)
|
||||
err := r.Send()
|
||||
apiErr := Error(err)
|
||||
assert.NotNil(t, err)
|
||||
assert.NotNil(t, apiErr)
|
||||
assert.Equal(t, 500, apiErr.StatusCode)
|
||||
assert.Equal(t, "UnknownError", apiErr.Code)
|
||||
assert.Equal(t, "An error occurred.", apiErr.Message)
|
||||
assert.Equal(t, 3, int(r.RetryCount))
|
||||
assert.True(t, reflect.DeepEqual([]time.Duration{30 * time.Millisecond, 60 * time.Millisecond, 120 * time.Millisecond}, delays))
|
||||
}
|
||||
142
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/service.go
сгенерированный
поставляемый
Обычный файл
142
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/service.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,142 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/internal/endpoints"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
Config *Config
|
||||
Handlers Handlers
|
||||
ManualSend bool
|
||||
ServiceName string
|
||||
APIVersion string
|
||||
Endpoint string
|
||||
JSONVersion string
|
||||
TargetPrefix string
|
||||
RetryRules func(*Request) time.Duration
|
||||
ShouldRetry func(*Request) bool
|
||||
DefaultMaxRetries uint
|
||||
}
|
||||
|
||||
var schemeRE = regexp.MustCompile("^([^:]+)://")
|
||||
|
||||
func NewService(config *Config) *Service {
|
||||
svc := &Service{Config: config}
|
||||
svc.Initialize()
|
||||
return svc
|
||||
}
|
||||
|
||||
func (s *Service) Initialize() {
|
||||
if s.Config == nil {
|
||||
s.Config = &Config{}
|
||||
}
|
||||
if s.Config.HTTPClient == nil {
|
||||
s.Config.HTTPClient = http.DefaultClient
|
||||
}
|
||||
|
||||
if s.RetryRules == nil {
|
||||
s.RetryRules = retryRules
|
||||
}
|
||||
|
||||
if s.ShouldRetry == nil {
|
||||
s.ShouldRetry = shouldRetry
|
||||
}
|
||||
|
||||
s.DefaultMaxRetries = 3
|
||||
s.Handlers.Build.PushBack(UserAgentHandler)
|
||||
s.Handlers.Sign.PushBack(BuildContentLength)
|
||||
s.Handlers.Send.PushBack(SendHandler)
|
||||
s.Handlers.AfterRetry.PushBack(AfterRetryHandler)
|
||||
s.Handlers.ValidateResponse.PushBack(ValidateResponseHandler)
|
||||
s.AddDebugHandlers()
|
||||
s.buildEndpoint()
|
||||
|
||||
if !s.Config.DisableParamValidation {
|
||||
s.Handlers.Validate.PushBack(ValidateParameters)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) buildEndpoint() {
|
||||
if s.Config.Endpoint != "" {
|
||||
s.Endpoint = s.Config.Endpoint
|
||||
} else {
|
||||
s.Endpoint = endpoints.EndpointForRegion(s.ServiceName, s.Config.Region)
|
||||
}
|
||||
|
||||
if !schemeRE.MatchString(s.Endpoint) {
|
||||
scheme := "https"
|
||||
if s.Config.DisableSSL {
|
||||
scheme = "http"
|
||||
}
|
||||
s.Endpoint = scheme + "://" + s.Endpoint
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) AddDebugHandlers() {
|
||||
out := s.Config.Logger
|
||||
if s.Config.LogLevel == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s.Handlers.Sign.PushBack(func(r *Request) {
|
||||
dumpedBody, _ := httputil.DumpRequest(r.HTTPRequest, true)
|
||||
|
||||
fmt.Fprintf(out, "=> [%s] %s.%s(%+v)\n", r.Time,
|
||||
r.Service.ServiceName, r.Operation.Name, r.Params)
|
||||
fmt.Fprintf(out, "---[ REQUEST PRE-SIGN ]------------------------------\n")
|
||||
fmt.Fprintf(out, "%s\n", string(dumpedBody))
|
||||
fmt.Fprintf(out, "-----------------------------------------------------\n")
|
||||
})
|
||||
s.Handlers.Send.PushFront(func(r *Request) {
|
||||
dumpedBody, _ := httputil.DumpRequest(r.HTTPRequest, true)
|
||||
|
||||
fmt.Fprintf(out, "---[ REQUEST POST-SIGN ]-----------------------------\n")
|
||||
fmt.Fprintf(out, "%s\n", string(dumpedBody))
|
||||
fmt.Fprintf(out, "-----------------------------------------------------\n")
|
||||
})
|
||||
s.Handlers.Send.PushBack(func(r *Request) {
|
||||
fmt.Fprintf(out, "---[ RESPONSE ]--------------------------------------\n")
|
||||
if r.HTTPResponse != nil {
|
||||
dumpedBody, _ := httputil.DumpResponse(r.HTTPResponse, true)
|
||||
fmt.Fprintf(out, "%s\n", string(dumpedBody))
|
||||
} else if r.Error != nil {
|
||||
fmt.Fprintf(out, "%s\n", r.Error)
|
||||
}
|
||||
fmt.Fprintf(out, "-----------------------------------------------------\n")
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) MaxRetries() uint {
|
||||
if s.Config.MaxRetries < 0 {
|
||||
return s.DefaultMaxRetries
|
||||
} else {
|
||||
return uint(s.Config.MaxRetries)
|
||||
}
|
||||
}
|
||||
|
||||
func retryRules(r *Request) time.Duration {
|
||||
delay := time.Duration(math.Pow(2, float64(r.RetryCount))) * 30
|
||||
return delay * time.Millisecond
|
||||
}
|
||||
|
||||
func shouldRetry(r *Request) bool {
|
||||
if err := Error(r.Error); err != nil {
|
||||
if err.StatusCode >= 500 {
|
||||
return true
|
||||
}
|
||||
|
||||
switch err.Code {
|
||||
case "ExpiredTokenException":
|
||||
case "ProvisionedThroughputExceededException", "Throttling":
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
63
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/types.go
сгенерированный
поставляемый
Обычный файл
63
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/types.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,63 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// String converts a Go string into a string pointer.
|
||||
func String(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Boolean converts a Go bool into a boolean pointer.
|
||||
func Boolean(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Long converts a Go int64 into a long pointer.
|
||||
func Long(v int64) *int64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Double converts a Go float64 into a double pointer.
|
||||
func Double(v float64) *float64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Time converts a Go Time into a Time pointer
|
||||
func Time(t time.Time) *time.Time {
|
||||
return &t
|
||||
}
|
||||
|
||||
func ReadSeekCloser(r io.Reader) ReaderSeekerCloser {
|
||||
return ReaderSeekerCloser{r}
|
||||
}
|
||||
|
||||
type ReaderSeekerCloser struct {
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (r ReaderSeekerCloser) Read(p []byte) (int, error) {
|
||||
switch t := r.r.(type) {
|
||||
case io.Reader:
|
||||
return t.Read(p)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (r ReaderSeekerCloser) Seek(offset int64, whence int) (int64, error) {
|
||||
switch t := r.r.(type) {
|
||||
case io.Seeker:
|
||||
return t.Seek(offset, whence)
|
||||
}
|
||||
return int64(0), nil
|
||||
}
|
||||
|
||||
func (r ReaderSeekerCloser) Close() error {
|
||||
switch t := r.r.(type) {
|
||||
case io.Closer:
|
||||
return t.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
5
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/version.go
сгенерированный
поставляемый
Обычный файл
5
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/version.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,5 @@
|
||||
// Package aws provides core functionality for making requests to AWS services.
|
||||
package aws
|
||||
|
||||
const SDKName = "aws-sdk-go"
|
||||
const SDKVersion = "0.5.0"
|
||||
24
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/endpoints/endpoints.go
сгенерированный
поставляемый
Обычный файл
24
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/endpoints/endpoints.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,24 @@
|
||||
package endpoints
|
||||
|
||||
//go:generate go run ../model/cli/gen-endpoints/main.go endpoints.json endpoints_map.go
|
||||
|
||||
import "strings"
|
||||
|
||||
func EndpointForRegion(svcName, region string) string {
|
||||
derivedKeys := []string{
|
||||
region + "/" + svcName,
|
||||
region + "/*",
|
||||
"*/" + svcName,
|
||||
"*/*",
|
||||
}
|
||||
|
||||
for _, key := range derivedKeys {
|
||||
if val, ok := endpointsMap.Endpoints[key]; ok {
|
||||
ep := val.Endpoint
|
||||
ep = strings.Replace(ep, "{region}", region, -1)
|
||||
ep = strings.Replace(ep, "{service}", svcName, -1)
|
||||
return ep
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
67
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/endpoints/endpoints.json
сгенерированный
поставляемый
Обычный файл
67
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/endpoints/endpoints.json
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"version": 2,
|
||||
"endpoints": {
|
||||
"*/*": {
|
||||
"endpoint": "{service}.{region}.amazonaws.com"
|
||||
},
|
||||
"cn-north-1/*": {
|
||||
"endpoint": "{service}.{region}.amazonaws.com.cn",
|
||||
"signatureVersion": "v4"
|
||||
},
|
||||
"us-gov-west-1/iam": {
|
||||
"endpoint": "iam.us-gov.amazonaws.com"
|
||||
},
|
||||
"us-gov-west-1/sts": {
|
||||
"endpoint": "sts.us-gov-west-1.amazonaws.com"
|
||||
},
|
||||
"us-gov-west-1/s3": {
|
||||
"endpoint": "s3-{region}.amazonaws.com"
|
||||
},
|
||||
"*/cloudfront": {
|
||||
"endpoint": "cloudfront.amazonaws.com"
|
||||
},
|
||||
"*/iam": {
|
||||
"endpoint": "iam.amazonaws.com"
|
||||
},
|
||||
"*/importexport": {
|
||||
"endpoint": "importexport.amazonaws.com"
|
||||
},
|
||||
"*/route53": {
|
||||
"endpoint": "route53.amazonaws.com"
|
||||
},
|
||||
"*/sts": {
|
||||
"endpoint": "sts.amazonaws.com"
|
||||
},
|
||||
"us-east-1/sdb": {
|
||||
"endpoint": "sdb.amazonaws.com"
|
||||
},
|
||||
"us-east-1/s3": {
|
||||
"endpoint": "s3.amazonaws.com"
|
||||
},
|
||||
"us-west-1/s3": {
|
||||
"endpoint": "s3-{region}.amazonaws.com"
|
||||
},
|
||||
"us-west-2/s3": {
|
||||
"endpoint": "s3-{region}.amazonaws.com"
|
||||
},
|
||||
"eu-west-1/s3": {
|
||||
"endpoint": "s3-{region}.amazonaws.com"
|
||||
},
|
||||
"ap-southeast-1/s3": {
|
||||
"endpoint": "s3-{region}.amazonaws.com"
|
||||
},
|
||||
"ap-southeast-2/s3": {
|
||||
"endpoint": "s3-{region}.amazonaws.com"
|
||||
},
|
||||
"ap-northeast-1/s3": {
|
||||
"endpoint": "s3-{region}.amazonaws.com"
|
||||
},
|
||||
"sa-east-1/s3": {
|
||||
"endpoint": "s3-{region}.amazonaws.com"
|
||||
},
|
||||
"eu-central-1/s3": {
|
||||
"endpoint": "{service}.{region}.amazonaws.com",
|
||||
"signatureVersion": "v4"
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/endpoints/endpoints_map.go
сгенерированный
поставляемый
Обычный файл
78
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/endpoints/endpoints_map.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,78 @@
|
||||
package endpoints
|
||||
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||
|
||||
type endpointStruct struct {
|
||||
Version int
|
||||
Endpoints map[string]endpointEntry
|
||||
}
|
||||
|
||||
type endpointEntry struct {
|
||||
Endpoint string
|
||||
}
|
||||
|
||||
var endpointsMap = endpointStruct{
|
||||
Version: 2,
|
||||
Endpoints: map[string]endpointEntry{
|
||||
"*/*": endpointEntry{
|
||||
Endpoint: "{service}.{region}.amazonaws.com",
|
||||
},
|
||||
"*/cloudfront": endpointEntry{
|
||||
Endpoint: "cloudfront.amazonaws.com",
|
||||
},
|
||||
"*/iam": endpointEntry{
|
||||
Endpoint: "iam.amazonaws.com",
|
||||
},
|
||||
"*/importexport": endpointEntry{
|
||||
Endpoint: "importexport.amazonaws.com",
|
||||
},
|
||||
"*/route53": endpointEntry{
|
||||
Endpoint: "route53.amazonaws.com",
|
||||
},
|
||||
"*/sts": endpointEntry{
|
||||
Endpoint: "sts.amazonaws.com",
|
||||
},
|
||||
"ap-northeast-1/s3": endpointEntry{
|
||||
Endpoint: "s3-{region}.amazonaws.com",
|
||||
},
|
||||
"ap-southeast-1/s3": endpointEntry{
|
||||
Endpoint: "s3-{region}.amazonaws.com",
|
||||
},
|
||||
"ap-southeast-2/s3": endpointEntry{
|
||||
Endpoint: "s3-{region}.amazonaws.com",
|
||||
},
|
||||
"cn-north-1/*": endpointEntry{
|
||||
Endpoint: "{service}.{region}.amazonaws.com.cn",
|
||||
},
|
||||
"eu-central-1/s3": endpointEntry{
|
||||
Endpoint: "{service}.{region}.amazonaws.com",
|
||||
},
|
||||
"eu-west-1/s3": endpointEntry{
|
||||
Endpoint: "s3-{region}.amazonaws.com",
|
||||
},
|
||||
"sa-east-1/s3": endpointEntry{
|
||||
Endpoint: "s3-{region}.amazonaws.com",
|
||||
},
|
||||
"us-east-1/s3": endpointEntry{
|
||||
Endpoint: "s3.amazonaws.com",
|
||||
},
|
||||
"us-east-1/sdb": endpointEntry{
|
||||
Endpoint: "sdb.amazonaws.com",
|
||||
},
|
||||
"us-gov-west-1/iam": endpointEntry{
|
||||
Endpoint: "iam.us-gov.amazonaws.com",
|
||||
},
|
||||
"us-gov-west-1/s3": endpointEntry{
|
||||
Endpoint: "s3-{region}.amazonaws.com",
|
||||
},
|
||||
"us-gov-west-1/sts": endpointEntry{
|
||||
Endpoint: "sts.us-gov-west-1.amazonaws.com",
|
||||
},
|
||||
"us-west-1/s3": endpointEntry{
|
||||
Endpoint: "s3-{region}.amazonaws.com",
|
||||
},
|
||||
"us-west-2/s3": endpointEntry{
|
||||
Endpoint: "s3-{region}.amazonaws.com",
|
||||
},
|
||||
},
|
||||
}
|
||||
25
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/endpoints/endpoints_test.go
сгенерированный
поставляемый
Обычный файл
25
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/endpoints/endpoints_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,25 @@
|
||||
package endpoints
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGlobalEndpoints(t *testing.T) {
|
||||
region := "mock-region-1"
|
||||
svcs := []string{"cloudfront", "iam", "importexport", "route53", "sts"}
|
||||
|
||||
for _, name := range svcs {
|
||||
if EndpointForRegion(name, region) != name+".amazonaws.com" {
|
||||
t.Errorf("expected endpoint for %s to equal %s.amazonaws.com", name, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestServicesInCN(t *testing.T) {
|
||||
region := "cn-north-1"
|
||||
svcs := []string{"cloudfront", "iam", "importexport", "route53", "sts", "s3"}
|
||||
|
||||
for _, name := range svcs {
|
||||
if EndpointForRegion(name, region) != name+"."+region+".amazonaws.com.cn" {
|
||||
t.Errorf("expected endpoint for %s to equal %s.%s.amazonaws.com.cn", name, name, region)
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/build.go
сгенерированный
поставляемый
Обычный файл
30
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/build.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,30 @@
|
||||
package query
|
||||
|
||||
//go:generate go run ../../fixtures/protocol/generate.go ../../fixtures/protocol/input/query.json build_test.go
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
"github.com/awslabs/aws-sdk-go/internal/protocol/query/queryutil"
|
||||
)
|
||||
|
||||
func Build(r *aws.Request) {
|
||||
body := url.Values{
|
||||
"Action": {r.Operation.Name},
|
||||
"Version": {r.Service.APIVersion},
|
||||
}
|
||||
if err := queryutil.Parse(body, r.Params, false); err != nil {
|
||||
r.Error = err
|
||||
return
|
||||
}
|
||||
|
||||
if r.ExpireTime == 0 {
|
||||
r.HTTPRequest.Method = "POST"
|
||||
r.HTTPRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
||||
r.SetBufferBody([]byte(body.Encode()))
|
||||
} else { // This is a pre-signed request
|
||||
r.HTTPRequest.Method = "GET"
|
||||
r.HTTPRequest.URL.RawQuery = body.Encode()
|
||||
}
|
||||
}
|
||||
1167
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/build_test.go
сгенерированный
поставляемый
Обычный файл
1167
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/build_test.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
198
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/queryutil/queryutil.go
сгенерированный
поставляемый
Обычный файл
198
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/queryutil/queryutil.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,198 @@
|
||||
package queryutil
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Parse(body url.Values, i interface{}, isEC2 bool) error {
|
||||
q := queryParser{isEC2: isEC2}
|
||||
return q.parseValue(body, reflect.ValueOf(i), "", "")
|
||||
}
|
||||
|
||||
func elemOf(value reflect.Value) reflect.Value {
|
||||
for value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
type queryParser struct {
|
||||
isEC2 bool
|
||||
}
|
||||
|
||||
func (q *queryParser) parseValue(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
|
||||
value = elemOf(value)
|
||||
|
||||
// no need to handle zero values
|
||||
if !value.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
t := tag.Get("type")
|
||||
if t == "" {
|
||||
switch value.Kind() {
|
||||
case reflect.Struct:
|
||||
t = "structure"
|
||||
case reflect.Slice:
|
||||
t = "list"
|
||||
case reflect.Map:
|
||||
t = "map"
|
||||
}
|
||||
}
|
||||
|
||||
switch t {
|
||||
case "structure":
|
||||
return q.parseStruct(v, value, prefix)
|
||||
case "list":
|
||||
return q.parseList(v, value, prefix, tag)
|
||||
case "map":
|
||||
return q.parseMap(v, value, prefix, tag)
|
||||
default:
|
||||
return q.parseScalar(v, value, prefix, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix string) error {
|
||||
if !value.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
t := value.Type()
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
if c := t.Field(i).Name[0:1]; strings.ToLower(c) == c {
|
||||
continue // ignore unexported fields
|
||||
}
|
||||
|
||||
value := elemOf(value.Field(i))
|
||||
field := t.Field(i)
|
||||
var name string
|
||||
|
||||
if q.isEC2 {
|
||||
name = field.Tag.Get("queryName")
|
||||
}
|
||||
if name == "" {
|
||||
if field.Tag.Get("flattened") != "" && field.Tag.Get("locationNameList") != "" {
|
||||
name = field.Tag.Get("locationNameList")
|
||||
} else if locName := field.Tag.Get("locationName"); locName != "" {
|
||||
name = locName
|
||||
}
|
||||
if name != "" && q.isEC2 {
|
||||
name = strings.ToUpper(name[0:1]) + name[1:]
|
||||
}
|
||||
}
|
||||
if name == "" {
|
||||
name = field.Name
|
||||
}
|
||||
|
||||
if prefix != "" {
|
||||
name = prefix + "." + name
|
||||
}
|
||||
|
||||
if err := q.parseValue(v, value, name, field.Tag); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *queryParser) parseList(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
|
||||
// check for unflattened list member
|
||||
if !q.isEC2 && tag.Get("flattened") == "" {
|
||||
prefix += ".member"
|
||||
}
|
||||
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
slicePrefix := prefix
|
||||
if slicePrefix == "" {
|
||||
slicePrefix = strconv.Itoa(i + 1)
|
||||
} else {
|
||||
slicePrefix = slicePrefix + "." + strconv.Itoa(i+1)
|
||||
}
|
||||
if err := q.parseValue(v, value.Index(i), slicePrefix, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *queryParser) parseMap(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
|
||||
// check for unflattened list member
|
||||
if !q.isEC2 && tag.Get("flattened") == "" {
|
||||
prefix += ".entry"
|
||||
}
|
||||
|
||||
// sort keys for improved serialization consistency.
|
||||
// this is not strictly necessary for protocol support.
|
||||
mapKeyValues := value.MapKeys()
|
||||
mapKeys := map[string]reflect.Value{}
|
||||
mapKeyNames := make([]string, len(mapKeyValues))
|
||||
for i, mapKey := range mapKeyValues {
|
||||
name := mapKey.String()
|
||||
mapKeys[name] = mapKey
|
||||
mapKeyNames[i] = name
|
||||
}
|
||||
sort.Strings(mapKeyNames)
|
||||
|
||||
for i, mapKeyName := range mapKeyNames {
|
||||
mapKey := mapKeys[mapKeyName]
|
||||
mapValue := value.MapIndex(mapKey)
|
||||
|
||||
// serialize key
|
||||
var keyName string
|
||||
if prefix == "" {
|
||||
keyName = strconv.Itoa(i+1) + ".key"
|
||||
} else {
|
||||
keyName = prefix + "." + strconv.Itoa(i+1) + ".key"
|
||||
}
|
||||
|
||||
if err := q.parseValue(v, mapKey, keyName, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// serialize value
|
||||
var valueName string
|
||||
if prefix == "" {
|
||||
valueName = strconv.Itoa(i+1) + ".value"
|
||||
} else {
|
||||
valueName = prefix + "." + strconv.Itoa(i+1) + ".value"
|
||||
}
|
||||
|
||||
if err := q.parseValue(v, mapValue, valueName, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *queryParser) parseScalar(v url.Values, r reflect.Value, name string, tag reflect.StructTag) error {
|
||||
switch value := r.Interface().(type) {
|
||||
case string:
|
||||
v.Set(name, value)
|
||||
case []byte:
|
||||
v.Set(name, base64.StdEncoding.EncodeToString(value))
|
||||
case bool:
|
||||
v.Set(name, strconv.FormatBool(value))
|
||||
case int64:
|
||||
v.Set(name, strconv.FormatInt(value, 10))
|
||||
case int:
|
||||
v.Set(name, strconv.Itoa(value))
|
||||
case float64:
|
||||
v.Set(name, strconv.FormatFloat(value, 'f', -1, 64))
|
||||
case float32:
|
||||
v.Set(name, strconv.FormatFloat(float64(value), 'f', -1, 32))
|
||||
case time.Time:
|
||||
const ISO8601UTC = "2006-01-02T15:04:05Z"
|
||||
v.Set(name, value.UTC().Format(ISO8601UTC))
|
||||
default:
|
||||
return fmt.Errorf("unsupported value for param %s: %v (%s)", name, r.Interface(), r.Type().Name())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
26
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/unmarshal.go
сгенерированный
поставляемый
Обычный файл
26
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/unmarshal.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,26 @@
|
||||
package query
|
||||
|
||||
//go:generate go run ../../fixtures/protocol/generate.go ../../fixtures/protocol/output/query.json unmarshal_test.go
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
"github.com/awslabs/aws-sdk-go/internal/protocol/xml/xmlutil"
|
||||
)
|
||||
|
||||
func Unmarshal(r *aws.Request) {
|
||||
defer r.HTTPResponse.Body.Close()
|
||||
if r.DataFilled() {
|
||||
decoder := xml.NewDecoder(r.HTTPResponse.Body)
|
||||
err := xmlutil.UnmarshalXML(r.Data, decoder, r.Operation.Name+"Result")
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func UnmarshalMeta(r *aws.Request) {
|
||||
// TODO implement unmarshaling of request IDs
|
||||
}
|
||||
31
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/unmarshal_error.go
сгенерированный
поставляемый
Обычный файл
31
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/unmarshal_error.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,31 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
)
|
||||
|
||||
type xmlErrorResponse struct {
|
||||
XMLName xml.Name `xml:"ErrorResponse"`
|
||||
Code string `xml:"Error>Code"`
|
||||
Message string `xml:"Error>Message"`
|
||||
RequestID string `xml:"RequestId"`
|
||||
}
|
||||
|
||||
func UnmarshalError(r *aws.Request) {
|
||||
defer r.HTTPResponse.Body.Close()
|
||||
|
||||
resp := &xmlErrorResponse{}
|
||||
err := xml.NewDecoder(r.HTTPResponse.Body).Decode(resp)
|
||||
if err != nil && err != io.EOF {
|
||||
r.Error = err
|
||||
} else {
|
||||
r.Error = aws.APIError{
|
||||
StatusCode: r.HTTPResponse.StatusCode,
|
||||
Code: resp.Code,
|
||||
Message: resp.Message,
|
||||
}
|
||||
}
|
||||
}
|
||||
1361
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/unmarshal_test.go
сгенерированный
поставляемый
Обычный файл
1361
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/query/unmarshal_test.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
215
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/rest/build.go
сгенерированный
поставляемый
Обычный файл
215
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/rest/build.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,215 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
)
|
||||
|
||||
// RFC822 returns an RFC822 formatted timestamp for AWS protocols
|
||||
const RFC822 = "Mon, 2 Jan 2006 15:04:05 GMT"
|
||||
|
||||
func Build(r *aws.Request) {
|
||||
if r.ParamsFilled() {
|
||||
v := reflect.ValueOf(r.Params).Elem()
|
||||
buildLocationElements(r, v)
|
||||
buildBody(r, v)
|
||||
}
|
||||
}
|
||||
|
||||
func buildLocationElements(r *aws.Request, v reflect.Value) {
|
||||
query := r.HTTPRequest.URL.Query()
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
m := v.Field(i)
|
||||
if n := v.Type().Field(i).Name; n[0:1] == strings.ToLower(n[0:1]) {
|
||||
continue
|
||||
}
|
||||
|
||||
if m.IsValid() {
|
||||
field := v.Type().Field(i)
|
||||
name := field.Tag.Get("locationName")
|
||||
if name == "" {
|
||||
name = field.Name
|
||||
}
|
||||
if m.Kind() == reflect.Ptr {
|
||||
m = m.Elem()
|
||||
}
|
||||
if !m.IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
switch field.Tag.Get("location") {
|
||||
case "headers": // header maps
|
||||
buildHeaderMap(r, m, field.Tag.Get("locationName"))
|
||||
case "header":
|
||||
buildHeader(r, m, name)
|
||||
case "uri":
|
||||
buildURI(r, m, name)
|
||||
case "querystring":
|
||||
buildQueryString(r, m, name, query)
|
||||
}
|
||||
}
|
||||
if r.Error != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
r.HTTPRequest.URL.RawQuery = query.Encode()
|
||||
updatePath(r.HTTPRequest.URL, r.HTTPRequest.URL.Path)
|
||||
}
|
||||
|
||||
func buildBody(r *aws.Request, v reflect.Value) {
|
||||
if field, ok := v.Type().FieldByName("SDKShapeTraits"); ok {
|
||||
if payloadName := field.Tag.Get("payload"); payloadName != "" {
|
||||
pfield, _ := v.Type().FieldByName(payloadName)
|
||||
if ptag := pfield.Tag.Get("type"); ptag != "" && ptag != "structure" {
|
||||
payload := reflect.Indirect(v.FieldByName(payloadName))
|
||||
if payload.IsValid() && payload.Interface() != nil {
|
||||
switch reader := payload.Interface().(type) {
|
||||
case io.ReadSeeker:
|
||||
r.SetReaderBody(reader)
|
||||
case []byte:
|
||||
r.SetBufferBody(reader)
|
||||
case string:
|
||||
r.SetBufferBody([]byte(reader))
|
||||
default:
|
||||
r.Error = fmt.Errorf("unknown payload type %s", payload.Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildHeader(r *aws.Request, v reflect.Value, name string) {
|
||||
str, err := convertType(v)
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
} else if str != nil {
|
||||
r.HTTPRequest.Header.Add(name, *str)
|
||||
}
|
||||
}
|
||||
|
||||
func buildHeaderMap(r *aws.Request, v reflect.Value, prefix string) {
|
||||
for _, key := range v.MapKeys() {
|
||||
str, err := convertType(v.MapIndex(key))
|
||||
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
} else if str != nil {
|
||||
r.HTTPRequest.Header.Add(prefix+key.String(), *str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildURI(r *aws.Request, v reflect.Value, name string) {
|
||||
value, err := convertType(v)
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
} else if value != nil {
|
||||
uri := r.HTTPRequest.URL.Path
|
||||
uri = strings.Replace(uri, "{"+name+"}", escapePath(*value, true), -1)
|
||||
uri = strings.Replace(uri, "{"+name+"+}", escapePath(*value, false), -1)
|
||||
r.HTTPRequest.URL.Path = uri
|
||||
}
|
||||
}
|
||||
|
||||
func buildQueryString(r *aws.Request, v reflect.Value, name string, query url.Values) {
|
||||
str, err := convertType(v)
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
} else if str != nil {
|
||||
query.Set(name, *str)
|
||||
}
|
||||
}
|
||||
|
||||
func updatePath(url *url.URL, urlPath string) {
|
||||
scheme, query := url.Scheme, url.RawQuery
|
||||
|
||||
// clean up path
|
||||
urlPath = path.Clean(urlPath)
|
||||
|
||||
// get formatted URL minus scheme so we can build this into Opaque
|
||||
url.Scheme, url.Path, url.RawQuery = "", "", ""
|
||||
s := url.String()
|
||||
url.Scheme = scheme
|
||||
url.RawQuery = query
|
||||
|
||||
// build opaque URI
|
||||
url.Opaque = s + urlPath
|
||||
}
|
||||
|
||||
// Whether the byte value can be sent without escaping in AWS URLs
|
||||
var noEscape [256]bool
|
||||
var noEscapeInitialized = false
|
||||
|
||||
// initialise noEscape
|
||||
func initNoEscape() {
|
||||
for i := range noEscape {
|
||||
// Amazon expects every character except these escaped
|
||||
noEscape[i] = (i >= 'A' && i <= 'Z') ||
|
||||
(i >= 'a' && i <= 'z') ||
|
||||
(i >= '0' && i <= '9') ||
|
||||
i == '-' ||
|
||||
i == '.' ||
|
||||
i == '_' ||
|
||||
i == '~'
|
||||
}
|
||||
}
|
||||
|
||||
// escapePath escapes part of a URL path in Amazon style
|
||||
func escapePath(path string, encodeSep bool) string {
|
||||
if !noEscapeInitialized {
|
||||
initNoEscape()
|
||||
noEscapeInitialized = true
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(path); i++ {
|
||||
c := path[i]
|
||||
if noEscape[c] || (c == '/' && !encodeSep) {
|
||||
buf.WriteByte(c)
|
||||
} else {
|
||||
buf.WriteByte('%')
|
||||
buf.WriteString(strings.ToUpper(strconv.FormatUint(uint64(c), 16)))
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func convertType(v reflect.Value) (*string, error) {
|
||||
v = reflect.Indirect(v)
|
||||
if !v.IsValid() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var str string
|
||||
switch value := v.Interface().(type) {
|
||||
case string:
|
||||
str = value
|
||||
case []byte:
|
||||
str = base64.StdEncoding.EncodeToString(value)
|
||||
case bool:
|
||||
str = strconv.FormatBool(value)
|
||||
case int64:
|
||||
str = strconv.FormatInt(value, 10)
|
||||
case float64:
|
||||
str = strconv.FormatFloat(value, 'f', -1, 64)
|
||||
case time.Time:
|
||||
str = value.UTC().Format(RFC822)
|
||||
default:
|
||||
err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
|
||||
return nil, err
|
||||
}
|
||||
return &str, nil
|
||||
}
|
||||
43
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/rest/payload.go
сгенерированный
поставляемый
Обычный файл
43
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/rest/payload.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,43 @@
|
||||
package rest
|
||||
|
||||
import "reflect"
|
||||
|
||||
func PayloadMember(i interface{}) interface{} {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(i).Elem()
|
||||
if !v.IsValid() {
|
||||
return nil
|
||||
}
|
||||
if field, ok := v.Type().FieldByName("SDKShapeTraits"); ok {
|
||||
if payloadName := field.Tag.Get("payload"); payloadName != "" {
|
||||
field, _ := v.Type().FieldByName(payloadName)
|
||||
if field.Tag.Get("type") != "structure" {
|
||||
return nil
|
||||
}
|
||||
|
||||
payload := v.FieldByName(payloadName)
|
||||
if payload.IsValid() || (payload.Kind() == reflect.Ptr && !payload.IsNil()) {
|
||||
return payload.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func PayloadType(i interface{}) string {
|
||||
v := reflect.Indirect(reflect.ValueOf(i))
|
||||
if !v.IsValid() {
|
||||
return ""
|
||||
}
|
||||
if field, ok := v.Type().FieldByName("SDKShapeTraits"); ok {
|
||||
if payloadName := field.Tag.Get("payload"); payloadName != "" {
|
||||
if member, ok := v.Type().FieldByName(payloadName); ok {
|
||||
return member.Tag.Get("type")
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
174
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/rest/unmarshal.go
сгенерированный
поставляемый
Обычный файл
174
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/rest/unmarshal.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,174 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
)
|
||||
|
||||
func Unmarshal(r *aws.Request) {
|
||||
if r.DataFilled() {
|
||||
v := reflect.Indirect(reflect.ValueOf(r.Data))
|
||||
unmarshalBody(r, v)
|
||||
unmarshalLocationElements(r, v)
|
||||
}
|
||||
}
|
||||
|
||||
func unmarshalBody(r *aws.Request, v reflect.Value) {
|
||||
if field, ok := v.Type().FieldByName("SDKShapeTraits"); ok {
|
||||
if payloadName := field.Tag.Get("payload"); payloadName != "" {
|
||||
pfield, _ := v.Type().FieldByName(payloadName)
|
||||
if ptag := pfield.Tag.Get("type"); ptag != "" && ptag != "structure" {
|
||||
payload := reflect.Indirect(v.FieldByName(payloadName))
|
||||
if payload.IsValid() {
|
||||
switch payload.Interface().(type) {
|
||||
case []byte:
|
||||
b, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
} else {
|
||||
payload.Set(reflect.ValueOf(b))
|
||||
}
|
||||
case string:
|
||||
b, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
} else {
|
||||
payload.Set(reflect.ValueOf(string(b)))
|
||||
}
|
||||
default:
|
||||
switch payload.Type().String() {
|
||||
case "io.ReadSeeker":
|
||||
payload.Set(reflect.ValueOf(aws.ReadSeekCloser(r.HTTPResponse.Body)))
|
||||
case "aws.ReadSeekCloser", "io.ReadCloser":
|
||||
payload.Set(reflect.ValueOf(r.HTTPResponse.Body))
|
||||
default:
|
||||
r.Error = fmt.Errorf("unknown payload type %s", payload.Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unmarshalLocationElements(r *aws.Request, v reflect.Value) {
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
m, field := v.Field(i), v.Type().Field(i)
|
||||
if n := field.Name; n[0:1] == strings.ToLower(n[0:1]) {
|
||||
continue
|
||||
}
|
||||
|
||||
if m.IsValid() {
|
||||
name := field.Tag.Get("locationName")
|
||||
if name == "" {
|
||||
name = field.Name
|
||||
}
|
||||
|
||||
switch field.Tag.Get("location") {
|
||||
case "statusCode":
|
||||
unmarshalStatusCode(m, r.HTTPResponse.StatusCode)
|
||||
case "header":
|
||||
err := unmarshalHeader(m, r.HTTPResponse.Header.Get(name))
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
break
|
||||
}
|
||||
case "headers":
|
||||
prefix := field.Tag.Get("locationName")
|
||||
err := unmarshalHeaderMap(m, r.HTTPResponse.Header, prefix)
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.Error != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func unmarshalStatusCode(v reflect.Value, statusCode int) {
|
||||
if !v.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
switch v.Interface().(type) {
|
||||
case *int64:
|
||||
s := int64(statusCode)
|
||||
v.Set(reflect.ValueOf(&s))
|
||||
}
|
||||
}
|
||||
|
||||
func unmarshalHeaderMap(r reflect.Value, headers http.Header, prefix string) error {
|
||||
switch r.Interface().(type) {
|
||||
case *map[string]*string: // we only support string map value types
|
||||
out := map[string]*string{}
|
||||
for k, v := range headers {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
if strings.HasPrefix(strings.ToLower(k), strings.ToLower(prefix)) {
|
||||
out[k[len(prefix):]] = &v[0]
|
||||
}
|
||||
}
|
||||
r.Set(reflect.ValueOf(&out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalHeader(v reflect.Value, header string) error {
|
||||
if !v.IsValid() || (header == "" && v.Elem().Kind() != reflect.String) {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v.Interface().(type) {
|
||||
case *string:
|
||||
v.Set(reflect.ValueOf(&header))
|
||||
case []byte:
|
||||
b, err := base64.StdEncoding.DecodeString(header)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
v.Set(reflect.ValueOf(&b))
|
||||
}
|
||||
case *bool:
|
||||
b, err := strconv.ParseBool(header)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
v.Set(reflect.ValueOf(&b))
|
||||
}
|
||||
case *int64:
|
||||
i, err := strconv.ParseInt(header, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
v.Set(reflect.ValueOf(&i))
|
||||
}
|
||||
case *float64:
|
||||
f, err := strconv.ParseFloat(header, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
v.Set(reflect.ValueOf(&f))
|
||||
}
|
||||
case *time.Time:
|
||||
t, err := time.Parse(RFC822, header)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
v.Set(reflect.ValueOf(&t))
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
2571
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/restxml/build_test.go
сгенерированный
поставляемый
Обычный файл
2571
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/restxml/build_test.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
48
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/restxml/restxml.go
сгенерированный
поставляемый
Обычный файл
48
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/restxml/restxml.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,48 @@
|
||||
package restxml
|
||||
|
||||
//go:generate go run ../../fixtures/protocol/generate.go ../../fixtures/protocol/input/rest-xml.json build_test.go
|
||||
//go:generate go run ../../fixtures/protocol/generate.go ../../fixtures/protocol/output/rest-xml.json unmarshal_test.go
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
"github.com/awslabs/aws-sdk-go/internal/protocol/query"
|
||||
"github.com/awslabs/aws-sdk-go/internal/protocol/rest"
|
||||
"github.com/awslabs/aws-sdk-go/internal/protocol/xml/xmlutil"
|
||||
)
|
||||
|
||||
func Build(r *aws.Request) {
|
||||
rest.Build(r)
|
||||
|
||||
if t := rest.PayloadType(r.Params); t == "structure" || t == "" {
|
||||
var buf bytes.Buffer
|
||||
err := xmlutil.BuildXML(r.Params, xml.NewEncoder(&buf))
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
return
|
||||
}
|
||||
r.SetBufferBody(buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func Unmarshal(r *aws.Request) {
|
||||
if t := rest.PayloadType(r.Data); t == "structure" || t == "" {
|
||||
defer r.HTTPResponse.Body.Close()
|
||||
decoder := xml.NewDecoder(r.HTTPResponse.Body)
|
||||
err := xmlutil.UnmarshalXML(r.Data, decoder, "")
|
||||
if err != nil {
|
||||
r.Error = err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func UnmarshalMeta(r *aws.Request) {
|
||||
rest.Unmarshal(r)
|
||||
}
|
||||
|
||||
func UnmarshalError(r *aws.Request) {
|
||||
query.UnmarshalError(r)
|
||||
}
|
||||
1171
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/restxml/unmarshal_test.go
сгенерированный
поставляемый
Обычный файл
1171
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/restxml/unmarshal_test.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
262
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/xml/xmlutil/build.go
сгенерированный
поставляемый
Обычный файл
262
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/xml/xmlutil/build.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,262 @@
|
||||
package xmlutil
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func BuildXML(params interface{}, e *xml.Encoder) error {
|
||||
b := xmlBuilder{encoder: e, namespaces: map[string]string{}}
|
||||
root := NewXMLElement(xml.Name{})
|
||||
if err := b.buildValue(reflect.ValueOf(params), root, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range root.Children {
|
||||
for _, v := range c {
|
||||
return StructToXML(e, v, false)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func elemOf(value reflect.Value) reflect.Value {
|
||||
for value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
type xmlBuilder struct {
|
||||
encoder *xml.Encoder
|
||||
namespaces map[string]string
|
||||
}
|
||||
|
||||
func (b *xmlBuilder) buildValue(value reflect.Value, current *XMLNode, tag reflect.StructTag) error {
|
||||
value = elemOf(value)
|
||||
if !value.IsValid() { // no need to handle zero values
|
||||
return nil
|
||||
} else if tag.Get("location") != "" { // don't handle non-body location values
|
||||
return nil
|
||||
}
|
||||
|
||||
t := tag.Get("type")
|
||||
if t == "" {
|
||||
switch value.Kind() {
|
||||
case reflect.Struct:
|
||||
t = "structure"
|
||||
case reflect.Slice:
|
||||
t = "list"
|
||||
case reflect.Map:
|
||||
t = "map"
|
||||
}
|
||||
}
|
||||
|
||||
switch t {
|
||||
case "structure":
|
||||
if field, ok := value.Type().FieldByName("SDKShapeTraits"); ok {
|
||||
tag = tag + reflect.StructTag(" ") + field.Tag
|
||||
}
|
||||
return b.buildStruct(value, current, tag)
|
||||
case "list":
|
||||
return b.buildList(value, current, tag)
|
||||
case "map":
|
||||
return b.buildMap(value, current, tag)
|
||||
default:
|
||||
return b.buildScalar(value, current, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag reflect.StructTag) error {
|
||||
if !value.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
fieldAdded := false
|
||||
|
||||
// unwrap payloads
|
||||
if payload := tag.Get("payload"); payload != "" {
|
||||
field, _ := value.Type().FieldByName(payload)
|
||||
tag = field.Tag
|
||||
value = elemOf(value.FieldByName(payload))
|
||||
|
||||
if !value.IsValid() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
child := NewXMLElement(xml.Name{Local: tag.Get("locationName")})
|
||||
|
||||
// there is an xmlNamespace associated with this struct
|
||||
if prefix, uri := tag.Get("xmlPrefix"), tag.Get("xmlURI"); uri != "" {
|
||||
ns := xml.Attr{
|
||||
Name: xml.Name{Local: "xmlns"},
|
||||
Value: uri,
|
||||
}
|
||||
if prefix != "" {
|
||||
b.namespaces[prefix] = uri // register the namespace
|
||||
ns.Name.Local = "xmlns:" + prefix
|
||||
}
|
||||
|
||||
child.Attr = append(child.Attr, ns)
|
||||
}
|
||||
|
||||
t := value.Type()
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
if c := t.Field(i).Name[0:1]; strings.ToLower(c) == c {
|
||||
continue // ignore unexported fields
|
||||
}
|
||||
|
||||
member := elemOf(value.Field(i))
|
||||
field := t.Field(i)
|
||||
mTag := field.Tag
|
||||
|
||||
if mTag.Get("location") != "" { // skip non-body members
|
||||
continue
|
||||
}
|
||||
|
||||
memberName := mTag.Get("locationName")
|
||||
if memberName == "" {
|
||||
memberName = field.Name
|
||||
mTag = reflect.StructTag(string(mTag) + ` locationName:"` + memberName + `"`)
|
||||
}
|
||||
if err := b.buildValue(member, child, mTag); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fieldAdded = true
|
||||
}
|
||||
|
||||
if fieldAdded { // only append this child if we have one ore more valid members
|
||||
current.AddChild(child)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *xmlBuilder) buildList(value reflect.Value, current *XMLNode, tag reflect.StructTag) error {
|
||||
if value.IsNil() { // don't build omitted lists
|
||||
return nil
|
||||
}
|
||||
|
||||
// check for unflattened list member
|
||||
flattened := tag.Get("flattened") != ""
|
||||
|
||||
xname := xml.Name{Local: tag.Get("locationName")}
|
||||
if flattened {
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
child := NewXMLElement(xname)
|
||||
current.AddChild(child)
|
||||
if err := b.buildValue(value.Index(i), child, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
list := NewXMLElement(xname)
|
||||
current.AddChild(list)
|
||||
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
iname := tag.Get("locationNameList")
|
||||
if iname == "" {
|
||||
iname = "member"
|
||||
}
|
||||
|
||||
child := NewXMLElement(xml.Name{Local: iname})
|
||||
list.AddChild(child)
|
||||
if err := b.buildValue(value.Index(i), child, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *xmlBuilder) buildMap(value reflect.Value, current *XMLNode, tag reflect.StructTag) error {
|
||||
if value.IsNil() { // don't build omitted maps
|
||||
return nil
|
||||
}
|
||||
|
||||
maproot := NewXMLElement(xml.Name{Local: tag.Get("locationName")})
|
||||
current.AddChild(maproot)
|
||||
current = maproot
|
||||
|
||||
kname, vname := "key", "value"
|
||||
if n := tag.Get("locationNameKey"); n != "" {
|
||||
kname = n
|
||||
}
|
||||
if n := tag.Get("locationNameValue"); n != "" {
|
||||
vname = n
|
||||
}
|
||||
|
||||
// sorting is not required for compliance, but it makes testing easier
|
||||
keys := make([]string, value.Len())
|
||||
for i, k := range value.MapKeys() {
|
||||
keys[i] = k.String()
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
v := value.MapIndex(reflect.ValueOf(k))
|
||||
fmt.Println(k, v.Interface())
|
||||
|
||||
mapcur := current
|
||||
if tag.Get("flattened") == "" { // add "entry" tag to non-flat maps
|
||||
child := NewXMLElement(xml.Name{Local: "entry"})
|
||||
mapcur.AddChild(child)
|
||||
mapcur = child
|
||||
}
|
||||
|
||||
kchild := NewXMLElement(xml.Name{Local: kname})
|
||||
kchild.Text = k
|
||||
vchild := NewXMLElement(xml.Name{Local: vname})
|
||||
mapcur.AddChild(kchild)
|
||||
mapcur.AddChild(vchild)
|
||||
|
||||
if err := b.buildValue(v, vchild, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *xmlBuilder) buildScalar(value reflect.Value, current *XMLNode, tag reflect.StructTag) error {
|
||||
var str string
|
||||
switch converted := value.Interface().(type) {
|
||||
case string:
|
||||
str = converted
|
||||
case []byte:
|
||||
str = base64.StdEncoding.EncodeToString(converted)
|
||||
case bool:
|
||||
str = strconv.FormatBool(converted)
|
||||
case int64:
|
||||
str = strconv.FormatInt(converted, 10)
|
||||
case int:
|
||||
str = strconv.Itoa(converted)
|
||||
case float64:
|
||||
str = strconv.FormatFloat(converted, 'f', -1, 64)
|
||||
case float32:
|
||||
str = strconv.FormatFloat(float64(converted), 'f', -1, 32)
|
||||
case time.Time:
|
||||
const ISO8601UTC = "2006-01-02T15:04:05Z"
|
||||
str = converted.UTC().Format(ISO8601UTC)
|
||||
default:
|
||||
return fmt.Errorf("unsupported value for param %s: %v (%s)",
|
||||
tag.Get("locationName"), value.Interface(), value.Type().Name())
|
||||
}
|
||||
|
||||
xname := xml.Name{Local: tag.Get("locationName")}
|
||||
if tag.Get("xmlAttribute") != "" { // put into current node's attribute list
|
||||
attr := xml.Attr{Name: xname, Value: str}
|
||||
current.Attr = append(current.Attr, attr)
|
||||
} else { // regular text node
|
||||
current.AddChild(&XMLNode{Name: xname, Text: str})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
251
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/xml/xmlutil/unmarshal.go
сгенерированный
поставляемый
Обычный файл
251
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/xml/xmlutil/unmarshal.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,251 @@
|
||||
package xmlutil
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func UnmarshalXML(v interface{}, d *xml.Decoder, wrapper string) error {
|
||||
n, _ := XMLToStruct(d, nil)
|
||||
if n.Children != nil {
|
||||
for _, root := range n.Children {
|
||||
for _, c := range root {
|
||||
if wrappedChild, ok := c.Children[wrapper]; ok {
|
||||
c = wrappedChild[0] // pull out wrapped element
|
||||
}
|
||||
|
||||
err := parse(reflect.ValueOf(v), c, "")
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parse(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||
rtype := r.Type()
|
||||
if rtype.Kind() == reflect.Ptr {
|
||||
rtype = rtype.Elem() // check kind of actual element type
|
||||
}
|
||||
|
||||
t := tag.Get("type")
|
||||
if t == "" {
|
||||
switch rtype.Kind() {
|
||||
case reflect.Struct:
|
||||
t = "structure"
|
||||
case reflect.Slice:
|
||||
t = "list"
|
||||
case reflect.Map:
|
||||
t = "map"
|
||||
}
|
||||
}
|
||||
|
||||
switch t {
|
||||
case "structure":
|
||||
if field, ok := rtype.FieldByName("SDKShapeTraits"); ok {
|
||||
tag = field.Tag
|
||||
}
|
||||
return parseStruct(r, node, tag)
|
||||
case "list":
|
||||
return parseList(r, node, tag)
|
||||
case "map":
|
||||
return parseMap(r, node, tag)
|
||||
default:
|
||||
return parseScalar(r, node, tag)
|
||||
}
|
||||
}
|
||||
|
||||
func parseStruct(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||
t := r.Type()
|
||||
if r.Kind() == reflect.Ptr {
|
||||
if r.IsNil() { // create the structure if it's nil
|
||||
s := reflect.New(r.Type().Elem())
|
||||
r.Set(s)
|
||||
r = s
|
||||
}
|
||||
|
||||
r = r.Elem()
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
// unwrap any payloads
|
||||
if payload := tag.Get("payload"); payload != "" {
|
||||
field, _ := t.FieldByName(payload)
|
||||
return parseStruct(r.FieldByName(payload), node, field.Tag)
|
||||
}
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if c := field.Name[0:1]; strings.ToLower(c) == c {
|
||||
continue // ignore unexported fields
|
||||
}
|
||||
|
||||
// figure out what this field is called
|
||||
name := field.Name
|
||||
if field.Tag.Get("flattened") != "" && field.Tag.Get("locationNameList") != "" {
|
||||
name = field.Tag.Get("locationNameList")
|
||||
} else if locName := field.Tag.Get("locationName"); locName != "" {
|
||||
name = locName
|
||||
}
|
||||
|
||||
// try to find the field by name in elements
|
||||
elems := node.Children[name]
|
||||
|
||||
if elems == nil { // try to find the field in attributes
|
||||
for _, a := range node.Attr {
|
||||
if name == a.Name.Local {
|
||||
// turn this into a text node for de-serializing
|
||||
elems = []*XMLNode{&XMLNode{Text: a.Value}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
member := r.FieldByName(field.Name)
|
||||
for _, elem := range elems {
|
||||
err := parse(member, elem, field.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseList(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||
t := r.Type()
|
||||
|
||||
if tag.Get("flattened") == "" { // look at all item entries
|
||||
mname := "member"
|
||||
if name := tag.Get("locationNameList"); name != "" {
|
||||
mname = name
|
||||
}
|
||||
|
||||
if Children, ok := node.Children[mname]; ok {
|
||||
if r.IsNil() {
|
||||
r.Set(reflect.MakeSlice(t, len(Children), len(Children)))
|
||||
}
|
||||
|
||||
for i, c := range Children {
|
||||
err := parse(r.Index(i), c, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // flattened list means this is a single element
|
||||
if r.IsNil() {
|
||||
r.Set(reflect.MakeSlice(t, 0, 0))
|
||||
}
|
||||
|
||||
childR := reflect.Zero(t.Elem())
|
||||
r.Set(reflect.Append(r, childR))
|
||||
err := parse(r.Index(r.Len()-1), node, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseMap(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||
t := r.Type()
|
||||
if r.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
if r.IsNil() {
|
||||
r.Set(reflect.New(t))
|
||||
r.Elem().Set(reflect.MakeMap(t))
|
||||
}
|
||||
|
||||
r = r.Elem()
|
||||
}
|
||||
|
||||
if tag.Get("flattened") == "" { // look at all child entries
|
||||
for _, entry := range node.Children["entry"] {
|
||||
parseMapEntry(r, entry, tag)
|
||||
}
|
||||
} else { // this element is itself an entry
|
||||
parseMapEntry(r, node, tag)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseMapEntry(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||
kname, vname := "key", "value"
|
||||
if n := tag.Get("locationNameKey"); n != "" {
|
||||
kname = n
|
||||
}
|
||||
if n := tag.Get("locationNameValue"); n != "" {
|
||||
vname = n
|
||||
}
|
||||
|
||||
keys, ok := node.Children[kname]
|
||||
values := node.Children[vname]
|
||||
if ok {
|
||||
for i, key := range keys {
|
||||
keyR := reflect.ValueOf(key.Text)
|
||||
value := values[i]
|
||||
valueR := reflect.New(r.Type().Elem()).Elem()
|
||||
|
||||
parse(valueR, value, "")
|
||||
r.SetMapIndex(keyR, valueR)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseScalar(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
||||
switch r.Interface().(type) {
|
||||
case *string:
|
||||
r.Set(reflect.ValueOf(&node.Text))
|
||||
return nil
|
||||
case []byte:
|
||||
b, err := base64.StdEncoding.DecodeString(node.Text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Set(reflect.ValueOf(b))
|
||||
case *bool:
|
||||
v, err := strconv.ParseBool(node.Text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Set(reflect.ValueOf(&v))
|
||||
case *int64:
|
||||
v, err := strconv.ParseInt(node.Text, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Set(reflect.ValueOf(&v))
|
||||
case *float64:
|
||||
v, err := strconv.ParseFloat(node.Text, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Set(reflect.ValueOf(&v))
|
||||
case *time.Time:
|
||||
const ISO8601UTC = "2006-01-02T15:04:05Z"
|
||||
t, err := time.Parse(ISO8601UTC, node.Text)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
r.Set(reflect.ValueOf(&t))
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported value: %v (%s)", r.Interface(), r.Type())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
100
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/xml/xmlutil/xml_to_struct.go
сгенерированный
поставляемый
Обычный файл
100
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/protocol/xml/xmlutil/xml_to_struct.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,100 @@
|
||||
package xmlutil
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type XMLNode struct {
|
||||
Name xml.Name `json:",omitempty"`
|
||||
Children map[string][]*XMLNode `json:",omitempty"`
|
||||
Text string `json:",omitempty"`
|
||||
Attr []xml.Attr `json:",omitempty"`
|
||||
}
|
||||
|
||||
func NewXMLElement(name xml.Name) *XMLNode {
|
||||
return &XMLNode{
|
||||
Name: name,
|
||||
Children: map[string][]*XMLNode{},
|
||||
Attr: []xml.Attr{},
|
||||
}
|
||||
}
|
||||
|
||||
func (n *XMLNode) AddChild(child *XMLNode) {
|
||||
if _, ok := n.Children[child.Name.Local]; !ok {
|
||||
n.Children[child.Name.Local] = []*XMLNode{}
|
||||
}
|
||||
n.Children[child.Name.Local] = append(n.Children[child.Name.Local], child)
|
||||
}
|
||||
|
||||
func XMLToStruct(d *xml.Decoder, s *xml.StartElement) (*XMLNode, error) {
|
||||
out := &XMLNode{}
|
||||
for {
|
||||
tok, err := d.Token()
|
||||
if tok == nil || err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
switch typed := tok.(type) {
|
||||
case xml.CharData:
|
||||
out.Text = string(typed.Copy())
|
||||
case xml.StartElement:
|
||||
el := typed.Copy()
|
||||
out.Attr = el.Attr
|
||||
if out.Children == nil {
|
||||
out.Children = map[string][]*XMLNode{}
|
||||
}
|
||||
|
||||
name := typed.Name.Local
|
||||
slice := out.Children[name]
|
||||
if slice == nil {
|
||||
slice = []*XMLNode{}
|
||||
}
|
||||
node, e := XMLToStruct(d, &el)
|
||||
if e != nil {
|
||||
return out, e
|
||||
}
|
||||
node.Name = typed.Name
|
||||
slice = append(slice, node)
|
||||
out.Children[name] = slice
|
||||
case xml.EndElement:
|
||||
if s != nil && s.Name.Local == typed.Name.Local { // matching end token
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func StructToXML(e *xml.Encoder, node *XMLNode, sorted bool) error {
|
||||
e.EncodeToken(xml.StartElement{Name: node.Name, Attr: node.Attr})
|
||||
|
||||
if node.Text != "" {
|
||||
e.EncodeToken(xml.CharData([]byte(node.Text)))
|
||||
} else if sorted {
|
||||
sortedNames := []string{}
|
||||
for k, _ := range node.Children {
|
||||
sortedNames = append(sortedNames, k)
|
||||
}
|
||||
sort.Strings(sortedNames)
|
||||
|
||||
for _, k := range sortedNames {
|
||||
for _, v := range node.Children[k] {
|
||||
StructToXML(e, v, sorted)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, c := range node.Children {
|
||||
for _, v := range c {
|
||||
StructToXML(e, v, sorted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
e.EncodeToken(xml.EndElement{Name: node.Name})
|
||||
return e.Flush()
|
||||
}
|
||||
296
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/signer/v4/v4.go
сгенерированный
поставляемый
Обычный файл
296
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/signer/v4/v4.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,296 @@
|
||||
package v4
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
)
|
||||
|
||||
const (
|
||||
authHeaderPrefix = "AWS4-HMAC-SHA256"
|
||||
timeFormat = "20060102T150405Z"
|
||||
shortTimeFormat = "20060102"
|
||||
)
|
||||
|
||||
var ignoredHeaders = map[string]bool{
|
||||
"Authorization": true,
|
||||
"Content-Type": true,
|
||||
"Content-Length": true,
|
||||
"User-Agent": true,
|
||||
}
|
||||
|
||||
type signer struct {
|
||||
Request *http.Request
|
||||
Time time.Time
|
||||
ExpireTime time.Duration
|
||||
ServiceName string
|
||||
Region string
|
||||
AccessKeyID string
|
||||
SecretAccessKey string
|
||||
SessionToken string
|
||||
Query url.Values
|
||||
Body io.ReadSeeker
|
||||
Debug uint
|
||||
Logger io.Writer
|
||||
|
||||
isPresign bool
|
||||
formattedTime string
|
||||
formattedShortTime string
|
||||
|
||||
signedHeaders string
|
||||
canonicalHeaders string
|
||||
canonicalString string
|
||||
credentialString string
|
||||
stringToSign string
|
||||
signature string
|
||||
authorization string
|
||||
}
|
||||
|
||||
// Sign requests with signature version 4.
|
||||
func Sign(req *aws.Request) {
|
||||
creds, err := req.Service.Config.Credentials.Credentials()
|
||||
if err != nil {
|
||||
req.Error = err
|
||||
return
|
||||
}
|
||||
|
||||
s := signer{
|
||||
Request: req.HTTPRequest,
|
||||
Time: req.Time,
|
||||
ExpireTime: req.ExpireTime,
|
||||
Query: req.HTTPRequest.URL.Query(),
|
||||
Body: req.Body,
|
||||
ServiceName: req.Service.ServiceName,
|
||||
Region: req.Service.Config.Region,
|
||||
AccessKeyID: creds.AccessKeyID,
|
||||
SecretAccessKey: creds.SecretAccessKey,
|
||||
SessionToken: creds.SessionToken,
|
||||
Debug: req.Service.Config.LogLevel,
|
||||
Logger: req.Service.Config.Logger,
|
||||
}
|
||||
s.sign()
|
||||
return
|
||||
}
|
||||
|
||||
func (v4 *signer) sign() {
|
||||
if v4.ExpireTime != 0 {
|
||||
v4.isPresign = true
|
||||
}
|
||||
|
||||
if v4.isPresign {
|
||||
v4.Query.Set("X-Amz-Algorithm", authHeaderPrefix)
|
||||
if v4.SessionToken != "" {
|
||||
v4.Query.Set("X-Amz-Security-Token", v4.SessionToken)
|
||||
} else {
|
||||
v4.Query.Del("X-Amz-Security-Token")
|
||||
}
|
||||
} else if v4.SessionToken != "" {
|
||||
v4.Request.Header.Set("X-Amz-Security-Token", v4.SessionToken)
|
||||
}
|
||||
|
||||
v4.build()
|
||||
|
||||
if v4.Debug > 0 {
|
||||
out := v4.Logger
|
||||
fmt.Fprintf(out, "---[ CANONICAL STRING ]-----------------------------\n")
|
||||
fmt.Fprintln(out, v4.canonicalString)
|
||||
fmt.Fprintf(out, "---[ STRING TO SIGN ]--------------------------------\n")
|
||||
fmt.Fprintln(out, v4.stringToSign)
|
||||
fmt.Fprintf(out, "---[ SIGNED URL ]--------------------------------\n")
|
||||
fmt.Fprintln(out, v4.Request.URL)
|
||||
fmt.Fprintf(out, "-----------------------------------------------------\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (v4 *signer) build() {
|
||||
v4.buildTime() // no depends
|
||||
v4.buildCredentialString() // no depends
|
||||
if v4.isPresign {
|
||||
v4.buildQuery() // no depends
|
||||
}
|
||||
v4.buildCanonicalHeaders() // depends on cred string
|
||||
v4.buildCanonicalString() // depends on canon headers / signed headers
|
||||
v4.buildStringToSign() // depends on canon string
|
||||
v4.buildSignature() // depends on string to sign
|
||||
|
||||
if v4.isPresign {
|
||||
v4.Request.URL.RawQuery += "&X-Amz-Signature=" + v4.signature
|
||||
} else {
|
||||
parts := []string{
|
||||
authHeaderPrefix + " Credential=" + v4.AccessKeyID + "/" + v4.credentialString,
|
||||
"SignedHeaders=" + v4.signedHeaders,
|
||||
"Signature=" + v4.signature,
|
||||
}
|
||||
v4.Request.Header.Set("Authorization", strings.Join(parts, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
func (v4 *signer) buildTime() {
|
||||
v4.formattedTime = v4.Time.UTC().Format(timeFormat)
|
||||
v4.formattedShortTime = v4.Time.UTC().Format(shortTimeFormat)
|
||||
|
||||
if v4.isPresign {
|
||||
duration := int64(v4.ExpireTime / time.Second)
|
||||
v4.Query.Set("X-Amz-Date", v4.formattedTime)
|
||||
v4.Query.Set("X-Amz-Expires", strconv.FormatInt(duration, 10))
|
||||
} else {
|
||||
v4.Request.Header.Set("X-Amz-Date", v4.formattedTime)
|
||||
}
|
||||
}
|
||||
|
||||
func (v4 *signer) buildCredentialString() {
|
||||
v4.credentialString = strings.Join([]string{
|
||||
v4.formattedShortTime,
|
||||
v4.Region,
|
||||
v4.ServiceName,
|
||||
"aws4_request",
|
||||
}, "/")
|
||||
|
||||
if v4.isPresign {
|
||||
v4.Query.Set("X-Amz-Credential", v4.AccessKeyID+"/"+v4.credentialString)
|
||||
}
|
||||
}
|
||||
|
||||
func (v4 *signer) buildQuery() {
|
||||
for k, h := range v4.Request.Header {
|
||||
if strings.HasPrefix(http.CanonicalHeaderKey(k), "X-Amz-") {
|
||||
continue // never hoist x-amz-* headers, they must be signed
|
||||
}
|
||||
if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
|
||||
continue // never hoist ignored headers
|
||||
}
|
||||
|
||||
v4.Request.Header.Del(k)
|
||||
v4.Query.Del(k)
|
||||
for _, v := range h {
|
||||
v4.Query.Add(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v4 *signer) buildCanonicalHeaders() {
|
||||
headers := make([]string, 0)
|
||||
headers = append(headers, "host")
|
||||
for k, _ := range v4.Request.Header {
|
||||
if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
|
||||
continue // ignored header
|
||||
}
|
||||
headers = append(headers, strings.ToLower(k))
|
||||
}
|
||||
sort.Strings(headers)
|
||||
|
||||
v4.signedHeaders = strings.Join(headers, ";")
|
||||
|
||||
if v4.isPresign {
|
||||
v4.Query.Set("X-Amz-SignedHeaders", v4.signedHeaders)
|
||||
}
|
||||
|
||||
headerValues := make([]string, len(headers))
|
||||
for i, k := range headers {
|
||||
if k == "host" {
|
||||
headerValues[i] = "host:" + v4.Request.URL.Host
|
||||
} else {
|
||||
headerValues[i] = k + ":" +
|
||||
strings.Join(v4.Request.Header[http.CanonicalHeaderKey(k)], ",")
|
||||
}
|
||||
}
|
||||
|
||||
v4.canonicalHeaders = strings.Join(headerValues, "\n")
|
||||
}
|
||||
|
||||
func (v4 *signer) buildCanonicalString() {
|
||||
v4.Request.URL.RawQuery = v4.Query.Encode()
|
||||
uri := v4.Request.URL.Opaque
|
||||
if uri != "" {
|
||||
uri = "/" + strings.Join(strings.Split(uri, "/")[3:], "/")
|
||||
} else {
|
||||
uri = v4.Request.URL.Path
|
||||
}
|
||||
if uri == "" {
|
||||
uri = "/"
|
||||
}
|
||||
|
||||
v4.canonicalString = strings.Join([]string{
|
||||
v4.Request.Method,
|
||||
uri,
|
||||
v4.Request.URL.RawQuery,
|
||||
v4.canonicalHeaders + "\n",
|
||||
v4.signedHeaders,
|
||||
v4.bodyDigest(),
|
||||
}, "\n")
|
||||
}
|
||||
|
||||
func (v4 *signer) buildStringToSign() {
|
||||
v4.stringToSign = strings.Join([]string{
|
||||
authHeaderPrefix,
|
||||
v4.formattedTime,
|
||||
v4.credentialString,
|
||||
hex.EncodeToString(makeSha256([]byte(v4.canonicalString))),
|
||||
}, "\n")
|
||||
}
|
||||
|
||||
func (v4 *signer) buildSignature() {
|
||||
secret := v4.SecretAccessKey
|
||||
date := makeHmac([]byte("AWS4"+secret), []byte(v4.formattedShortTime))
|
||||
region := makeHmac(date, []byte(v4.Region))
|
||||
service := makeHmac(region, []byte(v4.ServiceName))
|
||||
credentials := makeHmac(service, []byte("aws4_request"))
|
||||
signature := makeHmac(credentials, []byte(v4.stringToSign))
|
||||
v4.signature = hex.EncodeToString(signature)
|
||||
}
|
||||
|
||||
func (v4 *signer) bodyDigest() string {
|
||||
hash := v4.Request.Header.Get("X-Amz-Content-Sha256")
|
||||
if hash == "" {
|
||||
if v4.isPresign && v4.ServiceName == "s3" {
|
||||
hash = "UNSIGNED-PAYLOAD"
|
||||
} else if v4.Body == nil {
|
||||
hash = hex.EncodeToString(makeSha256([]byte{}))
|
||||
} else {
|
||||
hash = hex.EncodeToString(makeSha256Reader(v4.Body))
|
||||
}
|
||||
v4.Request.Header.Add("X-Amz-Content-Sha256", hash)
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
func makeHmac(key []byte, data []byte) []byte {
|
||||
hash := hmac.New(sha256.New, key)
|
||||
hash.Write(data)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
func makeSha256(data []byte) []byte {
|
||||
hash := sha256.New()
|
||||
hash.Write(data)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
func makeSha256Reader(reader io.ReadSeeker) []byte {
|
||||
packet := make([]byte, 4096)
|
||||
hash := sha256.New()
|
||||
|
||||
reader.Seek(0, 0)
|
||||
for {
|
||||
n, err := reader.Read(packet)
|
||||
if n > 0 {
|
||||
hash.Write(packet[0:n])
|
||||
}
|
||||
if err == io.EOF || n == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
reader.Seek(0, 0)
|
||||
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
89
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/signer/v4/v4_test.go
сгенерированный
поставляемый
Обычный файл
89
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/internal/signer/v4/v4_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,89 @@
|
||||
package v4
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func buildSigner(serviceName string, region string, signTime time.Time, expireTime time.Duration, body string) signer {
|
||||
endpoint := "https://" + serviceName + "." + region + ".amazonaws.com"
|
||||
reader := strings.NewReader(body)
|
||||
req, _ := http.NewRequest("POST", endpoint, reader)
|
||||
req.URL.Opaque = "//example.org/bucket/key-._~,!@#$%^&*()"
|
||||
req.Header.Add("X-Amz-Target", "prefix.Operation")
|
||||
req.Header.Add("Content-Type", "application/x-amz-json-1.0")
|
||||
req.Header.Add("Content-Length", string(len(body)))
|
||||
req.Header.Add("X-Amz-Meta-Other-Header", "some-value=!@#$%^&* ()")
|
||||
|
||||
return signer{
|
||||
Request: req,
|
||||
Time: signTime,
|
||||
ExpireTime: expireTime,
|
||||
Query: req.URL.Query(),
|
||||
Body: reader,
|
||||
ServiceName: serviceName,
|
||||
Region: region,
|
||||
AccessKeyID: "AKID",
|
||||
SecretAccessKey: "SECRET",
|
||||
SessionToken: "SESSION",
|
||||
}
|
||||
}
|
||||
|
||||
func removeWS(text string) string {
|
||||
text = strings.Replace(text, " ", "", -1)
|
||||
text = strings.Replace(text, "\n", "", -1)
|
||||
text = strings.Replace(text, "\t", "", -1)
|
||||
return text
|
||||
}
|
||||
|
||||
func assertEqual(t *testing.T, expected, given string) {
|
||||
if removeWS(expected) != removeWS(given) {
|
||||
t.Errorf("\nExpected: %s\nGiven: %s", expected, given)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPresignRequest(t *testing.T) {
|
||||
signer := buildSigner("dynamodb", "us-east-1", time.Unix(0, 0), 300*time.Second, "{}")
|
||||
signer.sign()
|
||||
|
||||
expectedDate := "19700101T000000Z"
|
||||
expectedHeaders := "host;x-amz-meta-other-header;x-amz-target"
|
||||
expectedSig := "41c18d68f9191079dfeead4e3f034328f89d86c79f8e9d51dd48bb70eaf623fc"
|
||||
expectedCred := "AKID/19700101/us-east-1/dynamodb/aws4_request"
|
||||
|
||||
q := signer.Request.URL.Query()
|
||||
assert.Equal(t, expectedSig, q.Get("X-Amz-Signature"))
|
||||
assert.Equal(t, expectedCred, q.Get("X-Amz-Credential"))
|
||||
assert.Equal(t, expectedHeaders, q.Get("X-Amz-SignedHeaders"))
|
||||
assert.Equal(t, expectedDate, q.Get("X-Amz-Date"))
|
||||
}
|
||||
|
||||
func TestSignRequest(t *testing.T) {
|
||||
signer := buildSigner("dynamodb", "us-east-1", time.Unix(0, 0), 0, "{}")
|
||||
signer.sign()
|
||||
|
||||
expectedDate := "19700101T000000Z"
|
||||
expectedSig := "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=host;x-amz-date;x-amz-meta-other-header;x-amz-security-token;x-amz-target, Signature=0196959cabd964bd10c05217b40ed151882dd394190438bab0c658dafdbff7a1"
|
||||
|
||||
q := signer.Request.Header
|
||||
assert.Equal(t, expectedSig, q.Get("Authorization"))
|
||||
assert.Equal(t, expectedDate, q.Get("X-Amz-Date"))
|
||||
}
|
||||
|
||||
func BenchmarkPresignRequest(b *testing.B) {
|
||||
signer := buildSigner("dynamodb", "us-east-1", time.Now(), 300*time.Second, "{}")
|
||||
for i := 0; i < b.N; i++ {
|
||||
signer.sign()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSignRequest(b *testing.B) {
|
||||
signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, "{}")
|
||||
for i := 0; i < b.N; i++ {
|
||||
signer.sign()
|
||||
}
|
||||
}
|
||||
2738
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/service/route53/api.go
сгенерированный
поставляемый
Обычный файл
2738
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/service/route53/api.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
20
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/service/route53/customizations.go
сгенерированный
поставляемый
Обычный файл
20
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/service/route53/customizations.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,20 @@
|
||||
package route53
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initService = func(s *aws.Service) {
|
||||
s.Handlers.Build.PushBack(sanitizeURL)
|
||||
}
|
||||
}
|
||||
|
||||
var reSanitizeURL = regexp.MustCompile(`\/%2F\w+%2F`)
|
||||
|
||||
func sanitizeURL(r *aws.Request) {
|
||||
r.HTTPRequest.URL.Opaque =
|
||||
reSanitizeURL.ReplaceAllString(r.HTTPRequest.URL.Opaque, "/")
|
||||
}
|
||||
20
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/service/route53/customizations_test.go
сгенерированный
поставляемый
Обычный файл
20
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/service/route53/customizations_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,20 @@
|
||||
package route53_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
"github.com/awslabs/aws-sdk-go/internal/util/utilassert"
|
||||
"github.com/awslabs/aws-sdk-go/service/route53"
|
||||
)
|
||||
|
||||
func TestBuildCorrectURI(t *testing.T) {
|
||||
svc := route53.New(nil)
|
||||
req, _ := svc.GetHostedZoneRequest(&route53.GetHostedZoneInput{
|
||||
ID: aws.String("/hostedzone/ABCDEFG"),
|
||||
})
|
||||
|
||||
req.Build()
|
||||
|
||||
utilassert.Match(t, `\/hostedzone\/ABCDEFG$`, req.HTTPRequest.URL.String())
|
||||
}
|
||||
714
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/service/route53/examples_test.go
сгенерированный
поставляемый
Обычный файл
714
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/service/route53/examples_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,714 @@
|
||||
package route53_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
"github.com/awslabs/aws-sdk-go/aws/awsutil"
|
||||
"github.com/awslabs/aws-sdk-go/service/route53"
|
||||
)
|
||||
|
||||
var _ time.Duration
|
||||
var _ bytes.Buffer
|
||||
|
||||
func ExampleRoute53_AssociateVPCWithHostedZone() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.AssociateVPCWithHostedZoneInput{
|
||||
HostedZoneID: aws.String("ResourceId"), // Required
|
||||
VPC: &route53.VPC{ // Required
|
||||
VPCID: aws.String("VPCId"),
|
||||
VPCRegion: aws.String("VPCRegion"),
|
||||
},
|
||||
Comment: aws.String("AssociateVPCComment"),
|
||||
}
|
||||
resp, err := svc.AssociateVPCWithHostedZone(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_ChangeResourceRecordSets() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.ChangeResourceRecordSetsInput{
|
||||
ChangeBatch: &route53.ChangeBatch{ // Required
|
||||
Changes: []*route53.Change{ // Required
|
||||
&route53.Change{ // Required
|
||||
Action: aws.String("ChangeAction"), // Required
|
||||
ResourceRecordSet: &route53.ResourceRecordSet{ // Required
|
||||
Name: aws.String("DNSName"), // Required
|
||||
Type: aws.String("RRType"), // Required
|
||||
AliasTarget: &route53.AliasTarget{
|
||||
DNSName: aws.String("DNSName"), // Required
|
||||
EvaluateTargetHealth: aws.Boolean(true), // Required
|
||||
HostedZoneID: aws.String("ResourceId"), // Required
|
||||
},
|
||||
Failover: aws.String("ResourceRecordSetFailover"),
|
||||
GeoLocation: &route53.GeoLocation{
|
||||
ContinentCode: aws.String("GeoLocationContinentCode"),
|
||||
CountryCode: aws.String("GeoLocationCountryCode"),
|
||||
SubdivisionCode: aws.String("GeoLocationSubdivisionCode"),
|
||||
},
|
||||
HealthCheckID: aws.String("HealthCheckId"),
|
||||
Region: aws.String("ResourceRecordSetRegion"),
|
||||
ResourceRecords: []*route53.ResourceRecord{
|
||||
&route53.ResourceRecord{ // Required
|
||||
Value: aws.String("RData"), // Required
|
||||
},
|
||||
// More values...
|
||||
},
|
||||
SetIdentifier: aws.String("ResourceRecordSetIdentifier"),
|
||||
TTL: aws.Long(1),
|
||||
Weight: aws.Long(1),
|
||||
},
|
||||
},
|
||||
// More values...
|
||||
},
|
||||
Comment: aws.String("ResourceDescription"),
|
||||
},
|
||||
HostedZoneID: aws.String("ResourceId"), // Required
|
||||
}
|
||||
resp, err := svc.ChangeResourceRecordSets(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_ChangeTagsForResource() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.ChangeTagsForResourceInput{
|
||||
ResourceID: aws.String("TagResourceId"), // Required
|
||||
ResourceType: aws.String("TagResourceType"), // Required
|
||||
AddTags: []*route53.Tag{
|
||||
&route53.Tag{ // Required
|
||||
Key: aws.String("TagKey"),
|
||||
Value: aws.String("TagValue"),
|
||||
},
|
||||
// More values...
|
||||
},
|
||||
RemoveTagKeys: []*string{
|
||||
aws.String("TagKey"), // Required
|
||||
// More values...
|
||||
},
|
||||
}
|
||||
resp, err := svc.ChangeTagsForResource(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_CreateHealthCheck() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.CreateHealthCheckInput{
|
||||
CallerReference: aws.String("HealthCheckNonce"), // Required
|
||||
HealthCheckConfig: &route53.HealthCheckConfig{ // Required
|
||||
Type: aws.String("HealthCheckType"), // Required
|
||||
FailureThreshold: aws.Long(1),
|
||||
FullyQualifiedDomainName: aws.String("FullyQualifiedDomainName"),
|
||||
IPAddress: aws.String("IPAddress"),
|
||||
Port: aws.Long(1),
|
||||
RequestInterval: aws.Long(1),
|
||||
ResourcePath: aws.String("ResourcePath"),
|
||||
SearchString: aws.String("SearchString"),
|
||||
},
|
||||
}
|
||||
resp, err := svc.CreateHealthCheck(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_CreateHostedZone() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.CreateHostedZoneInput{
|
||||
CallerReference: aws.String("Nonce"), // Required
|
||||
Name: aws.String("DNSName"), // Required
|
||||
DelegationSetID: aws.String("ResourceId"),
|
||||
HostedZoneConfig: &route53.HostedZoneConfig{
|
||||
Comment: aws.String("ResourceDescription"),
|
||||
PrivateZone: aws.Boolean(true),
|
||||
},
|
||||
VPC: &route53.VPC{
|
||||
VPCID: aws.String("VPCId"),
|
||||
VPCRegion: aws.String("VPCRegion"),
|
||||
},
|
||||
}
|
||||
resp, err := svc.CreateHostedZone(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_CreateReusableDelegationSet() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.CreateReusableDelegationSetInput{
|
||||
CallerReference: aws.String("Nonce"), // Required
|
||||
HostedZoneID: aws.String("ResourceId"),
|
||||
}
|
||||
resp, err := svc.CreateReusableDelegationSet(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_DeleteHealthCheck() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.DeleteHealthCheckInput{
|
||||
HealthCheckID: aws.String("HealthCheckId"), // Required
|
||||
}
|
||||
resp, err := svc.DeleteHealthCheck(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_DeleteHostedZone() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.DeleteHostedZoneInput{
|
||||
ID: aws.String("ResourceId"), // Required
|
||||
}
|
||||
resp, err := svc.DeleteHostedZone(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_DeleteReusableDelegationSet() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.DeleteReusableDelegationSetInput{
|
||||
ID: aws.String("ResourceId"), // Required
|
||||
}
|
||||
resp, err := svc.DeleteReusableDelegationSet(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_DisassociateVPCFromHostedZone() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.DisassociateVPCFromHostedZoneInput{
|
||||
HostedZoneID: aws.String("ResourceId"), // Required
|
||||
VPC: &route53.VPC{ // Required
|
||||
VPCID: aws.String("VPCId"),
|
||||
VPCRegion: aws.String("VPCRegion"),
|
||||
},
|
||||
Comment: aws.String("DisassociateVPCComment"),
|
||||
}
|
||||
resp, err := svc.DisassociateVPCFromHostedZone(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_GetChange() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.GetChangeInput{
|
||||
ID: aws.String("ResourceId"), // Required
|
||||
}
|
||||
resp, err := svc.GetChange(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_GetCheckerIPRanges() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
var params *route53.GetCheckerIPRangesInput
|
||||
resp, err := svc.GetCheckerIPRanges(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_GetGeoLocation() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.GetGeoLocationInput{
|
||||
ContinentCode: aws.String("GeoLocationContinentCode"),
|
||||
CountryCode: aws.String("GeoLocationCountryCode"),
|
||||
SubdivisionCode: aws.String("GeoLocationSubdivisionCode"),
|
||||
}
|
||||
resp, err := svc.GetGeoLocation(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_GetHealthCheck() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.GetHealthCheckInput{
|
||||
HealthCheckID: aws.String("HealthCheckId"), // Required
|
||||
}
|
||||
resp, err := svc.GetHealthCheck(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_GetHealthCheckCount() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
var params *route53.GetHealthCheckCountInput
|
||||
resp, err := svc.GetHealthCheckCount(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_GetHealthCheckLastFailureReason() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.GetHealthCheckLastFailureReasonInput{
|
||||
HealthCheckID: aws.String("HealthCheckId"), // Required
|
||||
}
|
||||
resp, err := svc.GetHealthCheckLastFailureReason(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_GetHealthCheckStatus() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.GetHealthCheckStatusInput{
|
||||
HealthCheckID: aws.String("HealthCheckId"), // Required
|
||||
}
|
||||
resp, err := svc.GetHealthCheckStatus(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_GetHostedZone() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.GetHostedZoneInput{
|
||||
ID: aws.String("ResourceId"), // Required
|
||||
}
|
||||
resp, err := svc.GetHostedZone(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_GetHostedZoneCount() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
var params *route53.GetHostedZoneCountInput
|
||||
resp, err := svc.GetHostedZoneCount(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_GetReusableDelegationSet() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.GetReusableDelegationSetInput{
|
||||
ID: aws.String("ResourceId"), // Required
|
||||
}
|
||||
resp, err := svc.GetReusableDelegationSet(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_ListGeoLocations() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.ListGeoLocationsInput{
|
||||
MaxItems: aws.String("PageMaxItems"),
|
||||
StartContinentCode: aws.String("GeoLocationContinentCode"),
|
||||
StartCountryCode: aws.String("GeoLocationCountryCode"),
|
||||
StartSubdivisionCode: aws.String("GeoLocationSubdivisionCode"),
|
||||
}
|
||||
resp, err := svc.ListGeoLocations(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_ListHealthChecks() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.ListHealthChecksInput{
|
||||
Marker: aws.String("PageMarker"),
|
||||
MaxItems: aws.String("PageMaxItems"),
|
||||
}
|
||||
resp, err := svc.ListHealthChecks(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_ListHostedZones() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.ListHostedZonesInput{
|
||||
DelegationSetID: aws.String("ResourceId"),
|
||||
Marker: aws.String("PageMarker"),
|
||||
MaxItems: aws.String("PageMaxItems"),
|
||||
}
|
||||
resp, err := svc.ListHostedZones(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_ListHostedZonesByName() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.ListHostedZonesByNameInput{
|
||||
DNSName: aws.String("DNSName"),
|
||||
HostedZoneID: aws.String("ResourceId"),
|
||||
MaxItems: aws.String("PageMaxItems"),
|
||||
}
|
||||
resp, err := svc.ListHostedZonesByName(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_ListResourceRecordSets() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.ListResourceRecordSetsInput{
|
||||
HostedZoneID: aws.String("ResourceId"), // Required
|
||||
MaxItems: aws.String("PageMaxItems"),
|
||||
StartRecordIdentifier: aws.String("ResourceRecordSetIdentifier"),
|
||||
StartRecordName: aws.String("DNSName"),
|
||||
StartRecordType: aws.String("RRType"),
|
||||
}
|
||||
resp, err := svc.ListResourceRecordSets(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_ListReusableDelegationSets() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.ListReusableDelegationSetsInput{
|
||||
Marker: aws.String("PageMarker"),
|
||||
MaxItems: aws.String("PageMaxItems"),
|
||||
}
|
||||
resp, err := svc.ListReusableDelegationSets(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_ListTagsForResource() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.ListTagsForResourceInput{
|
||||
ResourceID: aws.String("TagResourceId"), // Required
|
||||
ResourceType: aws.String("TagResourceType"), // Required
|
||||
}
|
||||
resp, err := svc.ListTagsForResource(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_ListTagsForResources() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.ListTagsForResourcesInput{
|
||||
ResourceIDs: []*string{ // Required
|
||||
aws.String("TagResourceId"), // Required
|
||||
// More values...
|
||||
},
|
||||
ResourceType: aws.String("TagResourceType"), // Required
|
||||
}
|
||||
resp, err := svc.ListTagsForResources(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_UpdateHealthCheck() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.UpdateHealthCheckInput{
|
||||
HealthCheckID: aws.String("HealthCheckId"), // Required
|
||||
FailureThreshold: aws.Long(1),
|
||||
FullyQualifiedDomainName: aws.String("FullyQualifiedDomainName"),
|
||||
HealthCheckVersion: aws.Long(1),
|
||||
IPAddress: aws.String("IPAddress"),
|
||||
Port: aws.Long(1),
|
||||
ResourcePath: aws.String("ResourcePath"),
|
||||
SearchString: aws.String("SearchString"),
|
||||
}
|
||||
resp, err := svc.UpdateHealthCheck(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
|
||||
func ExampleRoute53_UpdateHostedZoneComment() {
|
||||
svc := route53.New(nil)
|
||||
|
||||
params := &route53.UpdateHostedZoneCommentInput{
|
||||
ID: aws.String("ResourceId"), // Required
|
||||
Comment: aws.String("ResourceDescription"),
|
||||
}
|
||||
resp, err := svc.UpdateHostedZoneComment(params)
|
||||
|
||||
if awserr := aws.Error(err); awserr != nil {
|
||||
// A service error occurred.
|
||||
fmt.Println("Error:", awserr.Code, awserr.Message)
|
||||
} else if err != nil {
|
||||
// A non-service error occurred.
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pretty-print the response data.
|
||||
fmt.Println(awsutil.StringValue(resp))
|
||||
}
|
||||
59
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/service/route53/service.go
сгенерированный
поставляемый
Обычный файл
59
Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/service/route53/service.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,59 @@
|
||||
package route53
|
||||
|
||||
import (
|
||||
"github.com/awslabs/aws-sdk-go/aws"
|
||||
"github.com/awslabs/aws-sdk-go/internal/protocol/restxml"
|
||||
"github.com/awslabs/aws-sdk-go/internal/signer/v4"
|
||||
)
|
||||
|
||||
// Route53 is a client for Route 53.
|
||||
type Route53 struct {
|
||||
*aws.Service
|
||||
}
|
||||
|
||||
// Used for custom service initialization logic
|
||||
var initService func(*aws.Service)
|
||||
|
||||
// Used for custom request initialization logic
|
||||
var initRequest func(*aws.Request)
|
||||
|
||||
// New returns a new Route53 client.
|
||||
func New(config *aws.Config) *Route53 {
|
||||
if config == nil {
|
||||
config = &aws.Config{}
|
||||
}
|
||||
|
||||
service := &aws.Service{
|
||||
Config: aws.DefaultConfig.Merge(config),
|
||||
ServiceName: "route53",
|
||||
APIVersion: "2013-04-01",
|
||||
}
|
||||
service.Initialize()
|
||||
|
||||
// Handlers
|
||||
service.Handlers.Sign.PushBack(v4.Sign)
|
||||
service.Handlers.Build.PushBack(restxml.Build)
|
||||
service.Handlers.Unmarshal.PushBack(restxml.Unmarshal)
|
||||
service.Handlers.UnmarshalMeta.PushBack(restxml.UnmarshalMeta)
|
||||
service.Handlers.UnmarshalError.PushBack(restxml.UnmarshalError)
|
||||
|
||||
// Run custom service initialization if present
|
||||
if initService != nil {
|
||||
initService(service)
|
||||
}
|
||||
|
||||
return &Route53{service}
|
||||
}
|
||||
|
||||
// newRequest creates a new request for a Route53 operation and runs any
|
||||
// custom request initialization.
|
||||
func (c *Route53) newRequest(op *aws.Operation, params, data interface{}) *aws.Request {
|
||||
req := aws.NewRequest(c.Service, op, params, data)
|
||||
|
||||
// Run custom request initialization if present
|
||||
if initRequest != nil {
|
||||
initRequest(req)
|
||||
}
|
||||
|
||||
return req
|
||||
}
|
||||
19
Godeps/_workspace/src/github.com/braintree/manners/LICENSE
сгенерированный
поставляемый
Обычный файл
19
Godeps/_workspace/src/github.com/braintree/manners/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2014 Braintree, a division of PayPal, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
34
Godeps/_workspace/src/github.com/braintree/manners/helper_test.go
сгенерированный
поставляемый
Обычный файл
34
Godeps/_workspace/src/github.com/braintree/manners/helper_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,34 @@
|
||||
package manners
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A response handler that blocks until it receives a signal; simulates an
|
||||
// arbitrarily long web request. The "ready" channel is to prevent a race
|
||||
// condition in the test where the test moves on before the server is ready
|
||||
// to handle the request.
|
||||
func newBlockingHandler(ready, done chan bool) *blockingHandler {
|
||||
return &blockingHandler{ready, done}
|
||||
}
|
||||
|
||||
type blockingHandler struct {
|
||||
ready chan bool
|
||||
done chan bool
|
||||
}
|
||||
|
||||
func (h *blockingHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
|
||||
h.ready <- true
|
||||
time.Sleep(1e2)
|
||||
h.done <- true
|
||||
}
|
||||
|
||||
// A response handler that does nothing.
|
||||
func newTestHandler() testHandler {
|
||||
return testHandler{}
|
||||
}
|
||||
|
||||
type testHandler struct{}
|
||||
|
||||
func (h testHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {}
|
||||
49
Godeps/_workspace/src/github.com/braintree/manners/listener.go
сгенерированный
поставляемый
Обычный файл
49
Godeps/_workspace/src/github.com/braintree/manners/listener.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,49 @@
|
||||
package manners
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func NewListener(l net.Listener, s *GracefulServer) *GracefulListener {
|
||||
return &GracefulListener{l, true, s, sync.RWMutex{}}
|
||||
}
|
||||
|
||||
// A GracefulListener differs from a standard net.Listener in one way: if
|
||||
// Accept() is called after it is gracefully closed, it returns a
|
||||
// listenerAlreadyClosed error. The GracefulServer will ignore this
|
||||
// error.
|
||||
type GracefulListener struct {
|
||||
net.Listener
|
||||
open bool
|
||||
server *GracefulServer
|
||||
rw sync.RWMutex
|
||||
}
|
||||
|
||||
func (l *GracefulListener) Accept() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
l.rw.RLock()
|
||||
defer l.rw.RUnlock()
|
||||
if !l.open {
|
||||
err = listenerAlreadyClosed{err}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (l *GracefulListener) Close() error {
|
||||
l.rw.Lock()
|
||||
defer l.rw.Unlock()
|
||||
if !l.open {
|
||||
return nil
|
||||
}
|
||||
l.open = false
|
||||
err := l.Listener.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
type listenerAlreadyClosed struct {
|
||||
error
|
||||
}
|
||||
83
Godeps/_workspace/src/github.com/braintree/manners/server.go
сгенерированный
поставляемый
Обычный файл
83
Godeps/_workspace/src/github.com/braintree/manners/server.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,83 @@
|
||||
package manners
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Creates a new GracefulServer. The server will begin shutting down when
|
||||
// a value is passed to the Shutdown channel.
|
||||
func NewServer() *GracefulServer {
|
||||
return &GracefulServer{
|
||||
Shutdown: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
// A GracefulServer maintains a WaitGroup that counts how many in-flight
|
||||
// requests the server is handling. When it receives a shutdown signal,
|
||||
// it stops accepting new requests but does not actually shut down until
|
||||
// all in-flight requests terminate.
|
||||
type GracefulServer struct {
|
||||
Shutdown chan bool
|
||||
wg sync.WaitGroup
|
||||
shutdownHandler func()
|
||||
InnerServer http.Server
|
||||
}
|
||||
|
||||
// A helper function that emulates the functionality of http.ListenAndServe.
|
||||
func (s *GracefulServer) ListenAndServe(addr string, handler http.Handler) error {
|
||||
oldListener, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listener := NewListener(oldListener, s)
|
||||
err = s.Serve(listener, handler)
|
||||
return err
|
||||
}
|
||||
|
||||
// Similar to http.Serve. The listener passed must wrap a GracefulListener.
|
||||
func (s *GracefulServer) Serve(listener net.Listener, handler http.Handler) error {
|
||||
s.shutdownHandler = func() { listener.Close() }
|
||||
s.listenForShutdown()
|
||||
s.InnerServer.Handler = handler
|
||||
s.InnerServer.ConnState = func(conn net.Conn, newState http.ConnState) {
|
||||
switch newState {
|
||||
case http.StateNew:
|
||||
s.StartRoutine()
|
||||
case http.StateClosed, http.StateHijacked:
|
||||
s.FinishRoutine()
|
||||
}
|
||||
}
|
||||
err := s.InnerServer.Serve(listener)
|
||||
|
||||
// This block is reached when the server has received a shut down command.
|
||||
if err == nil {
|
||||
s.wg.Wait()
|
||||
return nil
|
||||
} else if _, ok := err.(listenerAlreadyClosed); ok {
|
||||
s.wg.Wait()
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Increments the server's WaitGroup. Use this if a web request starts more
|
||||
// goroutines and these goroutines are not guaranteed to finish before the
|
||||
// request.
|
||||
func (s *GracefulServer) StartRoutine() {
|
||||
s.wg.Add(1)
|
||||
}
|
||||
|
||||
// Decrement the server's WaitGroup. Used this to complement StartRoutine().
|
||||
func (s *GracefulServer) FinishRoutine() {
|
||||
s.wg.Done()
|
||||
}
|
||||
|
||||
func (s *GracefulServer) listenForShutdown() {
|
||||
go func() {
|
||||
<-s.Shutdown
|
||||
s.shutdownHandler()
|
||||
}()
|
||||
}
|
||||
71
Godeps/_workspace/src/github.com/braintree/manners/server_test.go
сгенерированный
поставляемый
Обычный файл
71
Godeps/_workspace/src/github.com/braintree/manners/server_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,71 @@
|
||||
package manners
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests that the server allows in-flight requests to complete before shutting
|
||||
// down.
|
||||
func TestGracefulness(t *testing.T) {
|
||||
ready := make(chan bool)
|
||||
done := make(chan bool)
|
||||
|
||||
exited := false
|
||||
|
||||
handler := newBlockingHandler(ready, done)
|
||||
server := NewServer()
|
||||
|
||||
go func() {
|
||||
err := server.ListenAndServe(":7000", handler)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
exited = true
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_, err := http.Get("http://localhost:7000")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// This will block until the server is inside the handler function.
|
||||
<-ready
|
||||
|
||||
server.Shutdown <- true
|
||||
<-done
|
||||
|
||||
if exited {
|
||||
t.Fatal("The request did not complete before server exited")
|
||||
} else {
|
||||
// The handler is being allowed to run to completion; test passes.
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that the server begins to shut down when told to and does not accept
|
||||
// new requests
|
||||
func TestShutdown(t *testing.T) {
|
||||
handler := newTestHandler()
|
||||
server := NewServer()
|
||||
exited := make(chan bool)
|
||||
|
||||
go func() {
|
||||
err := server.ListenAndServe(":7100", handler)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
exited <- true
|
||||
}()
|
||||
|
||||
server.Shutdown <- true
|
||||
|
||||
<-exited
|
||||
_, err := http.Get("http://localhost:7100")
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Did not receive an error when trying to connect to server.")
|
||||
}
|
||||
}
|
||||
8
Godeps/_workspace/src/github.com/go-gorp/gorp/.gitignore
сгенерированный
поставляемый
Обычный файл
8
Godeps/_workspace/src/github.com/go-gorp/gorp/.gitignore
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,8 @@
|
||||
_test
|
||||
_testmain.go
|
||||
_obj
|
||||
*~
|
||||
*.6
|
||||
6.out
|
||||
gorptest.bin
|
||||
tmp
|
||||
23
Godeps/_workspace/src/github.com/go-gorp/gorp/.travis.yml
сгенерированный
поставляемый
Обычный файл
23
Godeps/_workspace/src/github.com/go-gorp/gorp/.travis.yml
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,23 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- tip
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- postgres
|
||||
- sqlite3
|
||||
|
||||
before_script:
|
||||
- mysql -e "CREATE DATABASE gorptest;"
|
||||
- mysql -u root -e "GRANT ALL ON gorptest.* TO gorptest@localhost IDENTIFIED BY 'gorptest'"
|
||||
- psql -c "CREATE DATABASE gorptest;" -U postgres
|
||||
- psql -c "CREATE USER "gorptest" WITH SUPERUSER PASSWORD 'gorptest';" -U postgres
|
||||
- go get github.com/lib/pq
|
||||
- go get github.com/mattn/go-sqlite3
|
||||
- go get github.com/ziutek/mymysql/godrv
|
||||
- go get github.com/go-sql-driver/mysql
|
||||
|
||||
script: ./test_all.sh
|
||||
22
Godeps/_workspace/src/github.com/go-gorp/gorp/LICENSE
сгенерированный
поставляемый
Обычный файл
22
Godeps/_workspace/src/github.com/go-gorp/gorp/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,22 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2012 James Cooper <james@bitmechanic.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
6
Godeps/_workspace/src/github.com/go-gorp/gorp/Makefile
сгенерированный
поставляемый
Обычный файл
6
Godeps/_workspace/src/github.com/go-gorp/gorp/Makefile
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,6 @@
|
||||
include $(GOROOT)/src/Make.inc
|
||||
|
||||
TARG = github.com/go-gorp/gorp
|
||||
GOFILES = gorp.go dialect.go
|
||||
|
||||
include $(GOROOT)/src/Make.pkg
|
||||
696
Godeps/_workspace/src/github.com/go-gorp/gorp/dialect.go
сгенерированный
поставляемый
Обычный файл
696
Godeps/_workspace/src/github.com/go-gorp/gorp/dialect.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,696 @@
|
||||
package gorp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The Dialect interface encapsulates behaviors that differ across
|
||||
// SQL databases. At present the Dialect is only used by CreateTables()
|
||||
// but this could change in the future
|
||||
type Dialect interface {
|
||||
|
||||
// adds a suffix to any query, usually ";"
|
||||
QuerySuffix() string
|
||||
|
||||
// ToSqlType returns the SQL column type to use when creating a
|
||||
// table of the given Go Type. maxsize can be used to switch based on
|
||||
// size. For example, in MySQL []byte could map to BLOB, MEDIUMBLOB,
|
||||
// or LONGBLOB depending on the maxsize
|
||||
ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string
|
||||
|
||||
// string to append to primary key column definitions
|
||||
AutoIncrStr() string
|
||||
|
||||
// string to bind autoincrement columns to. Empty string will
|
||||
// remove reference to those columns in the INSERT statement.
|
||||
AutoIncrBindValue() string
|
||||
|
||||
AutoIncrInsertSuffix(col *ColumnMap) string
|
||||
|
||||
// string to append to "create table" statement for vendor specific
|
||||
// table attributes
|
||||
CreateTableSuffix() string
|
||||
|
||||
// string to truncate tables
|
||||
TruncateClause() string
|
||||
|
||||
// bind variable string to use when forming SQL statements
|
||||
// in many dbs it is "?", but Postgres appears to use $1
|
||||
//
|
||||
// i is a zero based index of the bind variable in this statement
|
||||
//
|
||||
BindVar(i int) string
|
||||
|
||||
// Handles quoting of a field name to ensure that it doesn't raise any
|
||||
// SQL parsing exceptions by using a reserved word as a field name.
|
||||
QuoteField(field string) string
|
||||
|
||||
// Handles building up of a schema.database string that is compatible with
|
||||
// the given dialect
|
||||
//
|
||||
// schema - The schema that <table> lives in
|
||||
// table - The table name
|
||||
QuotedTableForQuery(schema string, table string) string
|
||||
|
||||
// Existance clause for table creation / deletion
|
||||
IfSchemaNotExists(command, schema string) string
|
||||
IfTableExists(command, schema, table string) string
|
||||
IfTableNotExists(command, schema, table string) string
|
||||
}
|
||||
|
||||
// IntegerAutoIncrInserter is implemented by dialects that can perform
|
||||
// inserts with automatically incremented integer primary keys. If
|
||||
// the dialect can handle automatic assignment of more than just
|
||||
// integers, see TargetedAutoIncrInserter.
|
||||
type IntegerAutoIncrInserter interface {
|
||||
InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error)
|
||||
}
|
||||
|
||||
// TargetedAutoIncrInserter is implemented by dialects that can
|
||||
// perform automatic assignment of any primary key type (i.e. strings
|
||||
// for uuids, integers for serials, etc).
|
||||
type TargetedAutoIncrInserter interface {
|
||||
// InsertAutoIncrToTarget runs an insert operation and assigns the
|
||||
// automatically generated primary key directly to the passed in
|
||||
// target. The target should be a pointer to the primary key
|
||||
// field of the value being inserted.
|
||||
InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error
|
||||
}
|
||||
|
||||
func standardInsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
res, err := exec.Exec(insertSql, params...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.LastInsertId()
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// sqlite3 //
|
||||
/////////////
|
||||
|
||||
type SqliteDialect struct {
|
||||
suffix string
|
||||
}
|
||||
|
||||
func (d SqliteDialect) QuerySuffix() string { return ";" }
|
||||
|
||||
func (d SqliteDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
|
||||
case reflect.Bool:
|
||||
return "integer"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return "integer"
|
||||
case reflect.Float64, reflect.Float32:
|
||||
return "real"
|
||||
case reflect.Slice:
|
||||
if val.Elem().Kind() == reflect.Uint8 {
|
||||
return "blob"
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Name() {
|
||||
case "NullInt64":
|
||||
return "integer"
|
||||
case "NullFloat64":
|
||||
return "real"
|
||||
case "NullBool":
|
||||
return "integer"
|
||||
case "Time":
|
||||
return "datetime"
|
||||
}
|
||||
|
||||
if maxsize < 1 {
|
||||
maxsize = 255
|
||||
}
|
||||
return fmt.Sprintf("varchar(%d)", maxsize)
|
||||
}
|
||||
|
||||
// Returns autoincrement
|
||||
func (d SqliteDialect) AutoIncrStr() string {
|
||||
return "autoincrement"
|
||||
}
|
||||
|
||||
func (d SqliteDialect) AutoIncrBindValue() string {
|
||||
return "null"
|
||||
}
|
||||
|
||||
func (d SqliteDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Returns suffix
|
||||
func (d SqliteDialect) CreateTableSuffix() string {
|
||||
return d.suffix
|
||||
}
|
||||
|
||||
// With sqlite, there technically isn't a TRUNCATE statement,
|
||||
// but a DELETE FROM uses a truncate optimization:
|
||||
// http://www.sqlite.org/lang_delete.html
|
||||
func (d SqliteDialect) TruncateClause() string {
|
||||
return "delete from"
|
||||
}
|
||||
|
||||
// Returns "?"
|
||||
func (d SqliteDialect) BindVar(i int) string {
|
||||
return "?"
|
||||
}
|
||||
|
||||
func (d SqliteDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
return standardInsertAutoIncr(exec, insertSql, params...)
|
||||
}
|
||||
|
||||
func (d SqliteDialect) QuoteField(f string) string {
|
||||
return `"` + f + `"`
|
||||
}
|
||||
|
||||
// sqlite does not have schemas like PostgreSQL does, so just escape it like normal
|
||||
func (d SqliteDialect) QuotedTableForQuery(schema string, table string) string {
|
||||
return d.QuoteField(table)
|
||||
}
|
||||
|
||||
func (d SqliteDialect) IfSchemaNotExists(command, schema string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
|
||||
func (d SqliteDialect) IfTableExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if exists", command)
|
||||
}
|
||||
|
||||
func (d SqliteDialect) IfTableNotExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// PostgreSQL //
|
||||
////////////////
|
||||
|
||||
type PostgresDialect struct {
|
||||
suffix string
|
||||
}
|
||||
|
||||
func (d PostgresDialect) QuerySuffix() string { return ";" }
|
||||
|
||||
func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
|
||||
case reflect.Bool:
|
||||
return "boolean"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
if isAutoIncr {
|
||||
return "serial"
|
||||
}
|
||||
return "integer"
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
if isAutoIncr {
|
||||
return "bigserial"
|
||||
}
|
||||
return "bigint"
|
||||
case reflect.Float64:
|
||||
return "double precision"
|
||||
case reflect.Float32:
|
||||
return "real"
|
||||
case reflect.Slice:
|
||||
if val.Elem().Kind() == reflect.Uint8 {
|
||||
return "bytea"
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Name() {
|
||||
case "NullInt64":
|
||||
return "bigint"
|
||||
case "NullFloat64":
|
||||
return "double precision"
|
||||
case "NullBool":
|
||||
return "boolean"
|
||||
case "Time", "NullTime":
|
||||
return "timestamp with time zone"
|
||||
}
|
||||
|
||||
if maxsize > 0 {
|
||||
return fmt.Sprintf("varchar(%d)", maxsize)
|
||||
} else {
|
||||
return "text"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Returns empty string
|
||||
func (d PostgresDialect) AutoIncrStr() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d PostgresDialect) AutoIncrBindValue() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
func (d PostgresDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
|
||||
return " returning " + col.ColumnName
|
||||
}
|
||||
|
||||
// Returns suffix
|
||||
func (d PostgresDialect) CreateTableSuffix() string {
|
||||
return d.suffix
|
||||
}
|
||||
|
||||
func (d PostgresDialect) TruncateClause() string {
|
||||
return "truncate"
|
||||
}
|
||||
|
||||
// Returns "$(i+1)"
|
||||
func (d PostgresDialect) BindVar(i int) string {
|
||||
return fmt.Sprintf("$%d", i+1)
|
||||
}
|
||||
|
||||
func (d PostgresDialect) InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error {
|
||||
rows, err := exec.query(insertSql, params...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
return fmt.Errorf("No serial value returned for insert: %s Encountered error: %s", insertSql, rows.Err())
|
||||
}
|
||||
if err := rows.Scan(target); err != nil {
|
||||
return err
|
||||
}
|
||||
if rows.Next() {
|
||||
return fmt.Errorf("more than two serial value returned for insert: %s", insertSql)
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func (d PostgresDialect) QuoteField(f string) string {
|
||||
return `"` + strings.ToLower(f) + `"`
|
||||
}
|
||||
|
||||
func (d PostgresDialect) QuotedTableForQuery(schema string, table string) string {
|
||||
if strings.TrimSpace(schema) == "" {
|
||||
return d.QuoteField(table)
|
||||
}
|
||||
|
||||
return schema + "." + d.QuoteField(table)
|
||||
}
|
||||
|
||||
func (d PostgresDialect) IfSchemaNotExists(command, schema string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
|
||||
func (d PostgresDialect) IfTableExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if exists", command)
|
||||
}
|
||||
|
||||
func (d PostgresDialect) IfTableNotExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// MySQL //
|
||||
///////////
|
||||
|
||||
// Implementation of Dialect for MySQL databases.
|
||||
type MySQLDialect struct {
|
||||
|
||||
// Engine is the storage engine to use "InnoDB" vs "MyISAM" for example
|
||||
Engine string
|
||||
|
||||
// Encoding is the character encoding to use for created tables
|
||||
Encoding string
|
||||
}
|
||||
|
||||
func (d MySQLDialect) QuerySuffix() string { return ";" }
|
||||
|
||||
func (d MySQLDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
|
||||
case reflect.Bool:
|
||||
return "boolean"
|
||||
case reflect.Int8:
|
||||
return "tinyint"
|
||||
case reflect.Uint8:
|
||||
return "tinyint unsigned"
|
||||
case reflect.Int16:
|
||||
return "smallint"
|
||||
case reflect.Uint16:
|
||||
return "smallint unsigned"
|
||||
case reflect.Int, reflect.Int32:
|
||||
return "int"
|
||||
case reflect.Uint, reflect.Uint32:
|
||||
return "int unsigned"
|
||||
case reflect.Int64:
|
||||
return "bigint"
|
||||
case reflect.Uint64:
|
||||
return "bigint unsigned"
|
||||
case reflect.Float64, reflect.Float32:
|
||||
return "double"
|
||||
case reflect.Slice:
|
||||
if val.Elem().Kind() == reflect.Uint8 {
|
||||
return "mediumblob"
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Name() {
|
||||
case "NullInt64":
|
||||
return "bigint"
|
||||
case "NullFloat64":
|
||||
return "double"
|
||||
case "NullBool":
|
||||
return "tinyint"
|
||||
case "Time":
|
||||
return "datetime"
|
||||
}
|
||||
|
||||
if maxsize < 1 {
|
||||
maxsize = 255
|
||||
}
|
||||
return fmt.Sprintf("varchar(%d)", maxsize)
|
||||
}
|
||||
|
||||
// Returns auto_increment
|
||||
func (d MySQLDialect) AutoIncrStr() string {
|
||||
return "auto_increment"
|
||||
}
|
||||
|
||||
func (d MySQLDialect) AutoIncrBindValue() string {
|
||||
return "null"
|
||||
}
|
||||
|
||||
func (d MySQLDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Returns engine=%s charset=%s based on values stored on struct
|
||||
func (d MySQLDialect) CreateTableSuffix() string {
|
||||
if d.Engine == "" || d.Encoding == "" {
|
||||
msg := "gorp - undefined"
|
||||
|
||||
if d.Engine == "" {
|
||||
msg += " MySQLDialect.Engine"
|
||||
}
|
||||
if d.Engine == "" && d.Encoding == "" {
|
||||
msg += ","
|
||||
}
|
||||
if d.Encoding == "" {
|
||||
msg += " MySQLDialect.Encoding"
|
||||
}
|
||||
msg += ". Check that your MySQLDialect was correctly initialized when declared."
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(" engine=%s charset=%s", d.Engine, d.Encoding)
|
||||
}
|
||||
|
||||
func (d MySQLDialect) TruncateClause() string {
|
||||
return "truncate"
|
||||
}
|
||||
|
||||
// Returns "?"
|
||||
func (d MySQLDialect) BindVar(i int) string {
|
||||
return "?"
|
||||
}
|
||||
|
||||
func (d MySQLDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
return standardInsertAutoIncr(exec, insertSql, params...)
|
||||
}
|
||||
|
||||
func (d MySQLDialect) QuoteField(f string) string {
|
||||
return "`" + f + "`"
|
||||
}
|
||||
|
||||
func (d MySQLDialect) QuotedTableForQuery(schema string, table string) string {
|
||||
if strings.TrimSpace(schema) == "" {
|
||||
return d.QuoteField(table)
|
||||
}
|
||||
|
||||
return schema + "." + d.QuoteField(table)
|
||||
}
|
||||
|
||||
func (d MySQLDialect) IfSchemaNotExists(command, schema string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
|
||||
func (d MySQLDialect) IfTableExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if exists", command)
|
||||
}
|
||||
|
||||
func (d MySQLDialect) IfTableNotExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Sql Server //
|
||||
////////////////
|
||||
|
||||
// Implementation of Dialect for Microsoft SQL Server databases.
|
||||
// Tested on SQL Server 2008 with driver: github.com/denisenkom/go-mssqldb
|
||||
|
||||
type SqlServerDialect struct {
|
||||
suffix string
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
|
||||
case reflect.Bool:
|
||||
return "bit"
|
||||
case reflect.Int8:
|
||||
return "tinyint"
|
||||
case reflect.Uint8:
|
||||
return "smallint"
|
||||
case reflect.Int16:
|
||||
return "smallint"
|
||||
case reflect.Uint16:
|
||||
return "int"
|
||||
case reflect.Int, reflect.Int32:
|
||||
return "int"
|
||||
case reflect.Uint, reflect.Uint32:
|
||||
return "bigint"
|
||||
case reflect.Int64:
|
||||
return "bigint"
|
||||
case reflect.Uint64:
|
||||
return "bigint"
|
||||
case reflect.Float32:
|
||||
return "real"
|
||||
case reflect.Float64:
|
||||
return "float(53)"
|
||||
case reflect.Slice:
|
||||
if val.Elem().Kind() == reflect.Uint8 {
|
||||
return "varbinary"
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Name() {
|
||||
case "NullInt64":
|
||||
return "bigint"
|
||||
case "NullFloat64":
|
||||
return "float(53)"
|
||||
case "NullBool":
|
||||
return "tinyint"
|
||||
case "Time":
|
||||
return "datetime"
|
||||
}
|
||||
|
||||
if maxsize < 1 {
|
||||
maxsize = 255
|
||||
}
|
||||
return fmt.Sprintf("varchar(%d)", maxsize)
|
||||
}
|
||||
|
||||
// Returns auto_increment
|
||||
func (d SqlServerDialect) AutoIncrStr() string {
|
||||
return "identity(0,1)"
|
||||
}
|
||||
|
||||
// Empty string removes autoincrement columns from the INSERT statements.
|
||||
func (d SqlServerDialect) AutoIncrBindValue() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Returns suffix
|
||||
func (d SqlServerDialect) CreateTableSuffix() string {
|
||||
|
||||
return d.suffix
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) TruncateClause() string {
|
||||
return "delete from"
|
||||
}
|
||||
|
||||
// Returns "?"
|
||||
func (d SqlServerDialect) BindVar(i int) string {
|
||||
return "?"
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
return standardInsertAutoIncr(exec, insertSql, params...)
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) QuoteField(f string) string {
|
||||
return `"` + f + `"`
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) QuotedTableForQuery(schema string, table string) string {
|
||||
if strings.TrimSpace(schema) == "" {
|
||||
return table
|
||||
}
|
||||
return schema + "." + table
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) QuerySuffix() string { return ";" }
|
||||
|
||||
func (d SqlServerDialect) IfSchemaNotExists(command, schema string) string {
|
||||
s := fmt.Sprintf("if not exists (select name from sys.schemas where name = '%s') %s", schema, command)
|
||||
return s
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) IfTableExists(command, schema, table string) string {
|
||||
var schema_clause string
|
||||
if strings.TrimSpace(schema) != "" {
|
||||
schema_clause = fmt.Sprintf("table_schema = '%s' and ", schema)
|
||||
}
|
||||
s := fmt.Sprintf("if exists (select * from information_schema.tables where %stable_name = '%s') %s", schema_clause, table, command)
|
||||
return s
|
||||
}
|
||||
|
||||
func (d SqlServerDialect) IfTableNotExists(command, schema, table string) string {
|
||||
var schema_clause string
|
||||
if strings.TrimSpace(schema) != "" {
|
||||
schema_clause = fmt.Sprintf("table_schema = '%s' and ", schema)
|
||||
}
|
||||
s := fmt.Sprintf("if not exists (select * from information_schema.tables where %stable_name = '%s') %s", schema_clause, table, command)
|
||||
return s
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Oracle //
|
||||
///////////
|
||||
|
||||
// Implementation of Dialect for Oracle databases.
|
||||
type OracleDialect struct{}
|
||||
|
||||
func (d OracleDialect) QuerySuffix() string { return "" }
|
||||
|
||||
func (d OracleDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
|
||||
switch val.Kind() {
|
||||
case reflect.Ptr:
|
||||
return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
|
||||
case reflect.Bool:
|
||||
return "boolean"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
if isAutoIncr {
|
||||
return "serial"
|
||||
}
|
||||
return "integer"
|
||||
case reflect.Int64, reflect.Uint64:
|
||||
if isAutoIncr {
|
||||
return "bigserial"
|
||||
}
|
||||
return "bigint"
|
||||
case reflect.Float64:
|
||||
return "double precision"
|
||||
case reflect.Float32:
|
||||
return "real"
|
||||
case reflect.Slice:
|
||||
if val.Elem().Kind() == reflect.Uint8 {
|
||||
return "bytea"
|
||||
}
|
||||
}
|
||||
|
||||
switch val.Name() {
|
||||
case "NullInt64":
|
||||
return "bigint"
|
||||
case "NullFloat64":
|
||||
return "double precision"
|
||||
case "NullBool":
|
||||
return "boolean"
|
||||
case "NullTime", "Time":
|
||||
return "timestamp with time zone"
|
||||
}
|
||||
|
||||
if maxsize > 0 {
|
||||
return fmt.Sprintf("varchar(%d)", maxsize)
|
||||
} else {
|
||||
return "text"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Returns empty string
|
||||
func (d OracleDialect) AutoIncrStr() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d OracleDialect) AutoIncrBindValue() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
func (d OracleDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
|
||||
return " returning " + col.ColumnName
|
||||
}
|
||||
|
||||
// Returns suffix
|
||||
func (d OracleDialect) CreateTableSuffix() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d OracleDialect) TruncateClause() string {
|
||||
return "truncate"
|
||||
}
|
||||
|
||||
// Returns "$(i+1)"
|
||||
func (d OracleDialect) BindVar(i int) string {
|
||||
return fmt.Sprintf(":%d", i+1)
|
||||
}
|
||||
|
||||
func (d OracleDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
|
||||
rows, err := exec.query(insertSql, params...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Next() {
|
||||
var id int64
|
||||
err := rows.Scan(&id)
|
||||
return id, err
|
||||
}
|
||||
|
||||
return 0, errors.New("No serial value returned for insert: " + insertSql + " Encountered error: " + rows.Err().Error())
|
||||
}
|
||||
|
||||
func (d OracleDialect) QuoteField(f string) string {
|
||||
return `"` + strings.ToUpper(f) + `"`
|
||||
}
|
||||
|
||||
func (d OracleDialect) QuotedTableForQuery(schema string, table string) string {
|
||||
if strings.TrimSpace(schema) == "" {
|
||||
return d.QuoteField(table)
|
||||
}
|
||||
|
||||
return schema + "." + d.QuoteField(table)
|
||||
}
|
||||
|
||||
func (d OracleDialect) IfSchemaNotExists(command, schema string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
|
||||
func (d OracleDialect) IfTableExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if exists", command)
|
||||
}
|
||||
|
||||
func (d OracleDialect) IfTableNotExists(command, schema, table string) string {
|
||||
return fmt.Sprintf("%s if not exists", command)
|
||||
}
|
||||
26
Godeps/_workspace/src/github.com/go-gorp/gorp/errors.go
сгенерированный
поставляемый
Обычный файл
26
Godeps/_workspace/src/github.com/go-gorp/gorp/errors.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,26 @@
|
||||
package gorp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A non-fatal error, when a select query returns columns that do not exist
|
||||
// as fields in the struct it is being mapped to
|
||||
type NoFieldInTypeError struct {
|
||||
TypeName string
|
||||
MissingColNames []string
|
||||
}
|
||||
|
||||
func (err *NoFieldInTypeError) Error() string {
|
||||
return fmt.Sprintf("gorp: No fields %+v in type %s", err.MissingColNames, err.TypeName)
|
||||
}
|
||||
|
||||
// returns true if the error is non-fatal (ie, we shouldn't immediately return)
|
||||
func NonFatalError(err error) bool {
|
||||
switch err.(type) {
|
||||
case *NoFieldInTypeError:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
2178
Godeps/_workspace/src/github.com/go-gorp/gorp/gorp.go
сгенерированный
поставляемый
Обычный файл
2178
Godeps/_workspace/src/github.com/go-gorp/gorp/gorp.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
2170
Godeps/_workspace/src/github.com/go-gorp/gorp/gorp_test.go
сгенерированный
поставляемый
Обычный файл
2170
Godeps/_workspace/src/github.com/go-gorp/gorp/gorp_test.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
22
Godeps/_workspace/src/github.com/go-gorp/gorp/test_all.sh
сгенерированный
поставляемый
Обычный файл
22
Godeps/_workspace/src/github.com/go-gorp/gorp/test_all.sh
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
# on macs, you may need to:
|
||||
# export GOBUILDFLAG=-ldflags -linkmode=external
|
||||
|
||||
set -e
|
||||
|
||||
export GORP_TEST_DSN=gorptest/gorptest/gorptest
|
||||
export GORP_TEST_DIALECT=mysql
|
||||
go test $GOBUILDFLAG .
|
||||
|
||||
export GORP_TEST_DSN=gorptest:gorptest@/gorptest
|
||||
export GORP_TEST_DIALECT=gomysql
|
||||
go test $GOBUILDFLAG .
|
||||
|
||||
export GORP_TEST_DSN="user=gorptest password=gorptest dbname=gorptest sslmode=disable"
|
||||
export GORP_TEST_DIALECT=postgres
|
||||
go test $GOBUILDFLAG .
|
||||
|
||||
export GORP_TEST_DSN=/tmp/gorptest.bin
|
||||
export GORP_TEST_DIALECT=sqlite
|
||||
go test $GOBUILDFLAG .
|
||||
8
Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore
сгенерированный
поставляемый
Обычный файл
8
Godeps/_workspace/src/github.com/go-sql-driver/mysql/.gitignore
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,8 @@
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
Icon?
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
10
Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml
сгенерированный
поставляемый
Обычный файл
10
Godeps/_workspace/src/github.com/go-sql-driver/mysql/.travis.yml
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,10 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- tip
|
||||
|
||||
before_script:
|
||||
- mysql -e 'create database gotest;'
|
||||
42
Godeps/_workspace/src/github.com/go-sql-driver/mysql/AUTHORS
сгенерированный
поставляемый
Обычный файл
42
Godeps/_workspace/src/github.com/go-sql-driver/mysql/AUTHORS
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,42 @@
|
||||
# This is the official list of Go-MySQL-Driver authors for copyright purposes.
|
||||
|
||||
# If you are submitting a patch, please add your name or the name of the
|
||||
# organization which holds the copyright to this list in alphabetical order.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name <email address>
|
||||
# The email address is not required for organizations.
|
||||
# Please keep the list sorted.
|
||||
|
||||
|
||||
# Individual Persons
|
||||
|
||||
Aaron Hopkins <go-sql-driver at die.net>
|
||||
Arne Hormann <arnehormann at gmail.com>
|
||||
Carlos Nieto <jose.carlos at menteslibres.net>
|
||||
Chris Moos <chris at tech9computers.com>
|
||||
DisposaBoy <disposaboy at dby.me>
|
||||
Frederick Mayle <frederickmayle at gmail.com>
|
||||
Gustavo Kristic <gkristic at gmail.com>
|
||||
Hanno Braun <mail at hannobraun.com>
|
||||
Henri Yandell <flamefew at gmail.com>
|
||||
INADA Naoki <songofacandy at gmail.com>
|
||||
James Harr <james.harr at gmail.com>
|
||||
Jian Zhen <zhenjl at gmail.com>
|
||||
Julien Schmidt <go-sql-driver at julienschmidt.com>
|
||||
Kamil Dziedzic <kamil at klecza.pl>
|
||||
Leonardo YongUk Kim <dalinaum at gmail.com>
|
||||
Lucas Liu <extrafliu at gmail.com>
|
||||
Luke Scott <luke at webconnex.com>
|
||||
Michael Woolnough <michael.woolnough at gmail.com>
|
||||
Nicola Peduzzi <thenikso at gmail.com>
|
||||
Runrioter Wung <runrioter at gmail.com>
|
||||
Soroush Pour <me at soroushjp.com>
|
||||
Xiaobing Jiang <s7v7nislands at gmail.com>
|
||||
Xiuming Chen <cc at cxm.cc>
|
||||
|
||||
# Organizations
|
||||
|
||||
Barracuda Networks, Inc.
|
||||
Google Inc.
|
||||
Stripe Inc.
|
||||
92
Godeps/_workspace/src/github.com/go-sql-driver/mysql/CHANGELOG.md
сгенерированный
поставляемый
Обычный файл
92
Godeps/_workspace/src/github.com/go-sql-driver/mysql/CHANGELOG.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,92 @@
|
||||
## HEAD
|
||||
|
||||
Changes:
|
||||
|
||||
- Go 1.1 is no longer supported
|
||||
- Use decimals field from MySQL to format time types (#249)
|
||||
- Buffer optimizations (#269)
|
||||
- TLS ServerName defaults to the host (#283)
|
||||
|
||||
Bugfixes:
|
||||
|
||||
- Enable microsecond resolution on TIME, DATETIME and TIMESTAMP (#249)
|
||||
- Fixed handling of queries without columns and rows (#255)
|
||||
- Fixed a panic when SetKeepAlive() failed (#298)
|
||||
|
||||
New Features:
|
||||
- Support for returning table alias on Columns() (#289)
|
||||
- Placeholder interpolation, can be actived with the DSN parameter `interpolateParams=true` (#309, #318)
|
||||
|
||||
|
||||
## Version 1.2 (2014-06-03)
|
||||
|
||||
Changes:
|
||||
|
||||
- We switched back to a "rolling release". `go get` installs the current master branch again
|
||||
- Version v1 of the driver will not be maintained anymore. Go 1.0 is no longer supported by this driver
|
||||
- Exported errors to allow easy checking from application code
|
||||
- Enabled TCP Keepalives on TCP connections
|
||||
- Optimized INFILE handling (better buffer size calculation, lazy init, ...)
|
||||
- The DSN parser also checks for a missing separating slash
|
||||
- Faster binary date / datetime to string formatting
|
||||
- Also exported the MySQLWarning type
|
||||
- mysqlConn.Close returns the first error encountered instead of ignoring all errors
|
||||
- writePacket() automatically writes the packet size to the header
|
||||
- readPacket() uses an iterative approach instead of the recursive approach to merge splitted packets
|
||||
|
||||
New Features:
|
||||
|
||||
- `RegisterDial` allows the usage of a custom dial function to establish the network connection
|
||||
- Setting the connection collation is possible with the `collation` DSN parameter. This parameter should be preferred over the `charset` parameter
|
||||
- Logging of critical errors is configurable with `SetLogger`
|
||||
- Google CloudSQL support
|
||||
|
||||
Bugfixes:
|
||||
|
||||
- Allow more than 32 parameters in prepared statements
|
||||
- Various old_password fixes
|
||||
- Fixed TestConcurrent test to pass Go's race detection
|
||||
- Fixed appendLengthEncodedInteger for large numbers
|
||||
- Renamed readLengthEnodedString to readLengthEncodedString and skipLengthEnodedString to skipLengthEncodedString (fixed typo)
|
||||
|
||||
|
||||
## Version 1.1 (2013-11-02)
|
||||
|
||||
Changes:
|
||||
|
||||
- Go-MySQL-Driver now requires Go 1.1
|
||||
- Connections now use the collation `utf8_general_ci` by default. Adding `&charset=UTF8` to the DSN should not be necessary anymore
|
||||
- Made closing rows and connections error tolerant. This allows for example deferring rows.Close() without checking for errors
|
||||
- `[]byte(nil)` is now treated as a NULL value. Before, it was treated like an empty string / `[]byte("")`
|
||||
- DSN parameter values must now be url.QueryEscape'ed. This allows text values to contain special characters, such as '&'.
|
||||
- Use the IO buffer also for writing. This results in zero allocations (by the driver) for most queries
|
||||
- Optimized the buffer for reading
|
||||
- stmt.Query now caches column metadata
|
||||
- New Logo
|
||||
- Changed the copyright header to include all contributors
|
||||
- Improved the LOAD INFILE documentation
|
||||
- The driver struct is now exported to make the driver directly accessible
|
||||
- Refactored the driver tests
|
||||
- Added more benchmarks and moved all to a separate file
|
||||
- Other small refactoring
|
||||
|
||||
New Features:
|
||||
|
||||
- Added *old_passwords* support: Required in some cases, but must be enabled by adding `allowOldPasswords=true` to the DSN since it is insecure
|
||||
- Added a `clientFoundRows` parameter: Return the number of matching rows instead of the number of rows changed on UPDATEs
|
||||
- Added TLS/SSL support: Use a TLS/SSL encrypted connection to the server. Custom TLS configs can be registered and used
|
||||
|
||||
Bugfixes:
|
||||
|
||||
- Fixed MySQL 4.1 support: MySQL 4.1 sends packets with lengths which differ from the specification
|
||||
- Convert to DB timezone when inserting `time.Time`
|
||||
- Splitted packets (more than 16MB) are now merged correctly
|
||||
- Fixed false positive `io.EOF` errors when the data was fully read
|
||||
- Avoid panics on reuse of closed connections
|
||||
- Fixed empty string producing false nil values
|
||||
- Fixed sign byte for positive TIME fields
|
||||
|
||||
|
||||
## Version 1.0 (2013-05-14)
|
||||
|
||||
Initial Release
|
||||
40
Godeps/_workspace/src/github.com/go-sql-driver/mysql/CONTRIBUTING.md
сгенерированный
поставляемый
Обычный файл
40
Godeps/_workspace/src/github.com/go-sql-driver/mysql/CONTRIBUTING.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,40 @@
|
||||
# Contributing Guidelines
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Before creating a new Issue, please check first if a similar Issue [already exists](https://github.com/go-sql-driver/mysql/issues?state=open) or was [recently closed](https://github.com/go-sql-driver/mysql/issues?direction=desc&page=1&sort=updated&state=closed).
|
||||
|
||||
Please provide the following minimum information:
|
||||
* Your Go-MySQL-Driver version (or git SHA)
|
||||
* Your Go version (run `go version` in your console)
|
||||
* A detailed issue description
|
||||
* Error Log if present
|
||||
* If possible, a short example
|
||||
|
||||
|
||||
## Contributing Code
|
||||
|
||||
By contributing to this project, you share your code under the Mozilla Public License 2, as specified in the LICENSE file.
|
||||
Don't forget to add yourself to the AUTHORS file.
|
||||
|
||||
### Pull Requests Checklist
|
||||
|
||||
Please check the following points before submitting your pull request:
|
||||
- [x] Code compiles correctly
|
||||
- [x] Created tests, if possible
|
||||
- [x] All tests pass
|
||||
- [x] Extended the README / documentation, if necessary
|
||||
- [x] Added yourself to the AUTHORS file
|
||||
|
||||
### Code Review
|
||||
|
||||
Everyone is invited to review and comment on pull requests.
|
||||
If it looks fine to you, comment with "LGTM" (Looks good to me).
|
||||
|
||||
If changes are required, notice the reviewers with "PTAL" (Please take another look) after committing the fixes.
|
||||
|
||||
Before merging the Pull Request, at least one [team member](https://github.com/go-sql-driver?tab=members) must have commented with "LGTM".
|
||||
|
||||
## Development Ideas
|
||||
|
||||
If you are looking for ideas for code contributions, please check our [Development Ideas](https://github.com/go-sql-driver/mysql/wiki/Development-Ideas) Wiki page.
|
||||
373
Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE
сгенерированный
поставляемый
Обычный файл
373
Godeps/_workspace/src/github.com/go-sql-driver/mysql/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
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/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
19
Godeps/_workspace/src/github.com/go-sql-driver/mysql/appengine.go
сгенерированный
поставляемый
Обычный файл
19
Godeps/_workspace/src/github.com/go-sql-driver/mysql/appengine.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,19 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"appengine/cloudsql"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterDial("cloudsql", cloudsql.Dial)
|
||||
}
|
||||
246
Godeps/_workspace/src/github.com/go-sql-driver/mysql/benchmark_test.go
сгенерированный
поставляемый
Обычный файл
246
Godeps/_workspace/src/github.com/go-sql-driver/mysql/benchmark_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,246 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TB testing.B
|
||||
|
||||
func (tb *TB) check(err error) {
|
||||
if err != nil {
|
||||
tb.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (tb *TB) checkDB(db *sql.DB, err error) *sql.DB {
|
||||
tb.check(err)
|
||||
return db
|
||||
}
|
||||
|
||||
func (tb *TB) checkRows(rows *sql.Rows, err error) *sql.Rows {
|
||||
tb.check(err)
|
||||
return rows
|
||||
}
|
||||
|
||||
func (tb *TB) checkStmt(stmt *sql.Stmt, err error) *sql.Stmt {
|
||||
tb.check(err)
|
||||
return stmt
|
||||
}
|
||||
|
||||
func initDB(b *testing.B, queries ...string) *sql.DB {
|
||||
tb := (*TB)(b)
|
||||
db := tb.checkDB(sql.Open("mysql", dsn))
|
||||
for _, query := range queries {
|
||||
if _, err := db.Exec(query); err != nil {
|
||||
if w, ok := err.(MySQLWarnings); ok {
|
||||
b.Logf("Warning on %q: %v", query, w)
|
||||
} else {
|
||||
b.Fatalf("Error on %q: %v", query, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
const concurrencyLevel = 10
|
||||
|
||||
func BenchmarkQuery(b *testing.B) {
|
||||
tb := (*TB)(b)
|
||||
b.StopTimer()
|
||||
b.ReportAllocs()
|
||||
db := initDB(b,
|
||||
"DROP TABLE IF EXISTS foo",
|
||||
"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
|
||||
`INSERT INTO foo VALUES (1, "one")`,
|
||||
`INSERT INTO foo VALUES (2, "two")`,
|
||||
)
|
||||
db.SetMaxIdleConns(concurrencyLevel)
|
||||
defer db.Close()
|
||||
|
||||
stmt := tb.checkStmt(db.Prepare("SELECT val FROM foo WHERE id=?"))
|
||||
defer stmt.Close()
|
||||
|
||||
remain := int64(b.N)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(concurrencyLevel)
|
||||
defer wg.Wait()
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < concurrencyLevel; i++ {
|
||||
go func() {
|
||||
for {
|
||||
if atomic.AddInt64(&remain, -1) < 0 {
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
|
||||
var got string
|
||||
tb.check(stmt.QueryRow(1).Scan(&got))
|
||||
if got != "one" {
|
||||
b.Errorf("query = %q; want one", got)
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkExec(b *testing.B) {
|
||||
tb := (*TB)(b)
|
||||
b.StopTimer()
|
||||
b.ReportAllocs()
|
||||
db := tb.checkDB(sql.Open("mysql", dsn))
|
||||
db.SetMaxIdleConns(concurrencyLevel)
|
||||
defer db.Close()
|
||||
|
||||
stmt := tb.checkStmt(db.Prepare("DO 1"))
|
||||
defer stmt.Close()
|
||||
|
||||
remain := int64(b.N)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(concurrencyLevel)
|
||||
defer wg.Wait()
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < concurrencyLevel; i++ {
|
||||
go func() {
|
||||
for {
|
||||
if atomic.AddInt64(&remain, -1) < 0 {
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := stmt.Exec(); err != nil {
|
||||
b.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// data, but no db writes
|
||||
var roundtripSample []byte
|
||||
|
||||
func initRoundtripBenchmarks() ([]byte, int, int) {
|
||||
if roundtripSample == nil {
|
||||
roundtripSample = []byte(strings.Repeat("0123456789abcdef", 1024*1024))
|
||||
}
|
||||
return roundtripSample, 16, len(roundtripSample)
|
||||
}
|
||||
|
||||
func BenchmarkRoundtripTxt(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sample, min, max := initRoundtripBenchmarks()
|
||||
sampleString := string(sample)
|
||||
b.ReportAllocs()
|
||||
tb := (*TB)(b)
|
||||
db := tb.checkDB(sql.Open("mysql", dsn))
|
||||
defer db.Close()
|
||||
b.StartTimer()
|
||||
var result string
|
||||
for i := 0; i < b.N; i++ {
|
||||
length := min + i
|
||||
if length > max {
|
||||
length = max
|
||||
}
|
||||
test := sampleString[0:length]
|
||||
rows := tb.checkRows(db.Query(`SELECT "` + test + `"`))
|
||||
if !rows.Next() {
|
||||
rows.Close()
|
||||
b.Fatalf("crashed")
|
||||
}
|
||||
err := rows.Scan(&result)
|
||||
if err != nil {
|
||||
rows.Close()
|
||||
b.Fatalf("crashed")
|
||||
}
|
||||
if result != test {
|
||||
rows.Close()
|
||||
b.Errorf("mismatch")
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRoundtripBin(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sample, min, max := initRoundtripBenchmarks()
|
||||
b.ReportAllocs()
|
||||
tb := (*TB)(b)
|
||||
db := tb.checkDB(sql.Open("mysql", dsn))
|
||||
defer db.Close()
|
||||
stmt := tb.checkStmt(db.Prepare("SELECT ?"))
|
||||
defer stmt.Close()
|
||||
b.StartTimer()
|
||||
var result sql.RawBytes
|
||||
for i := 0; i < b.N; i++ {
|
||||
length := min + i
|
||||
if length > max {
|
||||
length = max
|
||||
}
|
||||
test := sample[0:length]
|
||||
rows := tb.checkRows(stmt.Query(test))
|
||||
if !rows.Next() {
|
||||
rows.Close()
|
||||
b.Fatalf("crashed")
|
||||
}
|
||||
err := rows.Scan(&result)
|
||||
if err != nil {
|
||||
rows.Close()
|
||||
b.Fatalf("crashed")
|
||||
}
|
||||
if !bytes.Equal(result, test) {
|
||||
rows.Close()
|
||||
b.Errorf("mismatch")
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInterpolation(b *testing.B) {
|
||||
mc := &mysqlConn{
|
||||
cfg: &config{
|
||||
interpolateParams: true,
|
||||
loc: time.UTC,
|
||||
},
|
||||
maxPacketAllowed: maxPacketSize,
|
||||
maxWriteSize: maxPacketSize - 1,
|
||||
buf: newBuffer(nil),
|
||||
}
|
||||
|
||||
args := []driver.Value{
|
||||
int64(42424242),
|
||||
float64(math.Pi),
|
||||
false,
|
||||
time.Unix(1423411542, 807015000),
|
||||
[]byte("bytes containing special chars ' \" \a \x00"),
|
||||
"string containing special chars ' \" \a \x00",
|
||||
}
|
||||
q := "SELECT ?, ?, ?, ?, ?, ?"
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := mc.interpolateParams(q, args)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
136
Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go
сгенерированный
поставляемый
Обычный файл
136
Godeps/_workspace/src/github.com/go-sql-driver/mysql/buffer.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,136 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 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 "io"
|
||||
|
||||
const defaultBufSize = 4096
|
||||
|
||||
// A buffer which is used for both reading and writing.
|
||||
// This is possible since communication on each connection is synchronous.
|
||||
// In other words, we can't write and read simultaneously on the same connection.
|
||||
// The buffer is similar to bufio.Reader / Writer but zero-copy-ish
|
||||
// Also highly optimized for this particular use case.
|
||||
type buffer struct {
|
||||
buf []byte
|
||||
rd io.Reader
|
||||
idx int
|
||||
length int
|
||||
}
|
||||
|
||||
func newBuffer(rd io.Reader) buffer {
|
||||
var b [defaultBufSize]byte
|
||||
return buffer{
|
||||
buf: b[:],
|
||||
rd: rd,
|
||||
}
|
||||
}
|
||||
|
||||
// fill reads into the buffer until at least _need_ bytes are in it
|
||||
func (b *buffer) fill(need int) error {
|
||||
n := b.length
|
||||
|
||||
// move existing data to the beginning
|
||||
if n > 0 && b.idx > 0 {
|
||||
copy(b.buf[0:n], b.buf[b.idx:])
|
||||
}
|
||||
|
||||
// grow buffer if necessary
|
||||
// TODO: let the buffer shrink again at some point
|
||||
// Maybe keep the org buf slice and swap back?
|
||||
if need > len(b.buf) {
|
||||
// Round up to the next multiple of the default size
|
||||
newBuf := make([]byte, ((need/defaultBufSize)+1)*defaultBufSize)
|
||||
copy(newBuf, b.buf)
|
||||
b.buf = newBuf
|
||||
}
|
||||
|
||||
b.idx = 0
|
||||
|
||||
for {
|
||||
nn, err := b.rd.Read(b.buf[n:])
|
||||
n += nn
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
if n < need {
|
||||
continue
|
||||
}
|
||||
b.length = n
|
||||
return nil
|
||||
|
||||
case io.EOF:
|
||||
if n >= need {
|
||||
b.length = n
|
||||
return nil
|
||||
}
|
||||
return io.ErrUnexpectedEOF
|
||||
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns next N bytes from buffer.
|
||||
// The returned slice is only guaranteed to be valid until the next read
|
||||
func (b *buffer) readNext(need int) ([]byte, error) {
|
||||
if b.length < need {
|
||||
// refill
|
||||
if err := b.fill(need); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
offset := b.idx
|
||||
b.idx += need
|
||||
b.length -= need
|
||||
return b.buf[offset:b.idx], nil
|
||||
}
|
||||
|
||||
// returns a buffer with the requested size.
|
||||
// If possible, a slice from the existing buffer is returned.
|
||||
// Otherwise a bigger buffer is made.
|
||||
// Only one buffer (total) can be used at a time.
|
||||
func (b *buffer) takeBuffer(length int) []byte {
|
||||
if b.length > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// test (cheap) general case first
|
||||
if length <= defaultBufSize || length <= cap(b.buf) {
|
||||
return b.buf[:length]
|
||||
}
|
||||
|
||||
if length < maxPacketSize {
|
||||
b.buf = make([]byte, length)
|
||||
return b.buf
|
||||
}
|
||||
return make([]byte, length)
|
||||
}
|
||||
|
||||
// shortcut which can be used if the requested buffer is guaranteed to be
|
||||
// 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]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
return nil
|
||||
}
|
||||
250
Godeps/_workspace/src/github.com/go-sql-driver/mysql/collations.go
сгенерированный
поставляемый
Обычный файл
250
Godeps/_workspace/src/github.com/go-sql-driver/mysql/collations.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,250 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2014 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
|
||||
|
||||
const defaultCollation byte = 33 // utf8_general_ci
|
||||
|
||||
// A list of available collations mapped to the internal ID.
|
||||
// To update this map use the following MySQL query:
|
||||
// SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS
|
||||
var collations = map[string]byte{
|
||||
"big5_chinese_ci": 1,
|
||||
"latin2_czech_cs": 2,
|
||||
"dec8_swedish_ci": 3,
|
||||
"cp850_general_ci": 4,
|
||||
"latin1_german1_ci": 5,
|
||||
"hp8_english_ci": 6,
|
||||
"koi8r_general_ci": 7,
|
||||
"latin1_swedish_ci": 8,
|
||||
"latin2_general_ci": 9,
|
||||
"swe7_swedish_ci": 10,
|
||||
"ascii_general_ci": 11,
|
||||
"ujis_japanese_ci": 12,
|
||||
"sjis_japanese_ci": 13,
|
||||
"cp1251_bulgarian_ci": 14,
|
||||
"latin1_danish_ci": 15,
|
||||
"hebrew_general_ci": 16,
|
||||
"tis620_thai_ci": 18,
|
||||
"euckr_korean_ci": 19,
|
||||
"latin7_estonian_cs": 20,
|
||||
"latin2_hungarian_ci": 21,
|
||||
"koi8u_general_ci": 22,
|
||||
"cp1251_ukrainian_ci": 23,
|
||||
"gb2312_chinese_ci": 24,
|
||||
"greek_general_ci": 25,
|
||||
"cp1250_general_ci": 26,
|
||||
"latin2_croatian_ci": 27,
|
||||
"gbk_chinese_ci": 28,
|
||||
"cp1257_lithuanian_ci": 29,
|
||||
"latin5_turkish_ci": 30,
|
||||
"latin1_german2_ci": 31,
|
||||
"armscii8_general_ci": 32,
|
||||
"utf8_general_ci": 33,
|
||||
"cp1250_czech_cs": 34,
|
||||
"ucs2_general_ci": 35,
|
||||
"cp866_general_ci": 36,
|
||||
"keybcs2_general_ci": 37,
|
||||
"macce_general_ci": 38,
|
||||
"macroman_general_ci": 39,
|
||||
"cp852_general_ci": 40,
|
||||
"latin7_general_ci": 41,
|
||||
"latin7_general_cs": 42,
|
||||
"macce_bin": 43,
|
||||
"cp1250_croatian_ci": 44,
|
||||
"utf8mb4_general_ci": 45,
|
||||
"utf8mb4_bin": 46,
|
||||
"latin1_bin": 47,
|
||||
"latin1_general_ci": 48,
|
||||
"latin1_general_cs": 49,
|
||||
"cp1251_bin": 50,
|
||||
"cp1251_general_ci": 51,
|
||||
"cp1251_general_cs": 52,
|
||||
"macroman_bin": 53,
|
||||
"utf16_general_ci": 54,
|
||||
"utf16_bin": 55,
|
||||
"utf16le_general_ci": 56,
|
||||
"cp1256_general_ci": 57,
|
||||
"cp1257_bin": 58,
|
||||
"cp1257_general_ci": 59,
|
||||
"utf32_general_ci": 60,
|
||||
"utf32_bin": 61,
|
||||
"utf16le_bin": 62,
|
||||
"binary": 63,
|
||||
"armscii8_bin": 64,
|
||||
"ascii_bin": 65,
|
||||
"cp1250_bin": 66,
|
||||
"cp1256_bin": 67,
|
||||
"cp866_bin": 68,
|
||||
"dec8_bin": 69,
|
||||
"greek_bin": 70,
|
||||
"hebrew_bin": 71,
|
||||
"hp8_bin": 72,
|
||||
"keybcs2_bin": 73,
|
||||
"koi8r_bin": 74,
|
||||
"koi8u_bin": 75,
|
||||
"latin2_bin": 77,
|
||||
"latin5_bin": 78,
|
||||
"latin7_bin": 79,
|
||||
"cp850_bin": 80,
|
||||
"cp852_bin": 81,
|
||||
"swe7_bin": 82,
|
||||
"utf8_bin": 83,
|
||||
"big5_bin": 84,
|
||||
"euckr_bin": 85,
|
||||
"gb2312_bin": 86,
|
||||
"gbk_bin": 87,
|
||||
"sjis_bin": 88,
|
||||
"tis620_bin": 89,
|
||||
"ucs2_bin": 90,
|
||||
"ujis_bin": 91,
|
||||
"geostd8_general_ci": 92,
|
||||
"geostd8_bin": 93,
|
||||
"latin1_spanish_ci": 94,
|
||||
"cp932_japanese_ci": 95,
|
||||
"cp932_bin": 96,
|
||||
"eucjpms_japanese_ci": 97,
|
||||
"eucjpms_bin": 98,
|
||||
"cp1250_polish_ci": 99,
|
||||
"utf16_unicode_ci": 101,
|
||||
"utf16_icelandic_ci": 102,
|
||||
"utf16_latvian_ci": 103,
|
||||
"utf16_romanian_ci": 104,
|
||||
"utf16_slovenian_ci": 105,
|
||||
"utf16_polish_ci": 106,
|
||||
"utf16_estonian_ci": 107,
|
||||
"utf16_spanish_ci": 108,
|
||||
"utf16_swedish_ci": 109,
|
||||
"utf16_turkish_ci": 110,
|
||||
"utf16_czech_ci": 111,
|
||||
"utf16_danish_ci": 112,
|
||||
"utf16_lithuanian_ci": 113,
|
||||
"utf16_slovak_ci": 114,
|
||||
"utf16_spanish2_ci": 115,
|
||||
"utf16_roman_ci": 116,
|
||||
"utf16_persian_ci": 117,
|
||||
"utf16_esperanto_ci": 118,
|
||||
"utf16_hungarian_ci": 119,
|
||||
"utf16_sinhala_ci": 120,
|
||||
"utf16_german2_ci": 121,
|
||||
"utf16_croatian_ci": 122,
|
||||
"utf16_unicode_520_ci": 123,
|
||||
"utf16_vietnamese_ci": 124,
|
||||
"ucs2_unicode_ci": 128,
|
||||
"ucs2_icelandic_ci": 129,
|
||||
"ucs2_latvian_ci": 130,
|
||||
"ucs2_romanian_ci": 131,
|
||||
"ucs2_slovenian_ci": 132,
|
||||
"ucs2_polish_ci": 133,
|
||||
"ucs2_estonian_ci": 134,
|
||||
"ucs2_spanish_ci": 135,
|
||||
"ucs2_swedish_ci": 136,
|
||||
"ucs2_turkish_ci": 137,
|
||||
"ucs2_czech_ci": 138,
|
||||
"ucs2_danish_ci": 139,
|
||||
"ucs2_lithuanian_ci": 140,
|
||||
"ucs2_slovak_ci": 141,
|
||||
"ucs2_spanish2_ci": 142,
|
||||
"ucs2_roman_ci": 143,
|
||||
"ucs2_persian_ci": 144,
|
||||
"ucs2_esperanto_ci": 145,
|
||||
"ucs2_hungarian_ci": 146,
|
||||
"ucs2_sinhala_ci": 147,
|
||||
"ucs2_german2_ci": 148,
|
||||
"ucs2_croatian_ci": 149,
|
||||
"ucs2_unicode_520_ci": 150,
|
||||
"ucs2_vietnamese_ci": 151,
|
||||
"ucs2_general_mysql500_ci": 159,
|
||||
"utf32_unicode_ci": 160,
|
||||
"utf32_icelandic_ci": 161,
|
||||
"utf32_latvian_ci": 162,
|
||||
"utf32_romanian_ci": 163,
|
||||
"utf32_slovenian_ci": 164,
|
||||
"utf32_polish_ci": 165,
|
||||
"utf32_estonian_ci": 166,
|
||||
"utf32_spanish_ci": 167,
|
||||
"utf32_swedish_ci": 168,
|
||||
"utf32_turkish_ci": 169,
|
||||
"utf32_czech_ci": 170,
|
||||
"utf32_danish_ci": 171,
|
||||
"utf32_lithuanian_ci": 172,
|
||||
"utf32_slovak_ci": 173,
|
||||
"utf32_spanish2_ci": 174,
|
||||
"utf32_roman_ci": 175,
|
||||
"utf32_persian_ci": 176,
|
||||
"utf32_esperanto_ci": 177,
|
||||
"utf32_hungarian_ci": 178,
|
||||
"utf32_sinhala_ci": 179,
|
||||
"utf32_german2_ci": 180,
|
||||
"utf32_croatian_ci": 181,
|
||||
"utf32_unicode_520_ci": 182,
|
||||
"utf32_vietnamese_ci": 183,
|
||||
"utf8_unicode_ci": 192,
|
||||
"utf8_icelandic_ci": 193,
|
||||
"utf8_latvian_ci": 194,
|
||||
"utf8_romanian_ci": 195,
|
||||
"utf8_slovenian_ci": 196,
|
||||
"utf8_polish_ci": 197,
|
||||
"utf8_estonian_ci": 198,
|
||||
"utf8_spanish_ci": 199,
|
||||
"utf8_swedish_ci": 200,
|
||||
"utf8_turkish_ci": 201,
|
||||
"utf8_czech_ci": 202,
|
||||
"utf8_danish_ci": 203,
|
||||
"utf8_lithuanian_ci": 204,
|
||||
"utf8_slovak_ci": 205,
|
||||
"utf8_spanish2_ci": 206,
|
||||
"utf8_roman_ci": 207,
|
||||
"utf8_persian_ci": 208,
|
||||
"utf8_esperanto_ci": 209,
|
||||
"utf8_hungarian_ci": 210,
|
||||
"utf8_sinhala_ci": 211,
|
||||
"utf8_german2_ci": 212,
|
||||
"utf8_croatian_ci": 213,
|
||||
"utf8_unicode_520_ci": 214,
|
||||
"utf8_vietnamese_ci": 215,
|
||||
"utf8_general_mysql500_ci": 223,
|
||||
"utf8mb4_unicode_ci": 224,
|
||||
"utf8mb4_icelandic_ci": 225,
|
||||
"utf8mb4_latvian_ci": 226,
|
||||
"utf8mb4_romanian_ci": 227,
|
||||
"utf8mb4_slovenian_ci": 228,
|
||||
"utf8mb4_polish_ci": 229,
|
||||
"utf8mb4_estonian_ci": 230,
|
||||
"utf8mb4_spanish_ci": 231,
|
||||
"utf8mb4_swedish_ci": 232,
|
||||
"utf8mb4_turkish_ci": 233,
|
||||
"utf8mb4_czech_ci": 234,
|
||||
"utf8mb4_danish_ci": 235,
|
||||
"utf8mb4_lithuanian_ci": 236,
|
||||
"utf8mb4_slovak_ci": 237,
|
||||
"utf8mb4_spanish2_ci": 238,
|
||||
"utf8mb4_roman_ci": 239,
|
||||
"utf8mb4_persian_ci": 240,
|
||||
"utf8mb4_esperanto_ci": 241,
|
||||
"utf8mb4_hungarian_ci": 242,
|
||||
"utf8mb4_sinhala_ci": 243,
|
||||
"utf8mb4_german2_ci": 244,
|
||||
"utf8mb4_croatian_ci": 245,
|
||||
"utf8mb4_unicode_520_ci": 246,
|
||||
"utf8mb4_vietnamese_ci": 247,
|
||||
}
|
||||
|
||||
// A blacklist of collations which is unsafe to interpolate parameters.
|
||||
// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
|
||||
var unsafeCollations = map[byte]bool{
|
||||
1: true, // big5_chinese_ci
|
||||
13: true, // sjis_japanese_ci
|
||||
28: true, // gbk_chinese_ci
|
||||
84: true, // big5_bin
|
||||
86: true, // gb2312_bin
|
||||
87: true, // gbk_bin
|
||||
88: true, // sjis_bin
|
||||
95: true, // cp932_japanese_ci
|
||||
96: true, // cp932_bin
|
||||
}
|
||||
402
Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go
сгенерированный
поставляемый
Обычный файл
402
Godeps/_workspace/src/github.com/go-sql-driver/mysql/connection.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,402 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type mysqlConn struct {
|
||||
buf buffer
|
||||
netConn net.Conn
|
||||
affectedRows uint64
|
||||
insertId uint64
|
||||
cfg *config
|
||||
maxPacketAllowed int
|
||||
maxWriteSize int
|
||||
flags clientFlag
|
||||
status statusFlag
|
||||
sequence uint8
|
||||
parseTime bool
|
||||
strict bool
|
||||
}
|
||||
|
||||
type config struct {
|
||||
user string
|
||||
passwd string
|
||||
net string
|
||||
addr string
|
||||
dbname string
|
||||
params map[string]string
|
||||
loc *time.Location
|
||||
tls *tls.Config
|
||||
timeout time.Duration
|
||||
collation uint8
|
||||
allowAllFiles bool
|
||||
allowOldPasswords bool
|
||||
clientFoundRows bool
|
||||
columnsWithAlias bool
|
||||
interpolateParams bool
|
||||
}
|
||||
|
||||
// Handles parameters set in DSN after the connection is established
|
||||
func (mc *mysqlConn) handleParams() (err error) {
|
||||
for param, val := range mc.cfg.params {
|
||||
switch param {
|
||||
// Charset
|
||||
case "charset":
|
||||
charsets := strings.Split(val, ",")
|
||||
for i := range charsets {
|
||||
// ignore errors here - a charset may not exist
|
||||
err = mc.exec("SET NAMES " + charsets[i])
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// time.Time parsing
|
||||
case "parseTime":
|
||||
var isBool bool
|
||||
mc.parseTime, isBool = readBool(val)
|
||||
if !isBool {
|
||||
return errors.New("Invalid Bool value: " + val)
|
||||
}
|
||||
|
||||
// Strict mode
|
||||
case "strict":
|
||||
var isBool bool
|
||||
mc.strict, isBool = readBool(val)
|
||||
if !isBool {
|
||||
return errors.New("Invalid Bool value: " + val)
|
||||
}
|
||||
|
||||
// Compression
|
||||
case "compress":
|
||||
err = errors.New("Compression not implemented yet")
|
||||
return
|
||||
|
||||
// System Vars
|
||||
default:
|
||||
err = mc.exec("SET " + param + "=" + val + "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Begin() (driver.Tx, error) {
|
||||
if mc.netConn == nil {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
err := mc.exec("START TRANSACTION")
|
||||
if err == nil {
|
||||
return &mysqlTx{mc}, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Close() (err error) {
|
||||
// Makes Close idempotent
|
||||
if mc.netConn != nil {
|
||||
err = mc.writeCommandPacket(comQuit)
|
||||
if err == nil {
|
||||
err = mc.netConn.Close()
|
||||
} else {
|
||||
mc.netConn.Close()
|
||||
}
|
||||
mc.netConn = nil
|
||||
}
|
||||
|
||||
mc.cfg = nil
|
||||
mc.buf.rd = nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
|
||||
if mc.netConn == nil {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comStmtPrepare, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stmt := &mysqlStmt{
|
||||
mc: mc,
|
||||
}
|
||||
|
||||
// Read Result
|
||||
columnCount, err := stmt.readPrepareResultPacket()
|
||||
if err == nil {
|
||||
if stmt.paramCount > 0 {
|
||||
if err = mc.readUntilEOF(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if columnCount > 0 {
|
||||
err = mc.readUntilEOF()
|
||||
}
|
||||
}
|
||||
|
||||
return stmt, err
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) {
|
||||
buf := mc.buf.takeCompleteBuffer()
|
||||
if buf == nil {
|
||||
// can not take the buffer. Something must be wrong with the connection
|
||||
errLog.Print(ErrBusyBuffer)
|
||||
return "", driver.ErrBadConn
|
||||
}
|
||||
buf = buf[:0]
|
||||
argPos := 0
|
||||
|
||||
for i := 0; i < len(query); i++ {
|
||||
q := strings.IndexByte(query[i:], '?')
|
||||
if q == -1 {
|
||||
buf = append(buf, query[i:]...)
|
||||
break
|
||||
}
|
||||
buf = append(buf, query[i:i+q]...)
|
||||
i += q
|
||||
|
||||
arg := args[argPos]
|
||||
argPos++
|
||||
|
||||
if arg == nil {
|
||||
buf = append(buf, "NULL"...)
|
||||
continue
|
||||
}
|
||||
|
||||
switch v := arg.(type) {
|
||||
case int64:
|
||||
buf = strconv.AppendInt(buf, v, 10)
|
||||
case float64:
|
||||
buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
|
||||
case bool:
|
||||
if v {
|
||||
buf = append(buf, '1')
|
||||
} else {
|
||||
buf = append(buf, '0')
|
||||
}
|
||||
case time.Time:
|
||||
if v.IsZero() {
|
||||
buf = append(buf, "'0000-00-00'"...)
|
||||
} else {
|
||||
v := v.In(mc.cfg.loc)
|
||||
v = v.Add(time.Nanosecond * 500) // To round under microsecond
|
||||
year := v.Year()
|
||||
year100 := year / 100
|
||||
year1 := year % 100
|
||||
month := v.Month()
|
||||
day := v.Day()
|
||||
hour := v.Hour()
|
||||
minute := v.Minute()
|
||||
second := v.Second()
|
||||
micro := v.Nanosecond() / 1000
|
||||
|
||||
buf = append(buf, []byte{
|
||||
'\'',
|
||||
digits10[year100], digits01[year100],
|
||||
digits10[year1], digits01[year1],
|
||||
'-',
|
||||
digits10[month], digits01[month],
|
||||
'-',
|
||||
digits10[day], digits01[day],
|
||||
' ',
|
||||
digits10[hour], digits01[hour],
|
||||
':',
|
||||
digits10[minute], digits01[minute],
|
||||
':',
|
||||
digits10[second], digits01[second],
|
||||
}...)
|
||||
|
||||
if micro != 0 {
|
||||
micro10000 := micro / 10000
|
||||
micro100 := micro / 100 % 100
|
||||
micro1 := micro % 100
|
||||
buf = append(buf, []byte{
|
||||
'.',
|
||||
digits10[micro10000], digits01[micro10000],
|
||||
digits10[micro100], digits01[micro100],
|
||||
digits10[micro1], digits01[micro1],
|
||||
}...)
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
}
|
||||
case []byte:
|
||||
if v == nil {
|
||||
buf = append(buf, "NULL"...)
|
||||
} else {
|
||||
buf = append(buf, '\'')
|
||||
if mc.status&statusNoBackslashEscapes == 0 {
|
||||
buf = escapeBytesBackslash(buf, v)
|
||||
} else {
|
||||
buf = escapeBytesQuotes(buf, v)
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
}
|
||||
case string:
|
||||
buf = append(buf, '\'')
|
||||
if mc.status&statusNoBackslashEscapes == 0 {
|
||||
buf = escapeStringBackslash(buf, v)
|
||||
} else {
|
||||
buf = escapeStringQuotes(buf, v)
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
default:
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
|
||||
if len(buf)+4 > mc.maxPacketAllowed {
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
}
|
||||
if argPos != len(args) {
|
||||
return "", driver.ErrSkip
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
||||
if mc.netConn == nil {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
if len(args) != 0 {
|
||||
if !mc.cfg.interpolateParams {
|
||||
return nil, driver.ErrSkip
|
||||
}
|
||||
// try to interpolate the parameters to save extra roundtrips for preparing and closing a statement
|
||||
prepared, err := mc.interpolateParams(query, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query = prepared
|
||||
args = nil
|
||||
}
|
||||
mc.affectedRows = 0
|
||||
mc.insertId = 0
|
||||
|
||||
err := mc.exec(query)
|
||||
if err == nil {
|
||||
return &mysqlResult{
|
||||
affectedRows: int64(mc.affectedRows),
|
||||
insertId: int64(mc.insertId),
|
||||
}, err
|
||||
}
|
||||
return nil, 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
|
||||
}
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
if err == nil && resLen > 0 {
|
||||
if err = mc.readUntilEOF(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = mc.readUntilEOF()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
||||
if mc.netConn == nil {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
if len(args) != 0 {
|
||||
if !mc.cfg.interpolateParams {
|
||||
return nil, driver.ErrSkip
|
||||
}
|
||||
// try client-side prepare to reduce roundtrip
|
||||
prepared, err := mc.interpolateParams(query, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query = prepared
|
||||
args = nil
|
||||
}
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comQuery, query)
|
||||
if err == nil {
|
||||
// Read Result
|
||||
var resLen int
|
||||
resLen, err = mc.readResultSetHeaderPacket()
|
||||
if err == nil {
|
||||
rows := new(textRows)
|
||||
rows.mc = mc
|
||||
|
||||
if resLen == 0 {
|
||||
// no columns, no more data
|
||||
return emptyRows{}, nil
|
||||
}
|
||||
// Columns
|
||||
rows.columns, err = mc.readColumns(resLen)
|
||||
return rows, err
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Gets the value of the given MySQL System Variable
|
||||
// The returned byte slice is only valid until the next read
|
||||
func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
||||
// Send command
|
||||
if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
if err == nil {
|
||||
rows := new(textRows)
|
||||
rows.mc = mc
|
||||
|
||||
if resLen > 0 {
|
||||
// Columns
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
dest := make([]driver.Value, resLen)
|
||||
if err = rows.readRow(dest); err == nil {
|
||||
return dest[0].([]byte), mc.readUntilEOF()
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
162
Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go
сгенерированный
поставляемый
Обычный файл
162
Godeps/_workspace/src/github.com/go-sql-driver/mysql/const.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,162 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
const (
|
||||
minProtocolVersion byte = 10
|
||||
maxPacketSize = 1<<24 - 1
|
||||
timeFormat = "2006-01-02 15:04:05.999999"
|
||||
)
|
||||
|
||||
// MySQL constants documentation:
|
||||
// http://dev.mysql.com/doc/internals/en/client-server-protocol.html
|
||||
|
||||
const (
|
||||
iOK byte = 0x00
|
||||
iLocalInFile byte = 0xfb
|
||||
iEOF byte = 0xfe
|
||||
iERR byte = 0xff
|
||||
)
|
||||
|
||||
// https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags
|
||||
type clientFlag uint32
|
||||
|
||||
const (
|
||||
clientLongPassword clientFlag = 1 << iota
|
||||
clientFoundRows
|
||||
clientLongFlag
|
||||
clientConnectWithDB
|
||||
clientNoSchema
|
||||
clientCompress
|
||||
clientODBC
|
||||
clientLocalFiles
|
||||
clientIgnoreSpace
|
||||
clientProtocol41
|
||||
clientInteractive
|
||||
clientSSL
|
||||
clientIgnoreSIGPIPE
|
||||
clientTransactions
|
||||
clientReserved
|
||||
clientSecureConn
|
||||
clientMultiStatements
|
||||
clientMultiResults
|
||||
clientPSMultiResults
|
||||
clientPluginAuth
|
||||
clientConnectAttrs
|
||||
clientPluginAuthLenEncClientData
|
||||
clientCanHandleExpiredPasswords
|
||||
clientSessionTrack
|
||||
clientDeprecateEOF
|
||||
)
|
||||
|
||||
const (
|
||||
comQuit byte = iota + 1
|
||||
comInitDB
|
||||
comQuery
|
||||
comFieldList
|
||||
comCreateDB
|
||||
comDropDB
|
||||
comRefresh
|
||||
comShutdown
|
||||
comStatistics
|
||||
comProcessInfo
|
||||
comConnect
|
||||
comProcessKill
|
||||
comDebug
|
||||
comPing
|
||||
comTime
|
||||
comDelayedInsert
|
||||
comChangeUser
|
||||
comBinlogDump
|
||||
comTableDump
|
||||
comConnectOut
|
||||
comRegisterSlave
|
||||
comStmtPrepare
|
||||
comStmtExecute
|
||||
comStmtSendLongData
|
||||
comStmtClose
|
||||
comStmtReset
|
||||
comSetOption
|
||||
comStmtFetch
|
||||
)
|
||||
|
||||
// https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType
|
||||
const (
|
||||
fieldTypeDecimal byte = iota
|
||||
fieldTypeTiny
|
||||
fieldTypeShort
|
||||
fieldTypeLong
|
||||
fieldTypeFloat
|
||||
fieldTypeDouble
|
||||
fieldTypeNULL
|
||||
fieldTypeTimestamp
|
||||
fieldTypeLongLong
|
||||
fieldTypeInt24
|
||||
fieldTypeDate
|
||||
fieldTypeTime
|
||||
fieldTypeDateTime
|
||||
fieldTypeYear
|
||||
fieldTypeNewDate
|
||||
fieldTypeVarChar
|
||||
fieldTypeBit
|
||||
)
|
||||
const (
|
||||
fieldTypeNewDecimal byte = iota + 0xf6
|
||||
fieldTypeEnum
|
||||
fieldTypeSet
|
||||
fieldTypeTinyBLOB
|
||||
fieldTypeMediumBLOB
|
||||
fieldTypeLongBLOB
|
||||
fieldTypeBLOB
|
||||
fieldTypeVarString
|
||||
fieldTypeString
|
||||
fieldTypeGeometry
|
||||
)
|
||||
|
||||
type fieldFlag uint16
|
||||
|
||||
const (
|
||||
flagNotNULL fieldFlag = 1 << iota
|
||||
flagPriKey
|
||||
flagUniqueKey
|
||||
flagMultipleKey
|
||||
flagBLOB
|
||||
flagUnsigned
|
||||
flagZeroFill
|
||||
flagBinary
|
||||
flagEnum
|
||||
flagAutoIncrement
|
||||
flagTimestamp
|
||||
flagSet
|
||||
flagUnknown1
|
||||
flagUnknown2
|
||||
flagUnknown3
|
||||
flagUnknown4
|
||||
)
|
||||
|
||||
// http://dev.mysql.com/doc/internals/en/status-flags.html
|
||||
type statusFlag uint16
|
||||
|
||||
const (
|
||||
statusInTrans statusFlag = 1 << iota
|
||||
statusInAutocommit
|
||||
statusReserved // Not in documentation
|
||||
statusMoreResultsExists
|
||||
statusNoGoodIndexUsed
|
||||
statusNoIndexUsed
|
||||
statusCursorExists
|
||||
statusLastRowSent
|
||||
statusDbDropped
|
||||
statusNoBackslashEscapes
|
||||
statusMetadataChanged
|
||||
statusQueryWasSlow
|
||||
statusPsOutParams
|
||||
statusInTransReadonly
|
||||
statusSessionStateChanged
|
||||
)
|
||||
140
Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go
сгенерированный
поставляемый
Обычный файл
140
Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,140 @@
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// The driver should be used via the database/sql package:
|
||||
//
|
||||
// import "database/sql"
|
||||
// import _ "github.com/go-sql-driver/mysql"
|
||||
//
|
||||
// db, err := sql.Open("mysql", "user:password@/dbname")
|
||||
//
|
||||
// See https://github.com/go-sql-driver/mysql#usage for details
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"net"
|
||||
)
|
||||
|
||||
// This struct is exported to make the driver directly accessible.
|
||||
// In general the driver is used via the database/sql package.
|
||||
type MySQLDriver struct{}
|
||||
|
||||
// DialFunc is a function which can be used to establish the network connection.
|
||||
// Custom dial functions must be registered with RegisterDial
|
||||
type DialFunc func(addr string) (net.Conn, error)
|
||||
|
||||
var dials map[string]DialFunc
|
||||
|
||||
// RegisterDial registers a custom dial function. It can then be used by the
|
||||
// network address mynet(addr), where mynet is the registered new network.
|
||||
// addr is passed as a parameter to the dial function.
|
||||
func RegisterDial(net string, dial DialFunc) {
|
||||
if dials == nil {
|
||||
dials = make(map[string]DialFunc)
|
||||
}
|
||||
dials[net] = dial
|
||||
}
|
||||
|
||||
// Open new Connection.
|
||||
// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how
|
||||
// the DSN string is formated
|
||||
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
|
||||
var err error
|
||||
|
||||
// New mysqlConn
|
||||
mc := &mysqlConn{
|
||||
maxPacketAllowed: maxPacketSize,
|
||||
maxWriteSize: maxPacketSize - 1,
|
||||
}
|
||||
mc.cfg, err = parseDSN(dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Connect to Server
|
||||
if dial, ok := dials[mc.cfg.net]; ok {
|
||||
mc.netConn, err = dial(mc.cfg.addr)
|
||||
} else {
|
||||
nd := net.Dialer{Timeout: mc.cfg.timeout}
|
||||
mc.netConn, err = nd.Dial(mc.cfg.net, mc.cfg.addr)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Enable TCP Keepalives on TCP connections
|
||||
if tc, ok := mc.netConn.(*net.TCPConn); ok {
|
||||
if err := tc.SetKeepAlive(true); err != nil {
|
||||
// Don't send COM_QUIT before handshake.
|
||||
mc.netConn.Close()
|
||||
mc.netConn = nil
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
mc.buf = newBuffer(mc.netConn)
|
||||
|
||||
// Reading Handshake Initialization Packet
|
||||
cipher, err := mc.readInitPacket()
|
||||
if err != nil {
|
||||
mc.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Send Client Authentication Packet
|
||||
if err = mc.writeAuthPacket(cipher); err != nil {
|
||||
mc.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read Result Packet
|
||||
err = mc.readResultOK()
|
||||
if err != nil {
|
||||
// Retry with old authentication method, if allowed
|
||||
if mc.cfg != nil && mc.cfg.allowOldPasswords && err == ErrOldPassword {
|
||||
if err = mc.writeOldAuthPacket(cipher); err != nil {
|
||||
mc.Close()
|
||||
return nil, err
|
||||
}
|
||||
if err = mc.readResultOK(); err != nil {
|
||||
mc.Close()
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
mc.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Get max allowed packet size
|
||||
maxap, err := mc.getSystemVar("max_allowed_packet")
|
||||
if err != nil {
|
||||
mc.Close()
|
||||
return nil, err
|
||||
}
|
||||
mc.maxPacketAllowed = stringToInt(maxap) - 1
|
||||
if mc.maxPacketAllowed < maxPacketSize {
|
||||
mc.maxWriteSize = mc.maxPacketAllowed
|
||||
}
|
||||
|
||||
// Handle DSN Params
|
||||
err = mc.handleParams()
|
||||
if err != nil {
|
||||
mc.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mc, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
sql.Register("mysql", &MySQLDriver{})
|
||||
}
|
||||
1657
Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go
сгенерированный
поставляемый
Обычный файл
1657
Godeps/_workspace/src/github.com/go-sql-driver/mysql/driver_test.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
129
Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go
сгенерированный
поставляемый
Обычный файл
129
Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,129 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Various errors the driver might return. Can change between driver versions.
|
||||
var (
|
||||
ErrInvalidConn = errors.New("Invalid Connection")
|
||||
ErrMalformPkt = errors.New("Malformed Packet")
|
||||
ErrNoTLS = errors.New("TLS encryption requested but server does not support TLS")
|
||||
ErrOldPassword = errors.New("This server only supports the insecure old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords")
|
||||
ErrOldProtocol = errors.New("MySQL-Server does not support required Protocol 41+")
|
||||
ErrPktSync = errors.New("Commands out of sync. You can't run this command now")
|
||||
ErrPktSyncMul = errors.New("Commands out of sync. Did you run multiple statements at once?")
|
||||
ErrPktTooLarge = errors.New("Packet for query is too large. You can change this value on the server by adjusting the 'max_allowed_packet' variable.")
|
||||
ErrBusyBuffer = errors.New("Busy buffer")
|
||||
)
|
||||
|
||||
var errLog Logger = log.New(os.Stderr, "[MySQL] ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
||||
// Logger is used to log critical error messages.
|
||||
type Logger interface {
|
||||
Print(v ...interface{})
|
||||
}
|
||||
|
||||
// SetLogger is used to set the logger for critical errors.
|
||||
// The initial logger is os.Stderr.
|
||||
func SetLogger(logger Logger) error {
|
||||
if logger == nil {
|
||||
return errors.New("logger is nil")
|
||||
}
|
||||
errLog = logger
|
||||
return nil
|
||||
}
|
||||
|
||||
// MySQLError is an error type which represents a single MySQL error
|
||||
type MySQLError struct {
|
||||
Number uint16
|
||||
Message string
|
||||
}
|
||||
|
||||
func (me *MySQLError) Error() string {
|
||||
return fmt.Sprintf("Error %d: %s", me.Number, me.Message)
|
||||
}
|
||||
|
||||
// MySQLWarnings is an error type which represents a group of one or more MySQL
|
||||
// warnings
|
||||
type MySQLWarnings []MySQLWarning
|
||||
|
||||
func (mws MySQLWarnings) Error() string {
|
||||
var msg string
|
||||
for i, warning := range mws {
|
||||
if i > 0 {
|
||||
msg += "\r\n"
|
||||
}
|
||||
msg += fmt.Sprintf(
|
||||
"%s %s: %s",
|
||||
warning.Level,
|
||||
warning.Code,
|
||||
warning.Message,
|
||||
)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// MySQLWarning is an error type which represents a single MySQL warning.
|
||||
// Warnings are returned in groups only. See MySQLWarnings
|
||||
type MySQLWarning struct {
|
||||
Level string
|
||||
Code string
|
||||
Message string
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) getWarnings() (err error) {
|
||||
rows, err := mc.Query("SHOW WARNINGS", nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var warnings = MySQLWarnings{}
|
||||
var values = make([]driver.Value, 3)
|
||||
|
||||
for {
|
||||
err = rows.Next(values)
|
||||
switch err {
|
||||
case nil:
|
||||
warning := MySQLWarning{}
|
||||
|
||||
if raw, ok := values[0].([]byte); ok {
|
||||
warning.Level = string(raw)
|
||||
} else {
|
||||
warning.Level = fmt.Sprintf("%s", values[0])
|
||||
}
|
||||
if raw, ok := values[1].([]byte); ok {
|
||||
warning.Code = string(raw)
|
||||
} else {
|
||||
warning.Code = fmt.Sprintf("%s", values[1])
|
||||
}
|
||||
if raw, ok := values[2].([]byte); ok {
|
||||
warning.Message = string(raw)
|
||||
} else {
|
||||
warning.Message = fmt.Sprintf("%s", values[0])
|
||||
}
|
||||
|
||||
warnings = append(warnings, warning)
|
||||
|
||||
case io.EOF:
|
||||
return warnings
|
||||
|
||||
default:
|
||||
rows.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors_test.go
сгенерированный
поставляемый
Обычный файл
42
Godeps/_workspace/src/github.com/go-sql-driver/mysql/errors_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestErrorsSetLogger(t *testing.T) {
|
||||
previous := errLog
|
||||
defer func() {
|
||||
errLog = previous
|
||||
}()
|
||||
|
||||
// set up logger
|
||||
const expected = "prefix: test\n"
|
||||
buffer := bytes.NewBuffer(make([]byte, 0, 64))
|
||||
logger := log.New(buffer, "prefix: ", 0)
|
||||
|
||||
// print
|
||||
SetLogger(logger)
|
||||
errLog.Print("test")
|
||||
|
||||
// check result
|
||||
if actual := buffer.String(); actual != expected {
|
||||
t.Errorf("expected %q, got %q", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorsStrictIgnoreNotes(t *testing.T) {
|
||||
runTests(t, dsn+"&sql_notes=false", func(dbt *DBTest) {
|
||||
dbt.mustExec("DROP TABLE IF EXISTS does_not_exist")
|
||||
})
|
||||
}
|
||||
162
Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go
сгенерированный
поставляемый
Обычный файл
162
Godeps/_workspace/src/github.com/go-sql-driver/mysql/infile.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,162 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
fileRegister map[string]bool
|
||||
readerRegister map[string]func() io.Reader
|
||||
)
|
||||
|
||||
// RegisterLocalFile adds the given file to the file whitelist,
|
||||
// so that it can be used by "LOAD DATA LOCAL INFILE <filepath>".
|
||||
// Alternatively you can allow the use of all local files with
|
||||
// the DSN parameter 'allowAllFiles=true'
|
||||
//
|
||||
// filePath := "/home/gopher/data.csv"
|
||||
// mysql.RegisterLocalFile(filePath)
|
||||
// err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
|
||||
// if err != nil {
|
||||
// ...
|
||||
//
|
||||
func RegisterLocalFile(filePath string) {
|
||||
// lazy map init
|
||||
if fileRegister == nil {
|
||||
fileRegister = make(map[string]bool)
|
||||
}
|
||||
|
||||
fileRegister[strings.Trim(filePath, `"`)] = true
|
||||
}
|
||||
|
||||
// DeregisterLocalFile removes the given filepath from the whitelist.
|
||||
func DeregisterLocalFile(filePath string) {
|
||||
delete(fileRegister, strings.Trim(filePath, `"`))
|
||||
}
|
||||
|
||||
// RegisterReaderHandler registers a handler function which is used
|
||||
// to receive a io.Reader.
|
||||
// The Reader can be used by "LOAD DATA LOCAL INFILE Reader::<name>".
|
||||
// If the handler returns a io.ReadCloser Close() is called when the
|
||||
// request is finished.
|
||||
//
|
||||
// mysql.RegisterReaderHandler("data", func() io.Reader {
|
||||
// var csvReader io.Reader // Some Reader that returns CSV data
|
||||
// ... // Open Reader here
|
||||
// return csvReader
|
||||
// })
|
||||
// err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
|
||||
// if err != nil {
|
||||
// ...
|
||||
//
|
||||
func RegisterReaderHandler(name string, handler func() io.Reader) {
|
||||
// lazy map init
|
||||
if readerRegister == nil {
|
||||
readerRegister = make(map[string]func() io.Reader)
|
||||
}
|
||||
|
||||
readerRegister[name] = handler
|
||||
}
|
||||
|
||||
// DeregisterReaderHandler removes the ReaderHandler function with
|
||||
// the given name from the registry.
|
||||
func DeregisterReaderHandler(name string) {
|
||||
delete(readerRegister, name)
|
||||
}
|
||||
|
||||
func deferredClose(err *error, closer io.Closer) {
|
||||
closeErr := closer.Close()
|
||||
if *err == nil {
|
||||
*err = closeErr
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
|
||||
var rdr io.Reader
|
||||
var data []byte
|
||||
|
||||
if strings.HasPrefix(name, "Reader::") { // io.Reader
|
||||
name = name[8:]
|
||||
if handler, inMap := readerRegister[name]; inMap {
|
||||
rdr = handler()
|
||||
if rdr != nil {
|
||||
data = make([]byte, 4+mc.maxWriteSize)
|
||||
|
||||
if cl, ok := rdr.(io.Closer); ok {
|
||||
defer deferredClose(&err, cl)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Reader '%s' is <nil>", name)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Reader '%s' is not registered", name)
|
||||
}
|
||||
} else { // File
|
||||
name = strings.Trim(name, `"`)
|
||||
if mc.cfg.allowAllFiles || fileRegister[name] {
|
||||
var file *os.File
|
||||
var fi os.FileInfo
|
||||
|
||||
if file, err = os.Open(name); err == nil {
|
||||
defer deferredClose(&err, file)
|
||||
|
||||
// get file size
|
||||
if fi, err = file.Stat(); err == nil {
|
||||
rdr = file
|
||||
if fileSize := int(fi.Size()); fileSize <= mc.maxWriteSize {
|
||||
data = make([]byte, 4+fileSize)
|
||||
} else if fileSize <= mc.maxPacketAllowed {
|
||||
data = make([]byte, 4+mc.maxWriteSize)
|
||||
} else {
|
||||
err = fmt.Errorf("Local File '%s' too large: Size: %d, Max: %d", name, fileSize, mc.maxPacketAllowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Local File '%s' is not registered. Use the DSN parameter 'allowAllFiles=true' to allow all files", name)
|
||||
}
|
||||
}
|
||||
|
||||
// send content packets
|
||||
if err == nil {
|
||||
var n int
|
||||
for err == nil {
|
||||
n, err = rdr.Read(data[4:])
|
||||
if n > 0 {
|
||||
if ioErr := mc.writePacket(data[:4+n]); ioErr != nil {
|
||||
return ioErr
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
// send empty packet (termination)
|
||||
if data == nil {
|
||||
data = make([]byte, 4)
|
||||
}
|
||||
if ioErr := mc.writePacket(data[:4]); ioErr != nil {
|
||||
return ioErr
|
||||
}
|
||||
|
||||
// read OK packet
|
||||
if err == nil {
|
||||
return mc.readResultOK()
|
||||
} else {
|
||||
mc.readPacket()
|
||||
}
|
||||
return err
|
||||
}
|
||||
1138
Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go
сгенерированный
поставляемый
Обычный файл
1138
Godeps/_workspace/src/github.com/go-sql-driver/mysql/packets.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
22
Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go
сгенерированный
поставляемый
Обычный файл
22
Godeps/_workspace/src/github.com/go-sql-driver/mysql/result.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,22 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
type mysqlResult struct {
|
||||
affectedRows int64
|
||||
insertId int64
|
||||
}
|
||||
|
||||
func (res *mysqlResult) LastInsertId() (int64, error) {
|
||||
return res.insertId, nil
|
||||
}
|
||||
|
||||
func (res *mysqlResult) RowsAffected() (int64, error) {
|
||||
return res.affectedRows, nil
|
||||
}
|
||||
102
Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go
сгенерированный
поставляемый
Обычный файл
102
Godeps/_workspace/src/github.com/go-sql-driver/mysql/rows.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,102 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"io"
|
||||
)
|
||||
|
||||
type mysqlField struct {
|
||||
tableName string
|
||||
name string
|
||||
flags fieldFlag
|
||||
fieldType byte
|
||||
decimals byte
|
||||
}
|
||||
|
||||
type mysqlRows struct {
|
||||
mc *mysqlConn
|
||||
columns []mysqlField
|
||||
}
|
||||
|
||||
type binaryRows struct {
|
||||
mysqlRows
|
||||
}
|
||||
|
||||
type textRows struct {
|
||||
mysqlRows
|
||||
}
|
||||
|
||||
type emptyRows struct{}
|
||||
|
||||
func (rows *mysqlRows) Columns() []string {
|
||||
columns := make([]string, len(rows.columns))
|
||||
if rows.mc.cfg.columnsWithAlias {
|
||||
for i := range columns {
|
||||
columns[i] = rows.columns[i].tableName + "." + rows.columns[i].name
|
||||
}
|
||||
} else {
|
||||
for i := range columns {
|
||||
columns[i] = rows.columns[i].name
|
||||
}
|
||||
}
|
||||
return columns
|
||||
}
|
||||
|
||||
func (rows *mysqlRows) Close() error {
|
||||
mc := rows.mc
|
||||
if mc == nil {
|
||||
return nil
|
||||
}
|
||||
if mc.netConn == nil {
|
||||
return ErrInvalidConn
|
||||
}
|
||||
|
||||
// Remove unread packets from stream
|
||||
err := mc.readUntilEOF()
|
||||
rows.mc = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (rows *binaryRows) Next(dest []driver.Value) error {
|
||||
if mc := rows.mc; mc != nil {
|
||||
if mc.netConn == nil {
|
||||
return ErrInvalidConn
|
||||
}
|
||||
|
||||
// Fetch next row from stream
|
||||
return rows.readRow(dest)
|
||||
}
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
func (rows *textRows) Next(dest []driver.Value) error {
|
||||
if mc := rows.mc; mc != nil {
|
||||
if mc.netConn == nil {
|
||||
return ErrInvalidConn
|
||||
}
|
||||
|
||||
// Fetch next row from stream
|
||||
return rows.readRow(dest)
|
||||
}
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
func (rows emptyRows) Columns() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rows emptyRows) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rows emptyRows) Next(dest []driver.Value) error {
|
||||
return io.EOF
|
||||
}
|
||||
149
Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go
сгенерированный
поставляемый
Обычный файл
149
Godeps/_workspace/src/github.com/go-sql-driver/mysql/statement.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,149 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type mysqlStmt struct {
|
||||
mc *mysqlConn
|
||||
id uint32
|
||||
paramCount int
|
||||
columns []mysqlField // cached from the first query
|
||||
}
|
||||
|
||||
func (stmt *mysqlStmt) Close() error {
|
||||
if stmt.mc == nil || stmt.mc.netConn == nil {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
|
||||
err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id)
|
||||
stmt.mc = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (stmt *mysqlStmt) NumInput() int {
|
||||
return stmt.paramCount
|
||||
}
|
||||
|
||||
func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
|
||||
return converter{}
|
||||
}
|
||||
|
||||
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
if stmt.mc.netConn == nil {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
// Send command
|
||||
err := stmt.writeExecutePacket(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mc := stmt.mc
|
||||
|
||||
mc.affectedRows = 0
|
||||
mc.insertId = 0
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
if err == nil {
|
||||
if resLen > 0 {
|
||||
// Columns
|
||||
err = mc.readUntilEOF()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Rows
|
||||
err = mc.readUntilEOF()
|
||||
}
|
||||
if err == nil {
|
||||
return &mysqlResult{
|
||||
affectedRows: int64(mc.affectedRows),
|
||||
insertId: int64(mc.insertId),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||
if stmt.mc.netConn == nil {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
// Send command
|
||||
err := stmt.writeExecutePacket(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mc := stmt.mc
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := new(binaryRows)
|
||||
rows.mc = mc
|
||||
|
||||
if resLen > 0 {
|
||||
// Columns
|
||||
// If not cached, read them and cache them
|
||||
if stmt.columns == nil {
|
||||
rows.columns, err = mc.readColumns(resLen)
|
||||
stmt.columns = rows.columns
|
||||
} else {
|
||||
rows.columns = stmt.columns
|
||||
err = mc.readUntilEOF()
|
||||
}
|
||||
}
|
||||
|
||||
return rows, err
|
||||
}
|
||||
|
||||
type converter struct{}
|
||||
|
||||
func (converter) ConvertValue(v interface{}) (driver.Value, error) {
|
||||
if driver.IsValue(v) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(v)
|
||||
switch rv.Kind() {
|
||||
case reflect.Ptr:
|
||||
// indirect pointers
|
||||
if rv.IsNil() {
|
||||
return nil, nil
|
||||
}
|
||||
return driver.DefaultParameterConverter.ConvertValue(rv.Elem().Interface())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return rv.Int(), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
return int64(rv.Uint()), nil
|
||||
case reflect.Uint64:
|
||||
u64 := rv.Uint()
|
||||
if u64 >= 1<<63 {
|
||||
return fmt.Sprintf("%d", u64), nil
|
||||
}
|
||||
return int64(u64), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return rv.Float(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
|
||||
}
|
||||
31
Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go
сгенерированный
поставляемый
Обычный файл
31
Godeps/_workspace/src/github.com/go-sql-driver/mysql/transaction.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,31 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
type mysqlTx struct {
|
||||
mc *mysqlConn
|
||||
}
|
||||
|
||||
func (tx *mysqlTx) Commit() (err error) {
|
||||
if tx.mc == nil || tx.mc.netConn == nil {
|
||||
return ErrInvalidConn
|
||||
}
|
||||
err = tx.mc.exec("COMMIT")
|
||||
tx.mc = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (tx *mysqlTx) Rollback() (err error) {
|
||||
if tx.mc == nil || tx.mc.netConn == nil {
|
||||
return ErrInvalidConn
|
||||
}
|
||||
err = tx.mc.exec("ROLLBACK")
|
||||
tx.mc = nil
|
||||
return
|
||||
}
|
||||
963
Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go
сгенерированный
поставляемый
Обычный файл
963
Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,963 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/tls"
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
|
||||
|
||||
errInvalidDSNUnescaped = errors.New("Invalid DSN: Did you forget to escape a param value?")
|
||||
errInvalidDSNAddr = errors.New("Invalid DSN: Network Address not terminated (missing closing brace)")
|
||||
errInvalidDSNNoSlash = errors.New("Invalid DSN: Missing the slash separating the database name")
|
||||
errInvalidDSNUnsafeCollation = errors.New("Invalid DSN: interpolateParams can be used with ascii, latin1, utf8 and utf8mb4 charset")
|
||||
)
|
||||
|
||||
func init() {
|
||||
tlsConfigRegister = make(map[string]*tls.Config)
|
||||
}
|
||||
|
||||
// RegisterTLSConfig registers a custom tls.Config to be used with sql.Open.
|
||||
// Use the key as a value in the DSN where tls=value.
|
||||
//
|
||||
// rootCertPool := x509.NewCertPool()
|
||||
// pem, err := ioutil.ReadFile("/path/ca-cert.pem")
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
|
||||
// log.Fatal("Failed to append PEM.")
|
||||
// }
|
||||
// clientCert := make([]tls.Certificate, 0, 1)
|
||||
// certs, err := tls.LoadX509KeyPair("/path/client-cert.pem", "/path/client-key.pem")
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// clientCert = append(clientCert, certs)
|
||||
// mysql.RegisterTLSConfig("custom", &tls.Config{
|
||||
// RootCAs: rootCertPool,
|
||||
// Certificates: clientCert,
|
||||
// })
|
||||
// db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom")
|
||||
//
|
||||
func RegisterTLSConfig(key string, config *tls.Config) error {
|
||||
if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" {
|
||||
return fmt.Errorf("Key '%s' is reserved", key)
|
||||
}
|
||||
|
||||
tlsConfigRegister[key] = config
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeregisterTLSConfig removes the tls.Config associated with key.
|
||||
func DeregisterTLSConfig(key string) {
|
||||
delete(tlsConfigRegister, key)
|
||||
}
|
||||
|
||||
// parseDSN parses the DSN string to a config
|
||||
func parseDSN(dsn string) (cfg *config, err error) {
|
||||
// New config with some default values
|
||||
cfg = &config{
|
||||
loc: time.UTC,
|
||||
collation: defaultCollation,
|
||||
}
|
||||
|
||||
// TODO: use strings.IndexByte when we can depend on Go 1.2
|
||||
|
||||
// [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN]
|
||||
// Find the last '/' (since the password or the net addr might contain a '/')
|
||||
foundSlash := false
|
||||
for i := len(dsn) - 1; i >= 0; i-- {
|
||||
if dsn[i] == '/' {
|
||||
foundSlash = true
|
||||
var j, k int
|
||||
|
||||
// left part is empty if i <= 0
|
||||
if i > 0 {
|
||||
// [username[:password]@][protocol[(address)]]
|
||||
// Find the last '@' in dsn[:i]
|
||||
for j = i; j >= 0; j-- {
|
||||
if dsn[j] == '@' {
|
||||
// username[:password]
|
||||
// Find the first ':' in dsn[:j]
|
||||
for k = 0; k < j; k++ {
|
||||
if dsn[k] == ':' {
|
||||
cfg.passwd = dsn[k+1 : j]
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.user = dsn[:k]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// [protocol[(address)]]
|
||||
// Find the first '(' in dsn[j+1:i]
|
||||
for k = j + 1; k < i; k++ {
|
||||
if dsn[k] == '(' {
|
||||
// dsn[i-1] must be == ')' if an address is specified
|
||||
if dsn[i-1] != ')' {
|
||||
if strings.ContainsRune(dsn[k+1:i], ')') {
|
||||
return nil, errInvalidDSNUnescaped
|
||||
}
|
||||
return nil, errInvalidDSNAddr
|
||||
}
|
||||
cfg.addr = dsn[k+1 : i-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.net = dsn[j+1 : k]
|
||||
}
|
||||
|
||||
// dbname[?param1=value1&...¶mN=valueN]
|
||||
// Find the first '?' in dsn[i+1:]
|
||||
for j = i + 1; j < len(dsn); j++ {
|
||||
if dsn[j] == '?' {
|
||||
if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
cfg.dbname = dsn[i+1 : j]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundSlash && len(dsn) > 0 {
|
||||
return nil, errInvalidDSNNoSlash
|
||||
}
|
||||
|
||||
if cfg.interpolateParams && unsafeCollations[cfg.collation] {
|
||||
return nil, errInvalidDSNUnsafeCollation
|
||||
}
|
||||
|
||||
// Set default network if empty
|
||||
if cfg.net == "" {
|
||||
cfg.net = "tcp"
|
||||
}
|
||||
|
||||
// Set default address if empty
|
||||
if cfg.addr == "" {
|
||||
switch cfg.net {
|
||||
case "tcp":
|
||||
cfg.addr = "127.0.0.1:3306"
|
||||
case "unix":
|
||||
cfg.addr = "/tmp/mysql.sock"
|
||||
default:
|
||||
return nil, errors.New("Default addr for network '" + cfg.net + "' unknown")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseDSNParams parses the DSN "query string"
|
||||
// Values must be url.QueryEscape'ed
|
||||
func parseDSNParams(cfg *config, params string) (err error) {
|
||||
for _, v := range strings.Split(params, "&") {
|
||||
param := strings.SplitN(v, "=", 2)
|
||||
if len(param) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
// cfg params
|
||||
switch value := param[1]; param[0] {
|
||||
|
||||
// Enable client side placeholder substitution
|
||||
case "interpolateParams":
|
||||
var isBool bool
|
||||
cfg.interpolateParams, isBool = readBool(value)
|
||||
if !isBool {
|
||||
return fmt.Errorf("Invalid Bool value: %s", value)
|
||||
}
|
||||
|
||||
// Disable INFILE whitelist / enable all files
|
||||
case "allowAllFiles":
|
||||
var isBool bool
|
||||
cfg.allowAllFiles, isBool = readBool(value)
|
||||
if !isBool {
|
||||
return fmt.Errorf("Invalid Bool value: %s", value)
|
||||
}
|
||||
|
||||
// Use old authentication mode (pre MySQL 4.1)
|
||||
case "allowOldPasswords":
|
||||
var isBool bool
|
||||
cfg.allowOldPasswords, isBool = readBool(value)
|
||||
if !isBool {
|
||||
return fmt.Errorf("Invalid Bool value: %s", value)
|
||||
}
|
||||
|
||||
// Switch "rowsAffected" mode
|
||||
case "clientFoundRows":
|
||||
var isBool bool
|
||||
cfg.clientFoundRows, isBool = readBool(value)
|
||||
if !isBool {
|
||||
return fmt.Errorf("Invalid Bool value: %s", value)
|
||||
}
|
||||
|
||||
// Collation
|
||||
case "collation":
|
||||
collation, ok := collations[value]
|
||||
if !ok {
|
||||
// Note possibility for false negatives:
|
||||
// could be triggered although the collation is valid if the
|
||||
// collations map does not contain entries the server supports.
|
||||
err = errors.New("unknown collation")
|
||||
return
|
||||
}
|
||||
cfg.collation = collation
|
||||
break
|
||||
|
||||
case "columnsWithAlias":
|
||||
var isBool bool
|
||||
cfg.columnsWithAlias, isBool = readBool(value)
|
||||
if !isBool {
|
||||
return fmt.Errorf("Invalid Bool value: %s", value)
|
||||
}
|
||||
|
||||
// Time Location
|
||||
case "loc":
|
||||
if value, err = url.QueryUnescape(value); err != nil {
|
||||
return
|
||||
}
|
||||
cfg.loc, err = time.LoadLocation(value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Dial Timeout
|
||||
case "timeout":
|
||||
cfg.timeout, err = time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TLS-Encryption
|
||||
case "tls":
|
||||
boolValue, isBool := readBool(value)
|
||||
if isBool {
|
||||
if boolValue {
|
||||
cfg.tls = &tls.Config{}
|
||||
}
|
||||
} else {
|
||||
if strings.ToLower(value) == "skip-verify" {
|
||||
cfg.tls = &tls.Config{InsecureSkipVerify: true}
|
||||
} else if tlsConfig, ok := tlsConfigRegister[value]; ok {
|
||||
if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
|
||||
host, _, err := net.SplitHostPort(cfg.addr)
|
||||
if err == nil {
|
||||
tlsConfig.ServerName = host
|
||||
}
|
||||
}
|
||||
|
||||
cfg.tls = tlsConfig
|
||||
} else {
|
||||
return fmt.Errorf("Invalid value / unknown config name: %s", value)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// lazy init
|
||||
if cfg.params == nil {
|
||||
cfg.params = make(map[string]string)
|
||||
}
|
||||
|
||||
if cfg.params[param[0]], err = url.QueryUnescape(value); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Returns the bool value of the input.
|
||||
// The 2nd return value indicates if the input was a valid bool value
|
||||
func readBool(input string) (value bool, valid bool) {
|
||||
switch input {
|
||||
case "1", "true", "TRUE", "True":
|
||||
return true, true
|
||||
case "0", "false", "FALSE", "False":
|
||||
return false, true
|
||||
}
|
||||
|
||||
// Not a valid bool value
|
||||
return
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Authentication *
|
||||
******************************************************************************/
|
||||
|
||||
// Encrypt password using 4.1+ method
|
||||
func scramblePassword(scramble, password []byte) []byte {
|
||||
if len(password) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// stage1Hash = SHA1(password)
|
||||
crypt := sha1.New()
|
||||
crypt.Write(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
|
||||
}
|
||||
|
||||
// Encrypt 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
|
||||
}
|
||||
|
||||
// Encrypt password using insecure pre 4.1 method
|
||||
func scrambleOldPassword(scramble, password []byte) []byte {
|
||||
if len(password) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
scramble = scramble[:8]
|
||||
|
||||
hashPw := pwHash(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[:]
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Time related utils *
|
||||
******************************************************************************/
|
||||
|
||||
// NullTime represents a time.Time that may be NULL.
|
||||
// NullTime implements the Scanner interface so
|
||||
// it can be used as a scan destination:
|
||||
//
|
||||
// var nt NullTime
|
||||
// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
|
||||
// ...
|
||||
// if nt.Valid {
|
||||
// // use nt.Time
|
||||
// } else {
|
||||
// // NULL value
|
||||
// }
|
||||
//
|
||||
// This NullTime implementation is not driver-specific
|
||||
type NullTime struct {
|
||||
Time time.Time
|
||||
Valid bool // Valid is true if Time is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
// The value type must be time.Time or string / []byte (formatted time-string),
|
||||
// otherwise Scan fails.
|
||||
func (nt *NullTime) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
nt.Time, nt.Valid = time.Time{}, false
|
||||
return
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case time.Time:
|
||||
nt.Time, nt.Valid = v, true
|
||||
return
|
||||
case []byte:
|
||||
nt.Time, err = parseDateTime(string(v), time.UTC)
|
||||
nt.Valid = (err == nil)
|
||||
return
|
||||
case string:
|
||||
nt.Time, err = parseDateTime(v, time.UTC)
|
||||
nt.Valid = (err == nil)
|
||||
return
|
||||
}
|
||||
|
||||
nt.Valid = false
|
||||
return fmt.Errorf("Can't convert %T to time.Time", value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (nt NullTime) Value() (driver.Value, error) {
|
||||
if !nt.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return nt.Time, nil
|
||||
}
|
||||
|
||||
func parseDateTime(str string, loc *time.Location) (t time.Time, err error) {
|
||||
base := "0000-00-00 00:00:00.0000000"
|
||||
switch len(str) {
|
||||
case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
|
||||
if str == base[:len(str)] {
|
||||
return
|
||||
}
|
||||
t, err = time.Parse(timeFormat[:len(str)], str)
|
||||
default:
|
||||
err = fmt.Errorf("Invalid Time-String: %s", str)
|
||||
return
|
||||
}
|
||||
|
||||
// Adjust location
|
||||
if err == nil && loc != time.UTC {
|
||||
y, mo, d := t.Date()
|
||||
h, mi, s := t.Clock()
|
||||
t, err = time.Date(y, mo, d, h, mi, s, t.Nanosecond(), loc), nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) {
|
||||
switch num {
|
||||
case 0:
|
||||
return time.Time{}, nil
|
||||
case 4:
|
||||
return time.Date(
|
||||
int(binary.LittleEndian.Uint16(data[:2])), // year
|
||||
time.Month(data[2]), // month
|
||||
int(data[3]), // day
|
||||
0, 0, 0, 0,
|
||||
loc,
|
||||
), nil
|
||||
case 7:
|
||||
return time.Date(
|
||||
int(binary.LittleEndian.Uint16(data[:2])), // year
|
||||
time.Month(data[2]), // month
|
||||
int(data[3]), // day
|
||||
int(data[4]), // hour
|
||||
int(data[5]), // minutes
|
||||
int(data[6]), // seconds
|
||||
0,
|
||||
loc,
|
||||
), nil
|
||||
case 11:
|
||||
return time.Date(
|
||||
int(binary.LittleEndian.Uint16(data[:2])), // year
|
||||
time.Month(data[2]), // month
|
||||
int(data[3]), // day
|
||||
int(data[4]), // hour
|
||||
int(data[5]), // minutes
|
||||
int(data[6]), // seconds
|
||||
int(binary.LittleEndian.Uint32(data[7:11]))*1000, // nanoseconds
|
||||
loc,
|
||||
), nil
|
||||
}
|
||||
return nil, fmt.Errorf("Invalid DATETIME-packet length %d", num)
|
||||
}
|
||||
|
||||
// zeroDateTime is used in formatBinaryDateTime to avoid an allocation
|
||||
// if the DATE or DATETIME has the zero value.
|
||||
// It must never be changed.
|
||||
// The current behavior depends on database/sql copying the result.
|
||||
var zeroDateTime = []byte("0000-00-00 00:00:00.000000")
|
||||
|
||||
const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
|
||||
|
||||
func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value, error) {
|
||||
// length expects the deterministic length of the zero value,
|
||||
// negative time and 100+ hours are automatically added if needed
|
||||
if len(src) == 0 {
|
||||
if justTime {
|
||||
return zeroDateTime[11 : 11+length], nil
|
||||
}
|
||||
return zeroDateTime[:length], nil
|
||||
}
|
||||
var dst []byte // return value
|
||||
var pt, p1, p2, p3 byte // current digit pair
|
||||
var zOffs byte // offset of value in zeroDateTime
|
||||
if justTime {
|
||||
switch length {
|
||||
case
|
||||
8, // time (can be up to 10 when negative and 100+ hours)
|
||||
10, 11, 12, 13, 14, 15: // time with fractional seconds
|
||||
default:
|
||||
return nil, fmt.Errorf("illegal TIME length %d", length)
|
||||
}
|
||||
switch len(src) {
|
||||
case 8, 12:
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid TIME-packet length %d", len(src))
|
||||
}
|
||||
// +2 to enable negative time and 100+ hours
|
||||
dst = make([]byte, 0, length+2)
|
||||
if src[0] == 1 {
|
||||
dst = append(dst, '-')
|
||||
}
|
||||
if src[1] != 0 {
|
||||
hour := uint16(src[1])*24 + uint16(src[5])
|
||||
pt = byte(hour / 100)
|
||||
p1 = byte(hour - 100*uint16(pt))
|
||||
dst = append(dst, digits01[pt])
|
||||
} else {
|
||||
p1 = src[5]
|
||||
}
|
||||
zOffs = 11
|
||||
src = src[6:]
|
||||
} else {
|
||||
switch length {
|
||||
case 10, 19, 21, 22, 23, 24, 25, 26:
|
||||
default:
|
||||
t := "DATE"
|
||||
if length > 10 {
|
||||
t += "TIME"
|
||||
}
|
||||
return nil, fmt.Errorf("illegal %s length %d", t, length)
|
||||
}
|
||||
switch len(src) {
|
||||
case 4, 7, 11:
|
||||
default:
|
||||
t := "DATE"
|
||||
if length > 10 {
|
||||
t += "TIME"
|
||||
}
|
||||
return nil, fmt.Errorf("illegal %s-packet length %d", t, len(src))
|
||||
}
|
||||
dst = make([]byte, 0, length)
|
||||
// start with the date
|
||||
year := binary.LittleEndian.Uint16(src[:2])
|
||||
pt = byte(year / 100)
|
||||
p1 = byte(year - 100*uint16(pt))
|
||||
p2, p3 = src[2], src[3]
|
||||
dst = append(dst,
|
||||
digits10[pt], digits01[pt],
|
||||
digits10[p1], digits01[p1], '-',
|
||||
digits10[p2], digits01[p2], '-',
|
||||
digits10[p3], digits01[p3],
|
||||
)
|
||||
if length == 10 {
|
||||
return dst, nil
|
||||
}
|
||||
if len(src) == 4 {
|
||||
return append(dst, zeroDateTime[10:length]...), nil
|
||||
}
|
||||
dst = append(dst, ' ')
|
||||
p1 = src[4] // hour
|
||||
src = src[5:]
|
||||
}
|
||||
// p1 is 2-digit hour, src is after hour
|
||||
p2, p3 = src[0], src[1]
|
||||
dst = append(dst,
|
||||
digits10[p1], digits01[p1], ':',
|
||||
digits10[p2], digits01[p2], ':',
|
||||
digits10[p3], digits01[p3],
|
||||
)
|
||||
if length <= byte(len(dst)) {
|
||||
return dst, nil
|
||||
}
|
||||
src = src[2:]
|
||||
if len(src) == 0 {
|
||||
return append(dst, zeroDateTime[19:zOffs+length]...), nil
|
||||
}
|
||||
microsecs := binary.LittleEndian.Uint32(src[:4])
|
||||
p1 = byte(microsecs / 10000)
|
||||
microsecs -= 10000 * uint32(p1)
|
||||
p2 = byte(microsecs / 100)
|
||||
microsecs -= 100 * uint32(p2)
|
||||
p3 = byte(microsecs)
|
||||
switch decimals := zOffs + length - 20; decimals {
|
||||
default:
|
||||
return append(dst, '.',
|
||||
digits10[p1], digits01[p1],
|
||||
digits10[p2], digits01[p2],
|
||||
digits10[p3], digits01[p3],
|
||||
), nil
|
||||
case 1:
|
||||
return append(dst, '.',
|
||||
digits10[p1],
|
||||
), nil
|
||||
case 2:
|
||||
return append(dst, '.',
|
||||
digits10[p1], digits01[p1],
|
||||
), nil
|
||||
case 3:
|
||||
return append(dst, '.',
|
||||
digits10[p1], digits01[p1],
|
||||
digits10[p2],
|
||||
), nil
|
||||
case 4:
|
||||
return append(dst, '.',
|
||||
digits10[p1], digits01[p1],
|
||||
digits10[p2], digits01[p2],
|
||||
), nil
|
||||
case 5:
|
||||
return append(dst, '.',
|
||||
digits10[p1], digits01[p1],
|
||||
digits10[p2], digits01[p2],
|
||||
digits10[p3],
|
||||
), nil
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Convert from and to bytes *
|
||||
******************************************************************************/
|
||||
|
||||
func uint64ToBytes(n uint64) []byte {
|
||||
return []byte{
|
||||
byte(n),
|
||||
byte(n >> 8),
|
||||
byte(n >> 16),
|
||||
byte(n >> 24),
|
||||
byte(n >> 32),
|
||||
byte(n >> 40),
|
||||
byte(n >> 48),
|
||||
byte(n >> 56),
|
||||
}
|
||||
}
|
||||
|
||||
func uint64ToString(n uint64) []byte {
|
||||
var a [20]byte
|
||||
i := 20
|
||||
|
||||
// U+0030 = 0
|
||||
// ...
|
||||
// U+0039 = 9
|
||||
|
||||
var q uint64
|
||||
for n >= 10 {
|
||||
i--
|
||||
q = n / 10
|
||||
a[i] = uint8(n-q*10) + 0x30
|
||||
n = q
|
||||
}
|
||||
|
||||
i--
|
||||
a[i] = uint8(n) + 0x30
|
||||
|
||||
return a[i:]
|
||||
}
|
||||
|
||||
// treats string value as unsigned integer representation
|
||||
func stringToInt(b []byte) int {
|
||||
val := 0
|
||||
for i := range b {
|
||||
val *= 10
|
||||
val += int(b[i] - 0x30)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// returns the string read as a bytes slice, wheter the value is NULL,
|
||||
// the number of bytes read and an error, in case the string is longer than
|
||||
// the input slice
|
||||
func readLengthEncodedString(b []byte) ([]byte, bool, int, error) {
|
||||
// Get length
|
||||
num, isNull, n := readLengthEncodedInteger(b)
|
||||
if num < 1 {
|
||||
return b[n:n], isNull, n, nil
|
||||
}
|
||||
|
||||
n += int(num)
|
||||
|
||||
// Check data length
|
||||
if len(b) >= n {
|
||||
return b[n-int(num) : n], false, n, nil
|
||||
}
|
||||
return nil, false, n, io.EOF
|
||||
}
|
||||
|
||||
// returns the number of bytes skipped and an error, in case the string is
|
||||
// longer than the input slice
|
||||
func skipLengthEncodedString(b []byte) (int, error) {
|
||||
// Get length
|
||||
num, _, n := readLengthEncodedInteger(b)
|
||||
if num < 1 {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
n += int(num)
|
||||
|
||||
// Check data length
|
||||
if len(b) >= n {
|
||||
return n, nil
|
||||
}
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
// returns the number read, whether the value is NULL and the number of bytes read
|
||||
func readLengthEncodedInteger(b []byte) (uint64, bool, int) {
|
||||
switch b[0] {
|
||||
|
||||
// 251: NULL
|
||||
case 0xfb:
|
||||
return 0, true, 1
|
||||
|
||||
// 252: value of following 2
|
||||
case 0xfc:
|
||||
return uint64(b[1]) | uint64(b[2])<<8, false, 3
|
||||
|
||||
// 253: value of following 3
|
||||
case 0xfd:
|
||||
return uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16, false, 4
|
||||
|
||||
// 254: value of following 8
|
||||
case 0xfe:
|
||||
return uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 |
|
||||
uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 |
|
||||
uint64(b[7])<<48 | uint64(b[8])<<56,
|
||||
false, 9
|
||||
}
|
||||
|
||||
// 0-250: value of first byte
|
||||
return uint64(b[0]), false, 1
|
||||
}
|
||||
|
||||
// encodes a uint64 value and appends it to the given bytes slice
|
||||
func appendLengthEncodedInteger(b []byte, n uint64) []byte {
|
||||
switch {
|
||||
case n <= 250:
|
||||
return append(b, byte(n))
|
||||
|
||||
case n <= 0xffff:
|
||||
return append(b, 0xfc, byte(n), byte(n>>8))
|
||||
|
||||
case n <= 0xffffff:
|
||||
return append(b, 0xfd, byte(n), byte(n>>8), byte(n>>16))
|
||||
}
|
||||
return append(b, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24),
|
||||
byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56))
|
||||
}
|
||||
|
||||
// reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize.
|
||||
// If cap(buf) is not enough, reallocate new buffer.
|
||||
func reserveBuffer(buf []byte, appendSize int) []byte {
|
||||
newSize := len(buf) + appendSize
|
||||
if cap(buf) < newSize {
|
||||
// Grow buffer exponentially
|
||||
newBuf := make([]byte, len(buf)*2+appendSize)
|
||||
copy(newBuf, buf)
|
||||
buf = newBuf
|
||||
}
|
||||
return buf[:newSize]
|
||||
}
|
||||
|
||||
// escapeBytesBackslash escapes []byte with backslashes (\)
|
||||
// This escapes the contents of a string (provided as []byte) by adding backslashes before special
|
||||
// characters, and turning others into specific escape sequences, such as
|
||||
// turning newlines into \n and null bytes into \0.
|
||||
// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L823-L932
|
||||
func escapeBytesBackslash(buf, v []byte) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
|
||||
for _, c := range v {
|
||||
switch c {
|
||||
case '\x00':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '0'
|
||||
pos += 2
|
||||
case '\n':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'n'
|
||||
pos += 2
|
||||
case '\r':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'r'
|
||||
pos += 2
|
||||
case '\x1a':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'Z'
|
||||
pos += 2
|
||||
case '\'':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '\''
|
||||
pos += 2
|
||||
case '"':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '"'
|
||||
pos += 2
|
||||
case '\\':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '\\'
|
||||
pos += 2
|
||||
default:
|
||||
buf[pos] = c
|
||||
pos += 1
|
||||
}
|
||||
}
|
||||
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
// escapeStringBackslash is similar to escapeBytesBackslash but for string.
|
||||
func escapeStringBackslash(buf []byte, v string) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
|
||||
for i := 0; i < len(v); i++ {
|
||||
c := v[i]
|
||||
switch c {
|
||||
case '\x00':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '0'
|
||||
pos += 2
|
||||
case '\n':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'n'
|
||||
pos += 2
|
||||
case '\r':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'r'
|
||||
pos += 2
|
||||
case '\x1a':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = 'Z'
|
||||
pos += 2
|
||||
case '\'':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '\''
|
||||
pos += 2
|
||||
case '"':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '"'
|
||||
pos += 2
|
||||
case '\\':
|
||||
buf[pos] = '\\'
|
||||
buf[pos+1] = '\\'
|
||||
pos += 2
|
||||
default:
|
||||
buf[pos] = c
|
||||
pos += 1
|
||||
}
|
||||
}
|
||||
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
// escapeBytesQuotes escapes apostrophes in []byte by doubling them up.
|
||||
// This escapes the contents of a string by doubling up any apostrophes that
|
||||
// it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
|
||||
// effect on the server.
|
||||
// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L963-L1038
|
||||
func escapeBytesQuotes(buf, v []byte) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
|
||||
for _, c := range v {
|
||||
if c == '\'' {
|
||||
buf[pos] = '\''
|
||||
buf[pos+1] = '\''
|
||||
pos += 2
|
||||
} else {
|
||||
buf[pos] = c
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
// escapeStringQuotes is similar to escapeBytesQuotes but for string.
|
||||
func escapeStringQuotes(buf []byte, v string) []byte {
|
||||
pos := len(buf)
|
||||
buf = reserveBuffer(buf, len(v)*2)
|
||||
|
||||
for i := 0; i < len(v); i++ {
|
||||
c := v[i]
|
||||
if c == '\'' {
|
||||
buf[pos] = '\''
|
||||
buf[pos+1] = '\''
|
||||
pos += 2
|
||||
} else {
|
||||
buf[pos] = c
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return buf[:pos]
|
||||
}
|
||||
346
Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go
сгенерированный
поставляемый
Обычный файл
346
Godeps/_workspace/src/github.com/go-sql-driver/mysql/utils_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,346 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var testDSNs = []struct {
|
||||
in string
|
||||
out string
|
||||
loc *time.Location
|
||||
}{
|
||||
{"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:true interpolateParams:false}", time.UTC},
|
||||
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
{"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
{"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8mb4,utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
{"user:password@/dbname?loc=UTC&timeout=30s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:30000000000 collation:224 allowAllFiles:true allowOldPasswords:true clientFoundRows:true columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
{"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{user:user passwd:p@ss(word) net:tcp addr:[de:ad:be:ef::ca:fe]:80 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.Local},
|
||||
{"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
{"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
{"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
{"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
{"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
{"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false interpolateParams:false}", time.UTC},
|
||||
}
|
||||
|
||||
func TestDSNParser(t *testing.T) {
|
||||
var cfg *config
|
||||
var err error
|
||||
var res string
|
||||
|
||||
for i, tst := range testDSNs {
|
||||
cfg, err = parseDSN(tst.in)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
// pointer not static
|
||||
cfg.tls = nil
|
||||
|
||||
res = fmt.Sprintf("%+v", cfg)
|
||||
if res != fmt.Sprintf(tst.out, tst.loc) {
|
||||
t.Errorf("%d. parseDSN(%q) => %q, want %q", i, tst.in, res, fmt.Sprintf(tst.out, tst.loc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDSNParserInvalid(t *testing.T) {
|
||||
var invalidDSNs = []string{
|
||||
"@net(addr/", // no closing brace
|
||||
"@tcp(/", // no closing brace
|
||||
"tcp(/", // no closing brace
|
||||
"(/", // no closing brace
|
||||
"net(addr)//", // unescaped
|
||||
"user:pass@tcp(1.2.3.4:3306)", // no trailing slash
|
||||
//"/dbname?arg=/some/unescaped/path",
|
||||
}
|
||||
|
||||
for i, tst := range invalidDSNs {
|
||||
if _, err := parseDSN(tst); err == nil {
|
||||
t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDSNWithCustomTLS(t *testing.T) {
|
||||
baseDSN := "user:password@tcp(localhost:5555)/dbname?tls="
|
||||
tlsCfg := tls.Config{}
|
||||
|
||||
RegisterTLSConfig("utils_test", &tlsCfg)
|
||||
|
||||
// Custom TLS is missing
|
||||
tst := baseDSN + "invalid_tls"
|
||||
cfg, err := parseDSN(tst)
|
||||
if err == nil {
|
||||
t.Errorf("Invalid custom TLS in DSN (%s) but did not error. Got config: %#v", tst, cfg)
|
||||
}
|
||||
|
||||
tst = baseDSN + "utils_test"
|
||||
|
||||
// Custom TLS with a server name
|
||||
name := "foohost"
|
||||
tlsCfg.ServerName = name
|
||||
cfg, err = parseDSN(tst)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
} else if cfg.tls.ServerName != name {
|
||||
t.Errorf("Did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst)
|
||||
}
|
||||
|
||||
// Custom TLS without a server name
|
||||
name = "localhost"
|
||||
tlsCfg.ServerName = ""
|
||||
cfg, err = parseDSN(tst)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
} else if cfg.tls.ServerName != name {
|
||||
t.Errorf("Did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
|
||||
}
|
||||
|
||||
DeregisterTLSConfig("utils_test")
|
||||
}
|
||||
|
||||
func TestDSNUnsafeCollation(t *testing.T) {
|
||||
_, err := parseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true")
|
||||
if err != errInvalidDSNUnsafeCollation {
|
||||
t.Error("Expected %v, Got %v", errInvalidDSNUnsafeCollation, err)
|
||||
}
|
||||
|
||||
_, err = parseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false")
|
||||
if err != nil {
|
||||
t.Error("Expected %v, Got %v", nil, err)
|
||||
}
|
||||
|
||||
_, err = parseDSN("/dbname?collation=gbk_chinese_ci")
|
||||
if err != nil {
|
||||
t.Error("Expected %v, Got %v", nil, err)
|
||||
}
|
||||
|
||||
_, err = parseDSN("/dbname?collation=ascii_bin&interpolateParams=true")
|
||||
if err != nil {
|
||||
t.Error("Expected %v, Got %v", nil, err)
|
||||
}
|
||||
|
||||
_, err = parseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true")
|
||||
if err != nil {
|
||||
t.Error("Expected %v, Got %v", nil, err)
|
||||
}
|
||||
|
||||
_, err = parseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true")
|
||||
if err != nil {
|
||||
t.Error("Expected %v, Got %v", nil, err)
|
||||
}
|
||||
|
||||
_, err = parseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true")
|
||||
if err != nil {
|
||||
t.Error("Expected %v, Got %v", nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseDSN(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, tst := range testDSNs {
|
||||
if _, err := parseDSN(tst.in); err != nil {
|
||||
b.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanNullTime(t *testing.T) {
|
||||
var scanTests = []struct {
|
||||
in interface{}
|
||||
error bool
|
||||
valid bool
|
||||
time time.Time
|
||||
}{
|
||||
{tDate, false, true, tDate},
|
||||
{sDate, false, true, tDate},
|
||||
{[]byte(sDate), false, true, tDate},
|
||||
{tDateTime, false, true, tDateTime},
|
||||
{sDateTime, false, true, tDateTime},
|
||||
{[]byte(sDateTime), false, true, tDateTime},
|
||||
{tDate0, false, true, tDate0},
|
||||
{sDate0, false, true, tDate0},
|
||||
{[]byte(sDate0), false, true, tDate0},
|
||||
{sDateTime0, false, true, tDate0},
|
||||
{[]byte(sDateTime0), false, true, tDate0},
|
||||
{"", true, false, tDate0},
|
||||
{"1234", true, false, tDate0},
|
||||
{0, true, false, tDate0},
|
||||
}
|
||||
|
||||
var nt = NullTime{}
|
||||
var err error
|
||||
|
||||
for _, tst := range scanTests {
|
||||
err = nt.Scan(tst.in)
|
||||
if (err != nil) != tst.error {
|
||||
t.Errorf("%v: expected error status %t, got %t", tst.in, tst.error, (err != nil))
|
||||
}
|
||||
if nt.Valid != tst.valid {
|
||||
t.Errorf("%v: expected valid status %t, got %t", tst.in, tst.valid, nt.Valid)
|
||||
}
|
||||
if nt.Time != tst.time {
|
||||
t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLengthEncodedInteger(t *testing.T) {
|
||||
var integerTests = []struct {
|
||||
num uint64
|
||||
encoded []byte
|
||||
}{
|
||||
{0x0000000000000000, []byte{0x00}},
|
||||
{0x0000000000000012, []byte{0x12}},
|
||||
{0x00000000000000fa, []byte{0xfa}},
|
||||
{0x0000000000000100, []byte{0xfc, 0x00, 0x01}},
|
||||
{0x0000000000001234, []byte{0xfc, 0x34, 0x12}},
|
||||
{0x000000000000ffff, []byte{0xfc, 0xff, 0xff}},
|
||||
{0x0000000000010000, []byte{0xfd, 0x00, 0x00, 0x01}},
|
||||
{0x0000000000123456, []byte{0xfd, 0x56, 0x34, 0x12}},
|
||||
{0x0000000000ffffff, []byte{0xfd, 0xff, 0xff, 0xff}},
|
||||
{0x0000000001000000, []byte{0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}},
|
||||
{0x123456789abcdef0, []byte{0xfe, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12}},
|
||||
{0xffffffffffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
|
||||
}
|
||||
|
||||
for _, tst := range integerTests {
|
||||
num, isNull, numLen := readLengthEncodedInteger(tst.encoded)
|
||||
if isNull {
|
||||
t.Errorf("%x: expected %d, got NULL", tst.encoded, tst.num)
|
||||
}
|
||||
if num != tst.num {
|
||||
t.Errorf("%x: expected %d, got %d", tst.encoded, tst.num, num)
|
||||
}
|
||||
if numLen != len(tst.encoded) {
|
||||
t.Errorf("%x: expected size %d, got %d", tst.encoded, len(tst.encoded), numLen)
|
||||
}
|
||||
encoded := appendLengthEncodedInteger(nil, num)
|
||||
if !bytes.Equal(encoded, tst.encoded) {
|
||||
t.Errorf("%v: expected %x, got %x", num, tst.encoded, encoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOldPass(t *testing.T) {
|
||||
scramble := []byte{9, 8, 7, 6, 5, 4, 3, 2}
|
||||
vectors := []struct {
|
||||
pass string
|
||||
out string
|
||||
}{
|
||||
{" pass", "47575c5a435b4251"},
|
||||
{"pass ", "47575c5a435b4251"},
|
||||
{"123\t456", "575c47505b5b5559"},
|
||||
{"C0mpl!ca ted#PASS123", "5d5d554849584a45"},
|
||||
}
|
||||
for _, tuple := range vectors {
|
||||
ours := scrambleOldPassword(scramble, []byte(tuple.pass))
|
||||
if tuple.out != fmt.Sprintf("%x", ours) {
|
||||
t.Errorf("Failed old password %q", tuple.pass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatBinaryDateTime(t *testing.T) {
|
||||
rawDate := [11]byte{}
|
||||
binary.LittleEndian.PutUint16(rawDate[:2], 1978) // years
|
||||
rawDate[2] = 12 // months
|
||||
rawDate[3] = 30 // days
|
||||
rawDate[4] = 15 // hours
|
||||
rawDate[5] = 46 // minutes
|
||||
rawDate[6] = 23 // seconds
|
||||
binary.LittleEndian.PutUint32(rawDate[7:], 987654) // microseconds
|
||||
expect := func(expected string, inlen, outlen uint8) {
|
||||
actual, _ := formatBinaryDateTime(rawDate[:inlen], outlen, false)
|
||||
bytes, ok := actual.([]byte)
|
||||
if !ok {
|
||||
t.Errorf("formatBinaryDateTime must return []byte, was %T", actual)
|
||||
}
|
||||
if string(bytes) != expected {
|
||||
t.Errorf(
|
||||
"expected %q, got %q for length in %d, out %d",
|
||||
bytes, actual, inlen, outlen,
|
||||
)
|
||||
}
|
||||
}
|
||||
expect("0000-00-00", 0, 10)
|
||||
expect("0000-00-00 00:00:00", 0, 19)
|
||||
expect("1978-12-30", 4, 10)
|
||||
expect("1978-12-30 15:46:23", 7, 19)
|
||||
expect("1978-12-30 15:46:23.987654", 11, 26)
|
||||
}
|
||||
|
||||
func TestEscapeBackslash(t *testing.T) {
|
||||
expect := func(expected, value string) {
|
||||
actual := string(escapeBytesBackslash([]byte{}, []byte(value)))
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"expected %s, got %s",
|
||||
expected, actual,
|
||||
)
|
||||
}
|
||||
|
||||
actual = string(escapeStringBackslash([]byte{}, value))
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"expected %s, got %s",
|
||||
expected, actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
expect("foo\\0bar", "foo\x00bar")
|
||||
expect("foo\\nbar", "foo\nbar")
|
||||
expect("foo\\rbar", "foo\rbar")
|
||||
expect("foo\\Zbar", "foo\x1abar")
|
||||
expect("foo\\\"bar", "foo\"bar")
|
||||
expect("foo\\\\bar", "foo\\bar")
|
||||
expect("foo\\'bar", "foo'bar")
|
||||
}
|
||||
|
||||
func TestEscapeQuotes(t *testing.T) {
|
||||
expect := func(expected, value string) {
|
||||
actual := string(escapeBytesQuotes([]byte{}, []byte(value)))
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"expected %s, got %s",
|
||||
expected, actual,
|
||||
)
|
||||
}
|
||||
|
||||
actual = string(escapeStringQuotes([]byte{}, value))
|
||||
if actual != expected {
|
||||
t.Errorf(
|
||||
"expected %s, got %s",
|
||||
expected, actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
expect("foo\x00bar", "foo\x00bar") // not affected
|
||||
expect("foo\nbar", "foo\nbar") // not affected
|
||||
expect("foo\rbar", "foo\rbar") // not affected
|
||||
expect("foo\x1abar", "foo\x1abar") // not affected
|
||||
expect("foo''bar", "foo'bar") // affected
|
||||
expect("foo\"bar", "foo\"bar") // not affected
|
||||
}
|
||||
74
Godeps/_workspace/src/github.com/goamz/goamz/aws/attempt.go
сгенерированный
поставляемый
Обычный файл
74
Godeps/_workspace/src/github.com/goamz/goamz/aws/attempt.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,74 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// AttemptStrategy represents a strategy for waiting for an action
|
||||
// to complete successfully. This is an internal type used by the
|
||||
// implementation of other goamz packages.
|
||||
type AttemptStrategy struct {
|
||||
Total time.Duration // total duration of attempt.
|
||||
Delay time.Duration // interval between each try in the burst.
|
||||
Min int // minimum number of retries; overrides Total
|
||||
}
|
||||
|
||||
type Attempt struct {
|
||||
strategy AttemptStrategy
|
||||
last time.Time
|
||||
end time.Time
|
||||
force bool
|
||||
count int
|
||||
}
|
||||
|
||||
// Start begins a new sequence of attempts for the given strategy.
|
||||
func (s AttemptStrategy) Start() *Attempt {
|
||||
now := time.Now()
|
||||
return &Attempt{
|
||||
strategy: s,
|
||||
last: now,
|
||||
end: now.Add(s.Total),
|
||||
force: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Next waits until it is time to perform the next attempt or returns
|
||||
// false if it is time to stop trying.
|
||||
func (a *Attempt) Next() bool {
|
||||
now := time.Now()
|
||||
sleep := a.nextSleep(now)
|
||||
if !a.force && !now.Add(sleep).Before(a.end) && a.strategy.Min <= a.count {
|
||||
return false
|
||||
}
|
||||
a.force = false
|
||||
if sleep > 0 && a.count > 0 {
|
||||
time.Sleep(sleep)
|
||||
now = time.Now()
|
||||
}
|
||||
a.count++
|
||||
a.last = now
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *Attempt) nextSleep(now time.Time) time.Duration {
|
||||
sleep := a.strategy.Delay - now.Sub(a.last)
|
||||
if sleep < 0 {
|
||||
return 0
|
||||
}
|
||||
return sleep
|
||||
}
|
||||
|
||||
// HasNext returns whether another attempt will be made if the current
|
||||
// one fails. If it returns true, the following call to Next is
|
||||
// guaranteed to return true.
|
||||
func (a *Attempt) HasNext() bool {
|
||||
if a.force || a.strategy.Min > a.count {
|
||||
return true
|
||||
}
|
||||
now := time.Now()
|
||||
if now.Add(a.nextSleep(now)).Before(a.end) {
|
||||
a.force = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
58
Godeps/_workspace/src/github.com/goamz/goamz/aws/attempt_test.go
сгенерированный
поставляемый
Обычный файл
58
Godeps/_workspace/src/github.com/goamz/goamz/aws/attempt_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,58 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func (S) TestAttemptTiming(c *C) {
|
||||
testAttempt := aws.AttemptStrategy{
|
||||
Total: 0.25e9,
|
||||
Delay: 0.1e9,
|
||||
}
|
||||
want := []time.Duration{0, 0.1e9, 0.2e9, 0.2e9}
|
||||
got := make([]time.Duration, 0, len(want)) // avoid allocation when testing timing
|
||||
t0 := time.Now()
|
||||
for a := testAttempt.Start(); a.Next(); {
|
||||
got = append(got, time.Now().Sub(t0))
|
||||
}
|
||||
got = append(got, time.Now().Sub(t0))
|
||||
c.Assert(got, HasLen, len(want))
|
||||
const margin = 0.01e9
|
||||
for i, got := range want {
|
||||
lo := want[i] - margin
|
||||
hi := want[i] + margin
|
||||
if got < lo || got > hi {
|
||||
c.Errorf("attempt %d want %g got %g", i, want[i].Seconds(), got.Seconds())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (S) TestAttemptNextHasNext(c *C) {
|
||||
a := aws.AttemptStrategy{}.Start()
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.Next(), Equals, false)
|
||||
|
||||
a = aws.AttemptStrategy{}.Start()
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.HasNext(), Equals, false)
|
||||
c.Assert(a.Next(), Equals, false)
|
||||
|
||||
a = aws.AttemptStrategy{Total: 2e8}.Start()
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.HasNext(), Equals, true)
|
||||
time.Sleep(2e8)
|
||||
c.Assert(a.HasNext(), Equals, true)
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.Next(), Equals, false)
|
||||
|
||||
a = aws.AttemptStrategy{Total: 1e8, Min: 2}.Start()
|
||||
time.Sleep(1e8)
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.HasNext(), Equals, true)
|
||||
c.Assert(a.Next(), Equals, true)
|
||||
c.Assert(a.HasNext(), Equals, false)
|
||||
c.Assert(a.Next(), Equals, false)
|
||||
}
|
||||
431
Godeps/_workspace/src/github.com/goamz/goamz/aws/aws.go
сгенерированный
поставляемый
Обычный файл
431
Godeps/_workspace/src/github.com/goamz/goamz/aws/aws.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,431 @@
|
||||
//
|
||||
// goamz - Go packages to interact with the Amazon Web Services.
|
||||
//
|
||||
// https://wiki.ubuntu.com/goamz
|
||||
//
|
||||
// Copyright (c) 2011 Canonical Ltd.
|
||||
//
|
||||
// Written by Gustavo Niemeyer <gustavo.niemeyer@canonical.com>
|
||||
//
|
||||
package aws
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/vaughan0/go-ini"
|
||||
)
|
||||
|
||||
// Defines the valid signers
|
||||
const (
|
||||
V2Signature = iota
|
||||
V4Signature = iota
|
||||
Route53Signature = iota
|
||||
)
|
||||
|
||||
// Defines the service endpoint and correct Signer implementation to use
|
||||
// to sign requests for this endpoint
|
||||
type ServiceInfo struct {
|
||||
Endpoint string
|
||||
Signer uint
|
||||
}
|
||||
|
||||
// Region defines the URLs where AWS services may be accessed.
|
||||
//
|
||||
// See http://goo.gl/d8BP1 for more details.
|
||||
type Region struct {
|
||||
Name string // the canonical name of this region.
|
||||
EC2Endpoint string
|
||||
S3Endpoint string
|
||||
S3BucketEndpoint string // Not needed by AWS S3. Use ${bucket} for bucket name.
|
||||
S3LocationConstraint bool // true if this region requires a LocationConstraint declaration.
|
||||
S3LowercaseBucket bool // true if the region requires bucket names to be lower case.
|
||||
SDBEndpoint string
|
||||
SESEndpoint string
|
||||
SNSEndpoint string
|
||||
SQSEndpoint string
|
||||
IAMEndpoint string
|
||||
ELBEndpoint string
|
||||
DynamoDBEndpoint string
|
||||
CloudWatchServicepoint ServiceInfo
|
||||
AutoScalingEndpoint string
|
||||
RDSEndpoint ServiceInfo
|
||||
STSEndpoint string
|
||||
CloudFormationEndpoint string
|
||||
ECSEndpoint string
|
||||
}
|
||||
|
||||
var Regions = map[string]Region{
|
||||
APNortheast.Name: APNortheast,
|
||||
APSoutheast.Name: APSoutheast,
|
||||
APSoutheast2.Name: APSoutheast2,
|
||||
EUCentral.Name: EUCentral,
|
||||
EUWest.Name: EUWest,
|
||||
USEast.Name: USEast,
|
||||
USWest.Name: USWest,
|
||||
USWest2.Name: USWest2,
|
||||
USGovWest.Name: USGovWest,
|
||||
SAEast.Name: SAEast,
|
||||
CNNorth.Name: CNNorth,
|
||||
}
|
||||
|
||||
// Designates a signer interface suitable for signing AWS requests, params
|
||||
// should be appropriately encoded for the request before signing.
|
||||
//
|
||||
// A signer should be initialized with Auth and the appropriate endpoint.
|
||||
type Signer interface {
|
||||
Sign(method, path string, params map[string]string)
|
||||
}
|
||||
|
||||
// An AWS Service interface with the API to query the AWS service
|
||||
//
|
||||
// Supplied as an easy way to mock out service calls during testing.
|
||||
type AWSService interface {
|
||||
// Queries the AWS service at a given method/path with the params and
|
||||
// returns an http.Response and error
|
||||
Query(method, path string, params map[string]string) (*http.Response, error)
|
||||
// Builds an error given an XML payload in the http.Response, can be used
|
||||
// to process an error if the status code is not 200 for example.
|
||||
BuildError(r *http.Response) error
|
||||
}
|
||||
|
||||
// Implements a Server Query/Post API to easily query AWS services and build
|
||||
// errors when desired
|
||||
type Service struct {
|
||||
service ServiceInfo
|
||||
signer Signer
|
||||
}
|
||||
|
||||
// Create a base set of params for an action
|
||||
func MakeParams(action string) map[string]string {
|
||||
params := make(map[string]string)
|
||||
params["Action"] = action
|
||||
return params
|
||||
}
|
||||
|
||||
// Create a new AWS server to handle making requests
|
||||
func NewService(auth Auth, service ServiceInfo) (s *Service, err error) {
|
||||
var signer Signer
|
||||
switch service.Signer {
|
||||
case V2Signature:
|
||||
signer, err = NewV2Signer(auth, service)
|
||||
// case V4Signature:
|
||||
// signer, err = NewV4Signer(auth, service, Regions["eu-west-1"])
|
||||
default:
|
||||
err = fmt.Errorf("Unsupported signer for service")
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = &Service{service: service, signer: signer}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) Query(method, path string, params map[string]string) (resp *http.Response, err error) {
|
||||
params["Timestamp"] = time.Now().UTC().Format(time.RFC3339)
|
||||
u, err := url.Parse(s.service.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Path = path
|
||||
|
||||
s.signer.Sign(method, path, params)
|
||||
if method == "GET" {
|
||||
u.RawQuery = multimap(params).Encode()
|
||||
resp, err = http.Get(u.String())
|
||||
} else if method == "POST" {
|
||||
resp, err = http.PostForm(u.String(), multimap(params))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) BuildError(r *http.Response) error {
|
||||
errors := ErrorResponse{}
|
||||
xml.NewDecoder(r.Body).Decode(&errors)
|
||||
var err Error
|
||||
err = errors.Errors
|
||||
err.RequestId = errors.RequestId
|
||||
err.StatusCode = r.StatusCode
|
||||
if err.Message == "" {
|
||||
err.Message = r.Status
|
||||
}
|
||||
return &err
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Errors Error `xml:"Error"`
|
||||
RequestId string // A unique ID for tracking the request
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
StatusCode int
|
||||
Type string
|
||||
Code string
|
||||
Message string
|
||||
RequestId string
|
||||
}
|
||||
|
||||
func (err *Error) Error() string {
|
||||
return fmt.Sprintf("Type: %s, Code: %s, Message: %s",
|
||||
err.Type, err.Code, err.Message,
|
||||
)
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
AccessKey, SecretKey string
|
||||
token string
|
||||
expiration time.Time
|
||||
}
|
||||
|
||||
func (a *Auth) Token() string {
|
||||
if a.token == "" {
|
||||
return ""
|
||||
}
|
||||
if time.Since(a.expiration) >= -30*time.Second { //in an ideal world this should be zero assuming the instance is synching it's clock
|
||||
*a, _ = GetAuth("", "", "", time.Time{})
|
||||
}
|
||||
return a.token
|
||||
}
|
||||
|
||||
func (a *Auth) Expiration() time.Time {
|
||||
return a.expiration
|
||||
}
|
||||
|
||||
// To be used with other APIs that return auth credentials such as STS
|
||||
func NewAuth(accessKey, secretKey, token string, expiration time.Time) *Auth {
|
||||
return &Auth{
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
token: token,
|
||||
expiration: expiration,
|
||||
}
|
||||
}
|
||||
|
||||
// ResponseMetadata
|
||||
type ResponseMetadata struct {
|
||||
RequestId string // A unique ID for tracking the request
|
||||
}
|
||||
|
||||
type BaseResponse struct {
|
||||
ResponseMetadata ResponseMetadata
|
||||
}
|
||||
|
||||
var unreserved = make([]bool, 128)
|
||||
var hex = "0123456789ABCDEF"
|
||||
|
||||
func init() {
|
||||
// RFC3986
|
||||
u := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-_.~"
|
||||
for _, c := range u {
|
||||
unreserved[c] = true
|
||||
}
|
||||
}
|
||||
|
||||
func multimap(p map[string]string) url.Values {
|
||||
q := make(url.Values, len(p))
|
||||
for k, v := range p {
|
||||
q[k] = []string{v}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
type credentials struct {
|
||||
Code string
|
||||
LastUpdated string
|
||||
Type string
|
||||
AccessKeyId string
|
||||
SecretAccessKey string
|
||||
Token string
|
||||
Expiration string
|
||||
}
|
||||
|
||||
// GetMetaData retrieves instance metadata about the current machine.
|
||||
//
|
||||
// See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html for more details.
|
||||
func GetMetaData(path string) (contents []byte, err error) {
|
||||
url := "http://169.254.169.254/latest/meta-data/" + path
|
||||
|
||||
resp, err := RetryingClient.Get(url)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("Code %d returned for url %s", resp.StatusCode, url)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return []byte(body), err
|
||||
}
|
||||
|
||||
func getInstanceCredentials() (cred credentials, err error) {
|
||||
credentialPath := "iam/security-credentials/"
|
||||
|
||||
// Get the instance role
|
||||
role, err := GetMetaData(credentialPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the instance role credentials
|
||||
credentialJSON, err := GetMetaData(credentialPath + string(role))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(credentialJSON), &cred)
|
||||
return
|
||||
}
|
||||
|
||||
// GetAuth creates an Auth based on either passed in credentials,
|
||||
// environment information or instance based role credentials.
|
||||
func GetAuth(accessKey string, secretKey, token string, expiration time.Time) (auth Auth, err error) {
|
||||
// First try passed in credentials
|
||||
if accessKey != "" && secretKey != "" {
|
||||
return Auth{accessKey, secretKey, token, expiration}, nil
|
||||
}
|
||||
|
||||
// Next try to get auth from the shared credentials file
|
||||
auth, err = SharedAuth()
|
||||
if err == nil {
|
||||
// Found auth, return
|
||||
return
|
||||
}
|
||||
|
||||
// Next try to get auth from the environment
|
||||
auth, err = EnvAuth()
|
||||
if err == nil {
|
||||
// Found auth, return
|
||||
return
|
||||
}
|
||||
|
||||
// Next try getting auth from the instance role
|
||||
cred, err := getInstanceCredentials()
|
||||
if err == nil {
|
||||
// Found auth, return
|
||||
auth.AccessKey = cred.AccessKeyId
|
||||
auth.SecretKey = cred.SecretAccessKey
|
||||
auth.token = cred.Token
|
||||
exptdate, err := time.Parse("2006-01-02T15:04:05Z", cred.Expiration)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error Parseing expiration date: cred.Expiration :%s , error: %s \n", cred.Expiration, err)
|
||||
}
|
||||
auth.expiration = exptdate
|
||||
return auth, err
|
||||
}
|
||||
err = errors.New("No valid AWS authentication found")
|
||||
return auth, err
|
||||
}
|
||||
|
||||
// EnvAuth creates an Auth based on environment information.
|
||||
// The AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment
|
||||
// variables are used.
|
||||
// AWS_SESSION_TOKEN is used if present.
|
||||
func EnvAuth() (auth Auth, err error) {
|
||||
auth.AccessKey = os.Getenv("AWS_ACCESS_KEY_ID")
|
||||
if auth.AccessKey == "" {
|
||||
auth.AccessKey = os.Getenv("AWS_ACCESS_KEY")
|
||||
}
|
||||
|
||||
auth.SecretKey = os.Getenv("AWS_SECRET_ACCESS_KEY")
|
||||
if auth.SecretKey == "" {
|
||||
auth.SecretKey = os.Getenv("AWS_SECRET_KEY")
|
||||
}
|
||||
if auth.AccessKey == "" {
|
||||
err = errors.New("AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment")
|
||||
}
|
||||
if auth.SecretKey == "" {
|
||||
err = errors.New("AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment")
|
||||
}
|
||||
|
||||
auth.token = os.Getenv("AWS_SESSION_TOKEN")
|
||||
return
|
||||
}
|
||||
|
||||
// SharedAuth creates an Auth based on shared credentials stored in
|
||||
// $HOME/.aws/credentials. The AWS_PROFILE environment variables is used to
|
||||
// select the profile.
|
||||
func SharedAuth() (auth Auth, err error) {
|
||||
var profileName = os.Getenv("AWS_PROFILE")
|
||||
|
||||
if profileName == "" {
|
||||
profileName = "default"
|
||||
}
|
||||
|
||||
var credentialsFile = os.Getenv("AWS_CREDENTIAL_FILE")
|
||||
if credentialsFile == "" {
|
||||
var homeDir = os.Getenv("HOME")
|
||||
if homeDir == "" {
|
||||
err = errors.New("Could not get HOME")
|
||||
return
|
||||
}
|
||||
credentialsFile = homeDir + "/.aws/credentials"
|
||||
}
|
||||
|
||||
file, err := ini.LoadFile(credentialsFile)
|
||||
if err != nil {
|
||||
err = errors.New("Couldn't parse AWS credentials file")
|
||||
return
|
||||
}
|
||||
|
||||
var profile = file[profileName]
|
||||
if profile == nil {
|
||||
err = errors.New("Couldn't find profile in AWS credentials file")
|
||||
return
|
||||
}
|
||||
|
||||
auth.AccessKey = profile["aws_access_key_id"]
|
||||
auth.SecretKey = profile["aws_secret_access_key"]
|
||||
|
||||
if auth.AccessKey == "" {
|
||||
err = errors.New("AWS_ACCESS_KEY_ID not found in environment in credentials file")
|
||||
}
|
||||
if auth.SecretKey == "" {
|
||||
err = errors.New("AWS_SECRET_ACCESS_KEY not found in credentials file")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Encode takes a string and URI-encodes it in a way suitable
|
||||
// to be used in AWS signatures.
|
||||
func Encode(s string) string {
|
||||
encode := false
|
||||
for i := 0; i != len(s); i++ {
|
||||
c := s[i]
|
||||
if c > 127 || !unreserved[c] {
|
||||
encode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !encode {
|
||||
return s
|
||||
}
|
||||
e := make([]byte, len(s)*3)
|
||||
ei := 0
|
||||
for i := 0; i != len(s); i++ {
|
||||
c := s[i]
|
||||
if c > 127 || !unreserved[c] {
|
||||
e[ei] = '%'
|
||||
e[ei+1] = hex[c>>4]
|
||||
e[ei+2] = hex[c&0xF]
|
||||
ei += 3
|
||||
} else {
|
||||
e[ei] = c
|
||||
ei += 1
|
||||
}
|
||||
}
|
||||
return string(e[:ei])
|
||||
}
|
||||
211
Godeps/_workspace/src/github.com/goamz/goamz/aws/aws_test.go
сгенерированный
поставляемый
Обычный файл
211
Godeps/_workspace/src/github.com/goamz/goamz/aws/aws_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,211 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/goamz/goamz/aws"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
TestingT(t)
|
||||
}
|
||||
|
||||
var _ = Suite(&S{})
|
||||
|
||||
type S struct {
|
||||
environ []string
|
||||
}
|
||||
|
||||
func (s *S) SetUpSuite(c *C) {
|
||||
s.environ = os.Environ()
|
||||
}
|
||||
|
||||
func (s *S) TearDownTest(c *C) {
|
||||
os.Clearenv()
|
||||
for _, kv := range s.environ {
|
||||
l := strings.SplitN(kv, "=", 2)
|
||||
os.Setenv(l[0], l[1])
|
||||
}
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuthNoHome(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_PROFILE", "foo")
|
||||
_, err := aws.SharedAuth()
|
||||
c.Assert(err, ErrorMatches, "Could not get HOME")
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuthNoCredentialsFile(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_PROFILE", "foo")
|
||||
os.Setenv("HOME", "/tmp")
|
||||
_, err := aws.SharedAuth()
|
||||
c.Assert(err, ErrorMatches, "Couldn't parse AWS credentials file")
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuthNoProfileInFile(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_PROFILE", "foo")
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = os.Mkdir(d+"/.aws", 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ioutil.WriteFile(d+"/.aws/credentials", []byte("[bar]\n"), 0644)
|
||||
os.Setenv("HOME", d)
|
||||
|
||||
_, err = aws.SharedAuth()
|
||||
c.Assert(err, ErrorMatches, "Couldn't find profile in AWS credentials file")
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuthNoKeysInProfile(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_PROFILE", "bar")
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = os.Mkdir(d+"/.aws", 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ioutil.WriteFile(d+"/.aws/credentials", []byte("[bar]\nawsaccesskeyid = AK.."), 0644)
|
||||
os.Setenv("HOME", d)
|
||||
|
||||
_, err = aws.SharedAuth()
|
||||
c.Assert(err, ErrorMatches, "AWS_SECRET_ACCESS_KEY not found in credentials file")
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuthDefaultCredentials(c *C) {
|
||||
os.Clearenv()
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = os.Mkdir(d+"/.aws", 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ioutil.WriteFile(d+"/.aws/credentials", []byte("[default]\naws_access_key_id = access\naws_secret_access_key = secret\n"), 0644)
|
||||
os.Setenv("HOME", d)
|
||||
|
||||
auth, err := aws.SharedAuth()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestSharedAuth(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_PROFILE", "bar")
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = os.Mkdir(d+"/.aws", 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ioutil.WriteFile(d+"/.aws/credentials", []byte("[bar]\naws_access_key_id = access\naws_secret_access_key = secret\n"), 0644)
|
||||
os.Setenv("HOME", d)
|
||||
|
||||
auth, err := aws.SharedAuth()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthNoSecret(c *C) {
|
||||
os.Clearenv()
|
||||
_, err := aws.EnvAuth()
|
||||
c.Assert(err, ErrorMatches, "AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment")
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthNoAccess(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "foo")
|
||||
_, err := aws.EnvAuth()
|
||||
c.Assert(err, ErrorMatches, "AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment")
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuth(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||
auth, err := aws.EnvAuth()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthAlt(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY", "access")
|
||||
auth, err := aws.EnvAuth()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestEnvAuthToken(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY", "access")
|
||||
os.Setenv("AWS_SESSION_TOKEN", "token")
|
||||
auth, err := aws.EnvAuth()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth.SecretKey, Equals, "secret")
|
||||
c.Assert(auth.AccessKey, Equals, "access")
|
||||
c.Assert(auth.Token(), Equals, "token")
|
||||
}
|
||||
|
||||
func (s *S) TestGetAuthStatic(c *C) {
|
||||
exptdate := time.Now().Add(time.Hour)
|
||||
auth, err := aws.GetAuth("access", "secret", "token", exptdate)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth.AccessKey, Equals, "access")
|
||||
c.Assert(auth.SecretKey, Equals, "secret")
|
||||
c.Assert(auth.Token(), Equals, "token")
|
||||
c.Assert(auth.Expiration(), Equals, exptdate)
|
||||
}
|
||||
|
||||
func (s *S) TestGetAuthEnv(c *C) {
|
||||
os.Clearenv()
|
||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
|
||||
os.Setenv("AWS_ACCESS_KEY_ID", "access")
|
||||
auth, err := aws.GetAuth("", "", "", time.Time{})
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(auth, Equals, aws.Auth{SecretKey: "secret", AccessKey: "access"})
|
||||
}
|
||||
|
||||
func (s *S) TestEncode(c *C) {
|
||||
c.Assert(aws.Encode("foo"), Equals, "foo")
|
||||
c.Assert(aws.Encode("/"), Equals, "%2F")
|
||||
}
|
||||
|
||||
func (s *S) TestRegionsAreNamed(c *C) {
|
||||
for n, r := range aws.Regions {
|
||||
c.Assert(n, Equals, r.Name)
|
||||
}
|
||||
}
|
||||
124
Godeps/_workspace/src/github.com/goamz/goamz/aws/client.go
сгенерированный
поставляемый
Обычный файл
124
Godeps/_workspace/src/github.com/goamz/goamz/aws/client.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,124 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RetryableFunc func(*http.Request, *http.Response, error) bool
|
||||
type WaitFunc func(try int)
|
||||
type DeadlineFunc func() time.Time
|
||||
|
||||
type ResilientTransport struct {
|
||||
// Timeout is the maximum amount of time a dial will wait for
|
||||
// a connect to complete.
|
||||
//
|
||||
// The default is no timeout.
|
||||
//
|
||||
// With or without a timeout, the operating system may impose
|
||||
// its own earlier timeout. For instance, TCP timeouts are
|
||||
// often around 3 minutes.
|
||||
DialTimeout time.Duration
|
||||
|
||||
// MaxTries, if non-zero, specifies the number of times we will retry on
|
||||
// failure. Retries are only attempted for temporary network errors or known
|
||||
// safe failures.
|
||||
MaxTries int
|
||||
Deadline DeadlineFunc
|
||||
ShouldRetry RetryableFunc
|
||||
Wait WaitFunc
|
||||
transport *http.Transport
|
||||
}
|
||||
|
||||
// Convenience method for creating an http client
|
||||
func NewClient(rt *ResilientTransport) *http.Client {
|
||||
rt.transport = &http.Transport{
|
||||
Dial: func(netw, addr string) (net.Conn, error) {
|
||||
c, err := net.DialTimeout(netw, addr, rt.DialTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.SetDeadline(rt.Deadline())
|
||||
return c, nil
|
||||
},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
// TODO: Would be nice is ResilientTransport allowed clients to initialize
|
||||
// with http.Transport attributes.
|
||||
return &http.Client{
|
||||
Transport: rt,
|
||||
}
|
||||
}
|
||||
|
||||
var retryingTransport = &ResilientTransport{
|
||||
Deadline: func() time.Time {
|
||||
return time.Now().Add(5 * time.Second)
|
||||
},
|
||||
DialTimeout: 10 * time.Second,
|
||||
MaxTries: 3,
|
||||
ShouldRetry: awsRetry,
|
||||
Wait: ExpBackoff,
|
||||
}
|
||||
|
||||
// Exported default client
|
||||
var RetryingClient = NewClient(retryingTransport)
|
||||
|
||||
func (t *ResilientTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return t.tries(req)
|
||||
}
|
||||
|
||||
// Retry a request a maximum of t.MaxTries times.
|
||||
// We'll only retry if the proper criteria are met.
|
||||
// If a wait function is specified, wait that amount of time
|
||||
// In between requests.
|
||||
func (t *ResilientTransport) tries(req *http.Request) (res *http.Response, err error) {
|
||||
for try := 0; try < t.MaxTries; try += 1 {
|
||||
res, err = t.transport.RoundTrip(req)
|
||||
|
||||
if !t.ShouldRetry(req, res, err) {
|
||||
break
|
||||
}
|
||||
if res != nil {
|
||||
res.Body.Close()
|
||||
}
|
||||
if t.Wait != nil {
|
||||
t.Wait(try)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ExpBackoff(try int) {
|
||||
time.Sleep(100 * time.Millisecond *
|
||||
time.Duration(math.Exp2(float64(try))))
|
||||
}
|
||||
|
||||
func LinearBackoff(try int) {
|
||||
time.Sleep(time.Duration(try*100) * time.Millisecond)
|
||||
}
|
||||
|
||||
// Decide if we should retry a request.
|
||||
// In general, the criteria for retrying a request is described here
|
||||
// http://docs.aws.amazon.com/general/latest/gr/api-retries.html
|
||||
func awsRetry(req *http.Request, res *http.Response, err error) bool {
|
||||
retry := false
|
||||
|
||||
// Retry if there's a temporary network error.
|
||||
if neterr, ok := err.(net.Error); ok {
|
||||
if neterr.Temporary() {
|
||||
retry = true
|
||||
}
|
||||
}
|
||||
|
||||
// Retry if we get a 5xx series error.
|
||||
if res != nil {
|
||||
if res.StatusCode >= 500 && res.StatusCode < 600 {
|
||||
retry = true
|
||||
}
|
||||
}
|
||||
|
||||
return retry
|
||||
}
|
||||
121
Godeps/_workspace/src/github.com/goamz/goamz/aws/client_test.go
сгенерированный
поставляемый
Обычный файл
121
Godeps/_workspace/src/github.com/goamz/goamz/aws/client_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,121 @@
|
||||
package aws_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goamz/goamz/aws"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Retrieve the response from handler using aws.RetryingClient
|
||||
func serveAndGet(handler http.HandlerFunc) (body string, err error) {
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
resp, err := aws.RetryingClient.Get(ts.URL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("Bad status code: %d", resp.StatusCode)
|
||||
}
|
||||
greeting, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return strings.TrimSpace(string(greeting)), nil
|
||||
}
|
||||
|
||||
func TestClient_expected(t *testing.T) {
|
||||
body := "foo bar"
|
||||
|
||||
resp, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, body)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp != body {
|
||||
t.Fatal("Body not as expected.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_delay(t *testing.T) {
|
||||
body := "baz"
|
||||
wait := 4
|
||||
resp, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) {
|
||||
if wait < 0 {
|
||||
// If we dipped to zero delay and still failed.
|
||||
t.Fatal("Never succeeded.")
|
||||
}
|
||||
wait -= 1
|
||||
time.Sleep(time.Second * time.Duration(wait))
|
||||
fmt.Fprintln(w, body)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp != body {
|
||||
t.Fatal("Body not as expected.", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_no4xxRetry(t *testing.T) {
|
||||
tries := 0
|
||||
|
||||
// Fail once before succeeding.
|
||||
_, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) {
|
||||
tries += 1
|
||||
http.Error(w, "error", 404)
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
if tries != 1 {
|
||||
t.Fatalf("should only try once: %d", tries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_retries(t *testing.T) {
|
||||
body := "biz"
|
||||
failed := false
|
||||
// Fail once before succeeding.
|
||||
resp, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !failed {
|
||||
http.Error(w, "error", 500)
|
||||
failed = true
|
||||
} else {
|
||||
fmt.Fprintln(w, body)
|
||||
}
|
||||
})
|
||||
if failed != true {
|
||||
t.Error("We didn't retry!")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp != body {
|
||||
t.Fatal("Body not as expected.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_fails(t *testing.T) {
|
||||
tries := 0
|
||||
// Fail 3 times and return the last error.
|
||||
_, err := serveAndGet(func(w http.ResponseWriter, r *http.Request) {
|
||||
tries += 1
|
||||
http.Error(w, "error", 500)
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tries != 3 {
|
||||
t.Fatal("Didn't retry enough")
|
||||
}
|
||||
}
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user