Bump deps

This commit is contained in:
Jeff Mitchell 2016-11-16 18:22:54 -05:00
parent 6b5327a04d
commit ddb9a0ce52
80 changed files with 2461 additions and 1269 deletions

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
@ -301,6 +302,65 @@ const (
ContainerAccessTypeContainer ContainerAccessType = "container" ContainerAccessTypeContainer ContainerAccessType = "container"
) )
// ContainerAccessOptions are used when setting ACLs of containers (after creation)
type ContainerAccessOptions struct {
ContainerAccess ContainerAccessType
Timeout int
LeaseID string
}
// AccessPolicyDetails are used for SETTING policies
type AccessPolicyDetails struct {
ID string
StartTime time.Time
ExpiryTime time.Time
CanRead bool
CanWrite bool
CanDelete bool
}
// ContainerPermissions is used when setting permissions and Access Policies for containers.
type ContainerPermissions struct {
AccessOptions ContainerAccessOptions
AccessPolicy AccessPolicyDetails
}
// AccessPolicyDetailsXML has specifics about an access policy
// annotated with XML details.
type AccessPolicyDetailsXML struct {
StartTime time.Time `xml:"Start"`
ExpiryTime time.Time `xml:"Expiry"`
Permission string `xml:"Permission"`
}
// SignedIdentifier is a wrapper for a specific policy
type SignedIdentifier struct {
ID string `xml:"Id"`
AccessPolicy AccessPolicyDetailsXML `xml:"AccessPolicy"`
}
// SignedIdentifiers part of the response from GetPermissions call.
type SignedIdentifiers struct {
SignedIdentifiers []SignedIdentifier `xml:"SignedIdentifier"`
}
// AccessPolicy is the response type from the GetPermissions call.
type AccessPolicy struct {
SignedIdentifiersList SignedIdentifiers `xml:"SignedIdentifiers"`
}
// ContainerAccessResponse is returned for the GetContainerPermissions function.
// This contains both the permission and access policy for the container.
type ContainerAccessResponse struct {
ContainerAccess ContainerAccessType
AccessPolicy SignedIdentifiers
}
// ContainerAccessHeader references header used when setting/getting container ACL
const (
ContainerAccessHeader string = "x-ms-blob-public-access"
)
// Maximum sizes (per REST API) for various concepts // Maximum sizes (per REST API) for various concepts
const ( const (
MaxBlobBlockSize = 4 * 1024 * 1024 MaxBlobBlockSize = 4 * 1024 * 1024
@ -416,7 +476,7 @@ func (b BlobStorageClient) createContainer(name string, access ContainerAccessTy
headers := b.client.getStandardHeaders() headers := b.client.getStandardHeaders()
if access != "" { if access != "" {
headers["x-ms-blob-public-access"] = string(access) headers[ContainerAccessHeader] = string(access)
} }
return b.client.exec(verb, uri, headers, nil) return b.client.exec(verb, uri, headers, nil)
} }
@ -438,6 +498,101 @@ func (b BlobStorageClient) ContainerExists(name string) (bool, error) {
return false, err return false, err
} }
// SetContainerPermissions sets up container permissions as per https://msdn.microsoft.com/en-us/library/azure/dd179391.aspx
func (b BlobStorageClient) SetContainerPermissions(container string, containerPermissions ContainerPermissions) (err error) {
params := url.Values{
"restype": {"container"},
"comp": {"acl"},
}
if containerPermissions.AccessOptions.Timeout > 0 {
params.Add("timeout", strconv.Itoa(containerPermissions.AccessOptions.Timeout))
}
uri := b.client.getEndpoint(blobServiceName, pathForContainer(container), params)
headers := b.client.getStandardHeaders()
if containerPermissions.AccessOptions.ContainerAccess != "" {
headers[ContainerAccessHeader] = string(containerPermissions.AccessOptions.ContainerAccess)
}
if containerPermissions.AccessOptions.LeaseID != "" {
headers[leaseID] = containerPermissions.AccessOptions.LeaseID
}
// generate the XML for the SharedAccessSignature if required.
accessPolicyXML, err := generateAccessPolicy(containerPermissions.AccessPolicy)
if err != nil {
return err
}
var resp *storageResponse
if accessPolicyXML != "" {
headers["Content-Length"] = strconv.Itoa(len(accessPolicyXML))
resp, err = b.client.exec("PUT", uri, headers, strings.NewReader(accessPolicyXML))
} else {
resp, err = b.client.exec("PUT", uri, headers, nil)
}
if err != nil {
return err
}
if resp != nil {
defer func() {
err = resp.body.Close()
}()
if resp.statusCode != http.StatusOK {
return errors.New("Unable to set permissions")
}
}
return nil
}
// GetContainerPermissions gets the container permissions as per https://msdn.microsoft.com/en-us/library/azure/dd179469.aspx
// If timeout is 0 then it will not be passed to Azure
// leaseID will only be passed to Azure if populated
// Returns permissionResponse which is combined permissions and AccessPolicy
func (b BlobStorageClient) GetContainerPermissions(container string, timeout int, leaseID string) (permissionResponse *ContainerAccessResponse, err error) {
params := url.Values{"restype": {"container"},
"comp": {"acl"}}
if timeout > 0 {
params.Add("timeout", strconv.Itoa(timeout))
}
uri := b.client.getEndpoint(blobServiceName, pathForContainer(container), params)
headers := b.client.getStandardHeaders()
if leaseID != "" {
headers[leaseID] = leaseID
}
resp, err := b.client.exec("GET", uri, headers, nil)
if err != nil {
return nil, err
}
// containerAccess. Blob, Container, empty
containerAccess := resp.headers.Get(http.CanonicalHeaderKey(ContainerAccessHeader))
defer func() {
err = resp.body.Close()
}()
var out AccessPolicy
err = xmlUnmarshal(resp.body, &out.SignedIdentifiersList)
if err != nil {
return nil, err
}
permissionResponse = &ContainerAccessResponse{}
permissionResponse.AccessPolicy = out.SignedIdentifiersList
permissionResponse.ContainerAccess = ContainerAccessType(containerAccess)
return permissionResponse, nil
}
// DeleteContainer deletes the container with given name on the storage // DeleteContainer deletes the container with given name on the storage
// account. If the container does not exist returns error. // account. If the container does not exist returns error.
// //
@ -614,8 +769,6 @@ func (b BlobStorageClient) AcquireLease(container string, name string, leaseTime
return returnedLeaseID, nil return returnedLeaseID, nil
} }
// what should we return in case of HTTP 201 but no lease ID?
// or it just cant happen? (brave words)
return "", errors.New("LeaseID not returned") return "", errors.New("LeaseID not returned")
} }
@ -1106,15 +1259,20 @@ func (b BlobStorageClient) AppendBlock(container, name string, chunk []byte, ext
// //
// See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx // See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx
func (b BlobStorageClient) CopyBlob(container, name, sourceBlob string) error { func (b BlobStorageClient) CopyBlob(container, name, sourceBlob string) error {
copyID, err := b.startBlobCopy(container, name, sourceBlob) copyID, err := b.StartBlobCopy(container, name, sourceBlob)
if err != nil { if err != nil {
return err return err
} }
return b.waitForBlobCopy(container, name, copyID) return b.WaitForBlobCopy(container, name, copyID)
} }
func (b BlobStorageClient) startBlobCopy(container, name, sourceBlob string) (string, error) { // StartBlobCopy starts a blob copy operation.
// sourceBlob parameter must be a canonical URL to the blob (can be
// obtained using GetBlobURL method.)
//
// See https://msdn.microsoft.com/en-us/library/azure/dd894037.aspx
func (b BlobStorageClient) StartBlobCopy(container, name, sourceBlob string) (string, error) {
uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{}) uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), url.Values{})
headers := b.client.getStandardHeaders() headers := b.client.getStandardHeaders()
@ -1137,7 +1295,39 @@ func (b BlobStorageClient) startBlobCopy(container, name, sourceBlob string) (st
return copyID, nil return copyID, nil
} }
func (b BlobStorageClient) waitForBlobCopy(container, name, copyID string) error { // AbortBlobCopy aborts a BlobCopy which has already been triggered by the StartBlobCopy function.
// copyID is generated from StartBlobCopy function.
// currentLeaseID is required IF the destination blob has an active lease on it.
// As defined in https://msdn.microsoft.com/en-us/library/azure/jj159098.aspx
func (b BlobStorageClient) AbortBlobCopy(container, name, copyID, currentLeaseID string, timeout int) error {
params := url.Values{"comp": {"copy"}, "copyid": {copyID}}
if timeout > 0 {
params.Add("timeout", strconv.Itoa(timeout))
}
uri := b.client.getEndpoint(blobServiceName, pathForBlob(container, name), params)
headers := b.client.getStandardHeaders()
headers["x-ms-copy-action"] = "abort"
if currentLeaseID != "" {
headers[leaseID] = currentLeaseID
}
resp, err := b.client.exec("PUT", uri, headers, nil)
if err != nil {
return err
}
defer resp.body.Close()
if err := checkRespCode(resp.statusCode, []int{http.StatusNoContent}); err != nil {
return err
}
return nil
}
// WaitForBlobCopy loops until a BlobCopy operation is completed (or fails with error)
func (b BlobStorageClient) WaitForBlobCopy(container, name, copyID string) error {
for { for {
props, err := b.GetBlobProperties(container, name) props, err := b.GetBlobProperties(container, name)
if err != nil { if err != nil {
@ -1181,10 +1371,12 @@ func (b BlobStorageClient) DeleteBlob(container, name string, extraHeaders map[s
// See https://msdn.microsoft.com/en-us/library/azure/dd179413.aspx // See https://msdn.microsoft.com/en-us/library/azure/dd179413.aspx
func (b BlobStorageClient) DeleteBlobIfExists(container, name string, extraHeaders map[string]string) (bool, error) { func (b BlobStorageClient) DeleteBlobIfExists(container, name string, extraHeaders map[string]string) (bool, error) {
resp, err := b.deleteBlob(container, name, extraHeaders) resp, err := b.deleteBlob(container, name, extraHeaders)
if resp != nil && (resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound) { if resp != nil {
return resp.statusCode == http.StatusAccepted, nil defer resp.body.Close()
if resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound {
return resp.statusCode == http.StatusAccepted, nil
}
} }
defer resp.body.Close()
return false, err return false, err
} }
@ -1210,17 +1402,18 @@ func pathForBlob(container, name string) string {
return fmt.Sprintf("/%s/%s", container, name) return fmt.Sprintf("/%s/%s", container, name)
} }
// GetBlobSASURI creates an URL to the specified blob which contains the Shared // GetBlobSASURIWithSignedIPAndProtocol creates an URL to the specified blob which contains the Shared
// Access Signature with specified permissions and expiration time. // Access Signature with specified permissions and expiration time. Also includes signedIPRange and allowed procotols.
// If old API version is used but no signedIP is passed (ie empty string) then this should still work.
// We only populate the signedIP when it non-empty.
// //
// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx // See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx
func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Time, permissions string) (string, error) { func (b BlobStorageClient) GetBlobSASURIWithSignedIPAndProtocol(container, name string, expiry time.Time, permissions string, signedIPRange string, HTTPSOnly bool) (string, error) {
var ( var (
signedPermissions = permissions signedPermissions = permissions
blobURL = b.GetBlobURL(container, name) blobURL = b.GetBlobURL(container, name)
) )
canonicalizedResource, err := b.client.buildCanonicalizedResource(blobURL) canonicalizedResource, err := b.client.buildCanonicalizedResource(blobURL)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -1232,7 +1425,6 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
// We need to replace + with %2b first to avoid being treated as a space (which is correct for query strings, but not the path component). // We need to replace + with %2b first to avoid being treated as a space (which is correct for query strings, but not the path component).
canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1) canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1)
canonicalizedResource, err = url.QueryUnescape(canonicalizedResource) canonicalizedResource, err = url.QueryUnescape(canonicalizedResource)
if err != nil { if err != nil {
return "", err return "", err
@ -1241,7 +1433,11 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
signedExpiry := expiry.UTC().Format(time.RFC3339) signedExpiry := expiry.UTC().Format(time.RFC3339)
signedResource := "b" signedResource := "b"
stringToSign, err := blobSASStringToSign(b.client.apiVersion, canonicalizedResource, signedExpiry, signedPermissions) protocols := "https,http"
if HTTPSOnly {
protocols = "https"
}
stringToSign, err := blobSASStringToSign(b.client.apiVersion, canonicalizedResource, signedExpiry, signedPermissions, signedIPRange, protocols)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -1255,6 +1451,13 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
"sig": {sig}, "sig": {sig},
} }
if b.client.apiVersion >= "2015-04-05" {
sasParams.Add("spr", protocols)
if signedIPRange != "" {
sasParams.Add("sip", signedIPRange)
}
}
sasURL, err := url.Parse(blobURL) sasURL, err := url.Parse(blobURL)
if err != nil { if err != nil {
return "", err return "", err
@ -1263,16 +1466,89 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
return sasURL.String(), nil return sasURL.String(), nil
} }
func blobSASStringToSign(signedVersion, canonicalizedResource, signedExpiry, signedPermissions string) (string, error) { // GetBlobSASURI creates an URL to the specified blob which contains the Shared
// Access Signature with specified permissions and expiration time.
//
// See https://msdn.microsoft.com/en-us/library/azure/ee395415.aspx
func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Time, permissions string) (string, error) {
url, err := b.GetBlobSASURIWithSignedIPAndProtocol(container, name, expiry, permissions, "", false)
return url, err
}
func blobSASStringToSign(signedVersion, canonicalizedResource, signedExpiry, signedPermissions string, signedIP string, protocols string) (string, error) {
var signedStart, signedIdentifier, rscc, rscd, rsce, rscl, rsct string var signedStart, signedIdentifier, rscc, rscd, rsce, rscl, rsct string
if signedVersion >= "2015-02-21" { if signedVersion >= "2015-02-21" {
canonicalizedResource = "/blob" + canonicalizedResource canonicalizedResource = "/blob" + canonicalizedResource
} }
// https://msdn.microsoft.com/en-us/library/azure/dn140255.aspx#Anchor_12
if signedVersion >= "2015-04-05" {
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedIP, protocols, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
}
// reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx // reference: http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
if signedVersion >= "2013-08-15" { if signedVersion >= "2013-08-15" {
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedVersion, rscc, rscd, rsce, rscl, rsct), nil return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", signedPermissions, signedStart, signedExpiry, canonicalizedResource, signedIdentifier, signedVersion, rscc, rscd, rsce, rscl, rsct), nil
} }
return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15") return "", errors.New("storage: not implemented SAS for versions earlier than 2013-08-15")
} }
func generatePermissions(accessPolicy AccessPolicyDetails) (permissions string) {
// generate the permissions string (rwd).
// still want the end user API to have bool flags.
permissions = ""
if accessPolicy.CanRead {
permissions += "r"
}
if accessPolicy.CanWrite {
permissions += "w"
}
if accessPolicy.CanDelete {
permissions += "d"
}
return permissions
}
// convertAccessPolicyToXMLStructs converts between AccessPolicyDetails which is a struct better for API usage to the
// AccessPolicy struct which will get converted to XML.
func convertAccessPolicyToXMLStructs(accessPolicy AccessPolicyDetails) SignedIdentifiers {
return SignedIdentifiers{
SignedIdentifiers: []SignedIdentifier{
{
ID: accessPolicy.ID,
AccessPolicy: AccessPolicyDetailsXML{
StartTime: accessPolicy.StartTime.UTC().Round(time.Second),
ExpiryTime: accessPolicy.ExpiryTime.UTC().Round(time.Second),
Permission: generatePermissions(accessPolicy),
},
},
},
}
}
// generateAccessPolicy generates the XML access policy used as the payload for SetContainerPermissions.
func generateAccessPolicy(accessPolicy AccessPolicyDetails) (accessPolicyXML string, err error) {
if accessPolicy.ID != "" {
signedIdentifiers := convertAccessPolicyToXMLStructs(accessPolicy)
body, _, err := xmlMarshal(signedIdentifiers)
if err != nil {
return "", err
}
xmlByteArray, err := ioutil.ReadAll(body)
if err != nil {
return "", err
}
accessPolicyXML = string(xmlByteArray)
return accessPolicyXML, nil
}
return "", nil
}

View File

@ -305,7 +305,7 @@ func (c Client) buildCanonicalizedResourceTable(uri string) (string, error) {
cr := "/" + c.getCanonicalizedAccountName() cr := "/" + c.getCanonicalizedAccountName()
if len(u.Path) > 0 { if len(u.Path) > 0 {
cr += u.Path cr += u.EscapedPath()
} }
return cr, nil return cr, nil

View File

@ -82,6 +82,24 @@ func (p PeekMessagesParameters) getParameters() url.Values {
return out return out
} }
// UpdateMessageParameters is the set of options can be specified for Update Messsage
// operation. A zero struct does not use any preferences for the request.
type UpdateMessageParameters struct {
PopReceipt string
VisibilityTimeout int
}
func (p UpdateMessageParameters) getParameters() url.Values {
out := url.Values{}
if p.PopReceipt != "" {
out.Set("popreceipt", p.PopReceipt)
}
if p.VisibilityTimeout != 0 {
out.Set("visibilitytimeout", strconv.Itoa(p.VisibilityTimeout))
}
return out
}
// GetMessagesResponse represents a response returned from Get Messages // GetMessagesResponse represents a response returned from Get Messages
// operation. // operation.
type GetMessagesResponse struct { type GetMessagesResponse struct {
@ -304,3 +322,23 @@ func (c QueueServiceClient) DeleteMessage(queue, messageID, popReceipt string) e
defer resp.body.Close() defer resp.body.Close()
return checkRespCode(resp.statusCode, []int{http.StatusNoContent}) return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
} }
// UpdateMessage operation deletes the specified message.
//
// See https://msdn.microsoft.com/en-us/library/azure/hh452234.aspx
func (c QueueServiceClient) UpdateMessage(queue string, messageID string, message string, params UpdateMessageParameters) error {
uri := c.client.getEndpoint(queueServiceName, pathForMessage(queue, messageID), params.getParameters())
req := putMessageRequest{MessageText: message}
body, nn, err := xmlMarshal(req)
if err != nil {
return err
}
headers := c.client.getStandardHeaders()
headers["Content-Length"] = fmt.Sprintf("%d", nn)
resp, err := c.client.exec("PUT", uri, headers, body)
if err != nil {
return err
}
defer resp.body.Close()
return checkRespCode(resp.statusCode, []int{http.StatusNoContent})
}

View File

@ -31,8 +31,7 @@ import (
"strings" "strings"
) )
/*--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
*/
var ( var (
// ErrOutOfBounds - Index out of bounds. // ErrOutOfBounds - Index out of bounds.
@ -63,40 +62,30 @@ var (
ErrInvalidBuffer = errors.New("input buffer contained invalid JSON") ErrInvalidBuffer = errors.New("input buffer contained invalid JSON")
) )
/*--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
*/
/* // Container - an internal structure that holds a reference to the core interface map of the parsed
Container - an internal structure that holds a reference to the core interface map of the parsed // json. Use this container to move context.
json. Use this container to move context.
*/
type Container struct { type Container struct {
object interface{} object interface{}
} }
/* // Data - Return the contained data as an interface{}.
Data - Return the contained data as an interface{}.
*/
func (g *Container) Data() interface{} { func (g *Container) Data() interface{} {
return g.object return g.object
} }
/*--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
*/
/* // Path - Search for a value using dot notation.
Path - Search for a value using dot notation.
*/
func (g *Container) Path(path string) *Container { func (g *Container) Path(path string) *Container {
return g.Search(strings.Split(path, ".")...) return g.Search(strings.Split(path, ".")...)
} }
/* // Search - Attempt to find and return an object within the JSON structure by specifying the
Search - Attempt to find and return an object within the JSON structure by specifying the hierarchy // hierarchy of field names to locate the target. If the search encounters an array and has not
of field names to locate the target. If the search encounters an array and has not reached the end // reached the end target then it will iterate each object of the array for the target and return
target then it will iterate each object of the array for the target and return all of the results in // all of the results in a JSON array.
a JSON array.
*/
func (g *Container) Search(hierarchy ...string) *Container { func (g *Container) Search(hierarchy ...string) *Container {
var object interface{} var object interface{}
@ -124,31 +113,22 @@ func (g *Container) Search(hierarchy ...string) *Container {
return &Container{object} return &Container{object}
} }
/* // S - Shorthand method, does the same thing as Search.
S - Shorthand method, does the same thing as Search.
*/
func (g *Container) S(hierarchy ...string) *Container { func (g *Container) S(hierarchy ...string) *Container {
return g.Search(hierarchy...) return g.Search(hierarchy...)
} }
/* // Exists - Checks whether a path exists.
Exists - Checks whether a path exists.
*/
func (g *Container) Exists(hierarchy ...string) bool { func (g *Container) Exists(hierarchy ...string) bool {
return g.Search(hierarchy...).Data() != nil return g.Search(hierarchy...).Data() != nil
} }
/* // ExistsP - Checks whether a dot notation path exists.
ExistsP - Checks whether a dot notation path exists.
*/
func (g *Container) ExistsP(path string) bool { func (g *Container) ExistsP(path string) bool {
return g.Exists(strings.Split(path, ".")...) return g.Exists(strings.Split(path, ".")...)
} }
/* // Index - Attempt to find and return an object within a JSON array by index.
Index - Attempt to find and return an object with a JSON array by specifying the index of the
target.
*/
func (g *Container) Index(index int) *Container { func (g *Container) Index(index int) *Container {
if array, ok := g.Data().([]interface{}); ok { if array, ok := g.Data().([]interface{}); ok {
if index >= len(array) { if index >= len(array) {
@ -159,11 +139,9 @@ func (g *Container) Index(index int) *Container {
return &Container{nil} return &Container{nil}
} }
/* // Children - Return a slice of all the children of the array. This also works for objects, however,
Children - Return a slice of all the children of the array. This also works for objects, however, // the children returned for an object will NOT be in order and you lose the names of the returned
the children returned for an object will NOT be in order and you lose the names of the returned // objects this way.
objects this way.
*/
func (g *Container) Children() ([]*Container, error) { func (g *Container) Children() ([]*Container, error) {
if array, ok := g.Data().([]interface{}); ok { if array, ok := g.Data().([]interface{}); ok {
children := make([]*Container, len(array)) children := make([]*Container, len(array))
@ -182,9 +160,7 @@ func (g *Container) Children() ([]*Container, error) {
return nil, ErrNotObjOrArray return nil, ErrNotObjOrArray
} }
/* // ChildrenMap - Return a map of all the children of an object.
ChildrenMap - Return a map of all the children of an object.
*/
func (g *Container) ChildrenMap() (map[string]*Container, error) { func (g *Container) ChildrenMap() (map[string]*Container, error) {
if mmap, ok := g.Data().(map[string]interface{}); ok { if mmap, ok := g.Data().(map[string]interface{}); ok {
children := map[string]*Container{} children := map[string]*Container{}
@ -196,14 +172,11 @@ func (g *Container) ChildrenMap() (map[string]*Container, error) {
return nil, ErrNotObj return nil, ErrNotObj
} }
/*--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
*/
/* // Set - Set the value of a field at a JSON path, any parts of the path that do not exist will be
Set - Set the value of a field at a JSON path, any parts of the path that do not exist will be // constructed, and if a collision occurs with a non object type whilst iterating the path an error
constructed, and if a collision occurs with a non object type whilst iterating the path an error is // is returned.
returned.
*/
func (g *Container) Set(value interface{}, path ...string) (*Container, error) { func (g *Container) Set(value interface{}, path ...string) (*Container, error) {
if len(path) == 0 { if len(path) == 0 {
g.object = value g.object = value
@ -229,16 +202,12 @@ func (g *Container) Set(value interface{}, path ...string) (*Container, error) {
return &Container{object}, nil return &Container{object}, nil
} }
/* // SetP - Does the same as Set, but using a dot notation JSON path.
SetP - Does the same as Set, but using a dot notation JSON path.
*/
func (g *Container) SetP(value interface{}, path string) (*Container, error) { func (g *Container) SetP(value interface{}, path string) (*Container, error) {
return g.Set(value, strings.Split(path, ".")...) return g.Set(value, strings.Split(path, ".")...)
} }
/* // SetIndex - Set a value of an array element based on the index.
SetIndex - Set a value of an array element based on the index.
*/
func (g *Container) SetIndex(value interface{}, index int) (*Container, error) { func (g *Container) SetIndex(value interface{}, index int) (*Container, error) {
if array, ok := g.Data().([]interface{}); ok { if array, ok := g.Data().([]interface{}); ok {
if index >= len(array) { if index >= len(array) {
@ -250,80 +219,60 @@ func (g *Container) SetIndex(value interface{}, index int) (*Container, error) {
return &Container{nil}, ErrNotArray return &Container{nil}, ErrNotArray
} }
/* // Object - Create a new JSON object at a path. Returns an error if the path contains a collision
Object - Create a new JSON object at a path. Returns an error if the path contains a collision with // with a non object type.
a non object type.
*/
func (g *Container) Object(path ...string) (*Container, error) { func (g *Container) Object(path ...string) (*Container, error) {
return g.Set(map[string]interface{}{}, path...) return g.Set(map[string]interface{}{}, path...)
} }
/* // ObjectP - Does the same as Object, but using a dot notation JSON path.
ObjectP - Does the same as Object, but using a dot notation JSON path.
*/
func (g *Container) ObjectP(path string) (*Container, error) { func (g *Container) ObjectP(path string) (*Container, error) {
return g.Object(strings.Split(path, ".")...) return g.Object(strings.Split(path, ".")...)
} }
/* // ObjectI - Create a new JSON object at an array index. Returns an error if the object is not an
ObjectI - Create a new JSON object at an array index. Returns an error if the object is not an array // array or the index is out of bounds.
or the index is out of bounds.
*/
func (g *Container) ObjectI(index int) (*Container, error) { func (g *Container) ObjectI(index int) (*Container, error) {
return g.SetIndex(map[string]interface{}{}, index) return g.SetIndex(map[string]interface{}{}, index)
} }
/* // Array - Create a new JSON array at a path. Returns an error if the path contains a collision with
Array - Create a new JSON array at a path. Returns an error if the path contains a collision with // a non object type.
a non object type.
*/
func (g *Container) Array(path ...string) (*Container, error) { func (g *Container) Array(path ...string) (*Container, error) {
return g.Set([]interface{}{}, path...) return g.Set([]interface{}{}, path...)
} }
/* // ArrayP - Does the same as Array, but using a dot notation JSON path.
ArrayP - Does the same as Array, but using a dot notation JSON path.
*/
func (g *Container) ArrayP(path string) (*Container, error) { func (g *Container) ArrayP(path string) (*Container, error) {
return g.Array(strings.Split(path, ".")...) return g.Array(strings.Split(path, ".")...)
} }
/* // ArrayI - Create a new JSON array at an array index. Returns an error if the object is not an
ArrayI - Create a new JSON array at an array index. Returns an error if the object is not an array // array or the index is out of bounds.
or the index is out of bounds.
*/
func (g *Container) ArrayI(index int) (*Container, error) { func (g *Container) ArrayI(index int) (*Container, error) {
return g.SetIndex([]interface{}{}, index) return g.SetIndex([]interface{}{}, index)
} }
/* // ArrayOfSize - Create a new JSON array of a particular size at a path. Returns an error if the
ArrayOfSize - Create a new JSON array of a particular size at a path. Returns an error if the path // path contains a collision with a non object type.
contains a collision with a non object type.
*/
func (g *Container) ArrayOfSize(size int, path ...string) (*Container, error) { func (g *Container) ArrayOfSize(size int, path ...string) (*Container, error) {
a := make([]interface{}, size) a := make([]interface{}, size)
return g.Set(a, path...) return g.Set(a, path...)
} }
/* // ArrayOfSizeP - Does the same as ArrayOfSize, but using a dot notation JSON path.
ArrayOfSizeP - Does the same as ArrayOfSize, but using a dot notation JSON path.
*/
func (g *Container) ArrayOfSizeP(size int, path string) (*Container, error) { func (g *Container) ArrayOfSizeP(size int, path string) (*Container, error) {
return g.ArrayOfSize(size, strings.Split(path, ".")...) return g.ArrayOfSize(size, strings.Split(path, ".")...)
} }
/* // ArrayOfSizeI - Create a new JSON array of a particular size at an array index. Returns an error
ArrayOfSizeI - Create a new JSON array of a particular size at an array index. Returns an error if // if the object is not an array or the index is out of bounds.
the object is not an array or the index is out of bounds.
*/
func (g *Container) ArrayOfSizeI(size, index int) (*Container, error) { func (g *Container) ArrayOfSizeI(size, index int) (*Container, error) {
a := make([]interface{}, size) a := make([]interface{}, size)
return g.SetIndex(a, index) return g.SetIndex(a, index)
} }
/* // Delete - Delete an element at a JSON path, an error is returned if the element does not exist.
Delete - Delete an element at a JSON path, an error is returned if the element does not exist.
*/
func (g *Container) Delete(path ...string) error { func (g *Container) Delete(path ...string) error {
var object interface{} var object interface{}
@ -346,24 +295,19 @@ func (g *Container) Delete(path ...string) error {
return nil return nil
} }
/* // DeleteP - Does the same as Delete, but using a dot notation JSON path.
DeleteP - Does the same as Delete, but using a dot notation JSON path.
*/
func (g *Container) DeleteP(path string) error { func (g *Container) DeleteP(path string) error {
return g.Delete(strings.Split(path, ".")...) return g.Delete(strings.Split(path, ".")...)
} }
/*--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
*/
/* /*
Array modification/search - Keeping these options simple right now, no need for anything more Array modification/search - Keeping these options simple right now, no need for anything more
complicated since you can just cast to []interface{}, modify and then reassign with Set. complicated since you can just cast to []interface{}, modify and then reassign with Set.
*/ */
/* // ArrayAppend - Append a value onto a JSON array.
ArrayAppend - Append a value onto a JSON array.
*/
func (g *Container) ArrayAppend(value interface{}, path ...string) error { func (g *Container) ArrayAppend(value interface{}, path ...string) error {
array, ok := g.Search(path...).Data().([]interface{}) array, ok := g.Search(path...).Data().([]interface{})
if !ok { if !ok {
@ -374,16 +318,12 @@ func (g *Container) ArrayAppend(value interface{}, path ...string) error {
return err return err
} }
/* // ArrayAppendP - Append a value onto a JSON array using a dot notation JSON path.
ArrayAppendP - Append a value onto a JSON array using a dot notation JSON path.
*/
func (g *Container) ArrayAppendP(value interface{}, path string) error { func (g *Container) ArrayAppendP(value interface{}, path string) error {
return g.ArrayAppend(value, strings.Split(path, ".")...) return g.ArrayAppend(value, strings.Split(path, ".")...)
} }
/* // ArrayRemove - Remove an element from a JSON array.
ArrayRemove - Remove an element from a JSON array.
*/
func (g *Container) ArrayRemove(index int, path ...string) error { func (g *Container) ArrayRemove(index int, path ...string) error {
if index < 0 { if index < 0 {
return ErrOutOfBounds return ErrOutOfBounds
@ -401,16 +341,12 @@ func (g *Container) ArrayRemove(index int, path ...string) error {
return err return err
} }
/* // ArrayRemoveP - Remove an element from a JSON array using a dot notation JSON path.
ArrayRemoveP - Remove an element from a JSON array using a dot notation JSON path.
*/
func (g *Container) ArrayRemoveP(index int, path string) error { func (g *Container) ArrayRemoveP(index int, path string) error {
return g.ArrayRemove(index, strings.Split(path, ".")...) return g.ArrayRemove(index, strings.Split(path, ".")...)
} }
/* // ArrayElement - Access an element from a JSON array.
ArrayElement - Access an element from a JSON array.
*/
func (g *Container) ArrayElement(index int, path ...string) (*Container, error) { func (g *Container) ArrayElement(index int, path ...string) (*Container, error) {
if index < 0 { if index < 0 {
return &Container{nil}, ErrOutOfBounds return &Container{nil}, ErrOutOfBounds
@ -425,16 +361,12 @@ func (g *Container) ArrayElement(index int, path ...string) (*Container, error)
return &Container{nil}, ErrOutOfBounds return &Container{nil}, ErrOutOfBounds
} }
/* // ArrayElementP - Access an element from a JSON array using a dot notation JSON path.
ArrayElementP - Access an element from a JSON array using a dot notation JSON path.
*/
func (g *Container) ArrayElementP(index int, path string) (*Container, error) { func (g *Container) ArrayElementP(index int, path string) (*Container, error) {
return g.ArrayElement(index, strings.Split(path, ".")...) return g.ArrayElement(index, strings.Split(path, ".")...)
} }
/* // ArrayCount - Count the number of elements in a JSON array.
ArrayCount - Count the number of elements in a JSON array.
*/
func (g *Container) ArrayCount(path ...string) (int, error) { func (g *Container) ArrayCount(path ...string) (int, error) {
if array, ok := g.Search(path...).Data().([]interface{}); ok { if array, ok := g.Search(path...).Data().([]interface{}); ok {
return len(array), nil return len(array), nil
@ -442,19 +374,14 @@ func (g *Container) ArrayCount(path ...string) (int, error) {
return 0, ErrNotArray return 0, ErrNotArray
} }
/* // ArrayCountP - Count the number of elements in a JSON array using a dot notation JSON path.
ArrayCountP - Count the number of elements in a JSON array using a dot notation JSON path.
*/
func (g *Container) ArrayCountP(path string) (int, error) { func (g *Container) ArrayCountP(path string) (int, error) {
return g.ArrayCount(strings.Split(path, ".")...) return g.ArrayCount(strings.Split(path, ".")...)
} }
/*--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
*/
/* // Bytes - Converts the contained object back to a JSON []byte blob.
Bytes - Converts the contained object back to a JSON []byte blob.
*/
func (g *Container) Bytes() []byte { func (g *Container) Bytes() []byte {
if g.object != nil { if g.object != nil {
if bytes, err := json.Marshal(g.object); err == nil { if bytes, err := json.Marshal(g.object); err == nil {
@ -464,9 +391,7 @@ func (g *Container) Bytes() []byte {
return []byte("{}") return []byte("{}")
} }
/* // BytesIndent - Converts the contained object to a JSON []byte blob formatted with prefix, indent.
BytesIndent - Converts the contained object back to a JSON []byte blob formatted with prefix and indent.
*/
func (g *Container) BytesIndent(prefix string, indent string) []byte { func (g *Container) BytesIndent(prefix string, indent string) []byte {
if g.object != nil { if g.object != nil {
if bytes, err := json.MarshalIndent(g.object, prefix, indent); err == nil { if bytes, err := json.MarshalIndent(g.object, prefix, indent); err == nil {
@ -476,37 +401,27 @@ func (g *Container) BytesIndent(prefix string, indent string) []byte {
return []byte("{}") return []byte("{}")
} }
/* // String - Converts the contained object to a JSON formatted string.
String - Converts the contained object back to a JSON formatted string.
*/
func (g *Container) String() string { func (g *Container) String() string {
return string(g.Bytes()) return string(g.Bytes())
} }
/* // StringIndent - Converts the contained object back to a JSON formatted string with prefix, indent.
StringIndent - Converts the contained object back to a JSON formatted string with prefix and indent.
*/
func (g *Container) StringIndent(prefix string, indent string) string { func (g *Container) StringIndent(prefix string, indent string) string {
return string(g.BytesIndent(prefix, indent)) return string(g.BytesIndent(prefix, indent))
} }
/* // New - Create a new gabs JSON object.
New - Create a new gabs JSON object.
*/
func New() *Container { func New() *Container {
return &Container{map[string]interface{}{}} return &Container{map[string]interface{}{}}
} }
/* // Consume - Gobble up an already converted JSON object, or a fresh map[string]interface{} object.
Consume - Gobble up an already converted JSON object, or a fresh map[string]interface{} object.
*/
func Consume(root interface{}) (*Container, error) { func Consume(root interface{}) (*Container, error) {
return &Container{root}, nil return &Container{root}, nil
} }
/* // ParseJSON - Convert a string into a representation of the parsed JSON.
ParseJSON - Convert a string into a representation of the parsed JSON.
*/
func ParseJSON(sample []byte) (*Container, error) { func ParseJSON(sample []byte) (*Container, error) {
var gabs Container var gabs Container
@ -517,9 +432,7 @@ func ParseJSON(sample []byte) (*Container, error) {
return &gabs, nil return &gabs, nil
} }
/* // ParseJSONDecoder - Convert a json.Decoder into a representation of the parsed JSON.
ParseJSONDecoder - Convert a json.Decoder into a representation of the parsed JSON.
*/
func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) { func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) {
var gabs Container var gabs Container
@ -530,9 +443,7 @@ func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) {
return &gabs, nil return &gabs, nil
} }
/* // ParseJSONFile - Read a file and convert into a representation of the parsed JSON.
ParseJSONFile - Read a file and convert into a representation of the parsed JSON.
*/
func ParseJSONFile(path string) (*Container, error) { func ParseJSONFile(path string) (*Container, error) {
if len(path) > 0 { if len(path) > 0 {
cBytes, err := ioutil.ReadFile(path) cBytes, err := ioutil.ReadFile(path)
@ -550,9 +461,7 @@ func ParseJSONFile(path string) (*Container, error) {
return nil, ErrInvalidPath return nil, ErrInvalidPath
} }
/* // ParseJSONBuffer - Read the contents of a buffer into a representation of the parsed JSON.
ParseJSONBuffer - Read the contents of a buffer into a representation of the parsed JSON.
*/
func ParseJSONBuffer(buffer io.Reader) (*Container, error) { func ParseJSONBuffer(buffer io.Reader) (*Container, error) {
var gabs Container var gabs Container
jsonDecoder := json.NewDecoder(buffer) jsonDecoder := json.NewDecoder(buffer)
@ -563,83 +472,4 @@ func ParseJSONBuffer(buffer io.Reader) (*Container, error) {
return &gabs, nil return &gabs, nil
} }
/*--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
*/
// DEPRECATED METHODS
/*
Push - DEPRECATED: Push a value onto a JSON array.
*/
func (g *Container) Push(target string, value interface{}) error {
if mmap, ok := g.Data().(map[string]interface{}); ok {
arrayTarget := mmap[target]
if array, ok := arrayTarget.([]interface{}); ok {
mmap[target] = append(array, value)
} else {
return ErrNotArray
}
} else {
return ErrNotObj
}
return nil
}
/*
RemoveElement - DEPRECATED: Remove a value from a JSON array.
*/
func (g *Container) RemoveElement(target string, index int) error {
if index < 0 {
return ErrOutOfBounds
}
if mmap, ok := g.Data().(map[string]interface{}); ok {
arrayTarget := mmap[target]
if array, ok := arrayTarget.([]interface{}); ok {
if index < len(array) {
mmap[target] = append(array[:index], array[index+1:]...)
} else {
return ErrOutOfBounds
}
} else {
return ErrNotArray
}
} else {
return ErrNotObj
}
return nil
}
/*
GetElement - DEPRECATED: Get the desired element from a JSON array
*/
func (g *Container) GetElement(target string, index int) *Container {
if index < 0 {
return &Container{nil}
}
if mmap, ok := g.Data().(map[string]interface{}); ok {
arrayTarget := mmap[target]
if array, ok := arrayTarget.([]interface{}); ok {
if index < len(array) {
return &Container{array[index]}
}
}
}
return &Container{nil}
}
/*
CountElements - DEPRECATED: Count the elements of a JSON array, returns -1 if the target is not an
array
*/
func (g *Container) CountElements(target string) int {
if mmap, ok := g.Data().(map[string]interface{}); ok {
arrayTarget := mmap[target]
if array, ok := arrayTarget.([]interface{}); ok {
return len(array)
}
}
return -1
}
/*---------------------------------------------------------------------------------------------------
*/

View File

@ -28,39 +28,42 @@ Examples
Here is an example of using the package: Here is an example of using the package:
func SlowMethod() { ```go
// Profiling the runtime of a method func SlowMethod() {
defer metrics.MeasureSince([]string{"SlowMethod"}, time.Now()) // Profiling the runtime of a method
} defer metrics.MeasureSince([]string{"SlowMethod"}, time.Now())
}
// Configure a statsite sink as the global metrics sink // Configure a statsite sink as the global metrics sink
sink, _ := metrics.NewStatsiteSink("statsite:8125") sink, _ := metrics.NewStatsiteSink("statsite:8125")
metrics.NewGlobal(metrics.DefaultConfig("service-name"), sink) metrics.NewGlobal(metrics.DefaultConfig("service-name"), sink)
// Emit a Key/Value pair
metrics.EmitKey([]string{"questions", "meaning of life"}, 42)
// Emit a Key/Value pair
metrics.EmitKey([]string{"questions", "meaning of life"}, 42)
```
Here is an example of setting up an signal handler: Here is an example of setting up an signal handler:
// Setup the inmem sink and signal handler ```go
inm := metrics.NewInmemSink(10*time.Second, time.Minute) // Setup the inmem sink and signal handler
sig := metrics.DefaultInmemSignal(inm) inm := metrics.NewInmemSink(10*time.Second, time.Minute)
metrics.NewGlobal(metrics.DefaultConfig("service-name"), inm) sig := metrics.DefaultInmemSignal(inm)
metrics.NewGlobal(metrics.DefaultConfig("service-name"), inm)
// Run some code // Run some code
inm.SetGauge([]string{"foo"}, 42) inm.SetGauge([]string{"foo"}, 42)
inm.EmitKey([]string{"bar"}, 30) inm.EmitKey([]string{"bar"}, 30)
inm.IncrCounter([]string{"baz"}, 42) inm.IncrCounter([]string{"baz"}, 42)
inm.IncrCounter([]string{"baz"}, 1) inm.IncrCounter([]string{"baz"}, 1)
inm.IncrCounter([]string{"baz"}, 80) inm.IncrCounter([]string{"baz"}, 80)
inm.AddSample([]string{"method", "wow"}, 42) inm.AddSample([]string{"method", "wow"}, 42)
inm.AddSample([]string{"method", "wow"}, 100) inm.AddSample([]string{"method", "wow"}, 100)
inm.AddSample([]string{"method", "wow"}, 22) inm.AddSample([]string{"method", "wow"}, 22)
.... ....
```
When a signal comes in, output like the following will be dumped to stderr: When a signal comes in, output like the following will be dumped to stderr:

View File

@ -182,6 +182,19 @@ type Config struct {
// the delay of a request see the aws/client.DefaultRetryer and // the delay of a request see the aws/client.DefaultRetryer and
// aws/request.Retryer. // aws/request.Retryer.
SleepDelay func(time.Duration) SleepDelay func(time.Duration)
// DisableRestProtocolURICleaning will not clean the URL path when making rest protocol requests.
// Will default to false. This would only be used for empty directory names in s3 requests.
//
// Example:
// sess, err := session.NewSession(&aws.Config{DisableRestProtocolURICleaning: aws.Bool(true))
//
// svc := s3.New(sess)
// out, err := svc.GetObject(&s3.GetObjectInput {
// Bucket: aws.String("bucketname"),
// Key: aws.String("//foo//bar//moo"),
// })
DisableRestProtocolURICleaning *bool
} }
// NewConfig returns a new Config pointer that can be chained with builder // NewConfig returns a new Config pointer that can be chained with builder
@ -403,6 +416,10 @@ func mergeInConfig(dst *Config, other *Config) {
if other.SleepDelay != nil { if other.SleepDelay != nil {
dst.SleepDelay = other.SleepDelay dst.SleepDelay = other.SleepDelay
} }
if other.DisableRestProtocolURICleaning != nil {
dst.DisableRestProtocolURICleaning = other.DisableRestProtocolURICleaning
}
} }
// Copy will return a shallow copy of the Config object. If any additional // Copy will return a shallow copy of the Config object. If any additional

View File

@ -111,7 +111,7 @@ func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
}, nil }, nil
} }
// A ec2RoleCredRespBody provides the shape for unmarshalling credential // A ec2RoleCredRespBody provides the shape for unmarshaling credential
// request responses. // request responses.
type ec2RoleCredRespBody struct { type ec2RoleCredRespBody struct {
// Success State // Success State

View File

@ -133,7 +133,7 @@ func (c *EC2Metadata) Available() bool {
return true return true
} }
// An EC2IAMInfo provides the shape for unmarshalling // An EC2IAMInfo provides the shape for unmarshaling
// an IAM info from the metadata API // an IAM info from the metadata API
type EC2IAMInfo struct { type EC2IAMInfo struct {
Code string Code string
@ -142,7 +142,7 @@ type EC2IAMInfo struct {
InstanceProfileID string InstanceProfileID string
} }
// An EC2InstanceIdentityDocument provides the shape for unmarshalling // An EC2InstanceIdentityDocument provides the shape for unmarshaling
// an instance identity document // an instance identity document
type EC2InstanceIdentityDocument struct { type EC2InstanceIdentityDocument struct {
DevpayProductCodes []string `json:"devpayProductCodes"` DevpayProductCodes []string `json:"devpayProductCodes"`

View File

@ -567,7 +567,11 @@ func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) {
} }
func (ctx *signingCtx) buildCanonicalString() { func (ctx *signingCtx) buildCanonicalString() {
ctx.Request.URL.RawQuery = strings.Replace(ctx.Query.Encode(), "+", "%20", -1) query := ctx.Query
for key := range query {
sort.Strings(query[key])
}
ctx.Request.URL.RawQuery = strings.Replace(query.Encode(), "+", "%20", -1)
uri := getURIPath(ctx.Request.URL) uri := getURIPath(ctx.Request.URL)

View File

@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go" const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK // SDKVersion is the version of this SDK
const SDKVersion = "1.5.0" const SDKVersion = "1.5.6"

View File

@ -14,6 +14,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
) )
@ -92,7 +93,7 @@ func buildLocationElements(r *request.Request, v reflect.Value) {
} }
r.HTTPRequest.URL.RawQuery = query.Encode() r.HTTPRequest.URL.RawQuery = query.Encode()
updatePath(r.HTTPRequest.URL, r.HTTPRequest.URL.Path) updatePath(r.HTTPRequest.URL, r.HTTPRequest.URL.Path, aws.BoolValue(r.Config.DisableRestProtocolURICleaning))
} }
func buildBody(r *request.Request, v reflect.Value) { func buildBody(r *request.Request, v reflect.Value) {
@ -193,13 +194,15 @@ func buildQueryString(query url.Values, v reflect.Value, name string) error {
return nil return nil
} }
func updatePath(url *url.URL, urlPath string) { func updatePath(url *url.URL, urlPath string, disableRestProtocolURICleaning bool) {
scheme, query := url.Scheme, url.RawQuery scheme, query := url.Scheme, url.RawQuery
hasSlash := strings.HasSuffix(urlPath, "/") hasSlash := strings.HasSuffix(urlPath, "/")
// clean up path // clean up path
urlPath = path.Clean(urlPath) if !disableRestProtocolURICleaning {
urlPath = path.Clean(urlPath)
}
if hasSlash && !strings.HasSuffix(urlPath, "/") { if hasSlash && !strings.HasSuffix(urlPath, "/") {
urlPath += "/" urlPath += "/"
} }

View File

@ -38,6 +38,7 @@ Lucas Liu <extrafliu at gmail.com>
Luke Scott <luke at webconnex.com> Luke Scott <luke at webconnex.com>
Michael Woolnough <michael.woolnough at gmail.com> Michael Woolnough <michael.woolnough at gmail.com>
Nicola Peduzzi <thenikso at gmail.com> Nicola Peduzzi <thenikso at gmail.com>
Olivier Mengué <dolmen at cpan.org>
Paul Bonser <misterpib at gmail.com> Paul Bonser <misterpib at gmail.com>
Runrioter Wung <runrioter at gmail.com> Runrioter Wung <runrioter at gmail.com>
Soroush Pour <me at soroushjp.com> Soroush Pour <me at soroushjp.com>

View File

@ -315,13 +315,21 @@ I/O write timeout. The value must be a decimal number with an unit suffix ( *"ms
##### System Variables ##### System Variables
All other parameters are interpreted as system variables: Any other parameters are interpreted as system variables:
* `autocommit`: `"SET autocommit=<value>"` * `<boolean_var>=<value>`: `SET <boolean_var>=<value>`
* [`time_zone`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `"SET time_zone=<value>"` * `<enum_var>=<value>`: `SET <enum_var>=<value>`
* [`tx_isolation`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `"SET tx_isolation=<value>"` * `<string_var>=%27<value>%27`: `SET <string_var>='<value>'`
* `param`: `"SET <param>=<value>"`
Rules:
* The values for string variables must be quoted with '
* The values must also be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!
(which implies values of string variables must be wrapped with `%27`)
Examples:
* `autocommit=1`: `SET autocommit=1`
* [`time_zone=%27Europe%2FParis%27`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `SET time_zone='Europe/Paris'`
* [`tx_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `SET tx_isolation='REPEATABLE-READ'`
*The values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!*
#### Examples #### Examples
``` ```

View File

@ -25,9 +25,9 @@ import (
// Read packet to buffer 'data' // Read packet to buffer 'data'
func (mc *mysqlConn) readPacket() ([]byte, error) { func (mc *mysqlConn) readPacket() ([]byte, error) {
var payload []byte var prevData []byte
for { for {
// Read packet header // read packet header
data, err := mc.buf.readNext(4) data, err := mc.buf.readNext(4)
if err != nil { if err != nil {
errLog.Print(err) errLog.Print(err)
@ -35,16 +35,10 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
// Packet Length [24 bit] // packet length [24 bit]
pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16) pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16)
if pktLen < 1 { // check packet sync [8 bit]
errLog.Print(ErrMalformPkt)
mc.Close()
return nil, driver.ErrBadConn
}
// Check Packet Sync [8 bit]
if data[3] != mc.sequence { if data[3] != mc.sequence {
if data[3] > mc.sequence { if data[3] > mc.sequence {
return nil, ErrPktSyncMul return nil, ErrPktSyncMul
@ -53,7 +47,20 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
} }
mc.sequence++ mc.sequence++
// Read packet body [pktLen bytes] // packets with length 0 terminate a previous packet which is a
// multiple of (2^24)1 bytes long
if pktLen == 0 {
// there was no previous packet
if prevData == nil {
errLog.Print(ErrMalformPkt)
mc.Close()
return nil, driver.ErrBadConn
}
return prevData, nil
}
// read packet body [pktLen bytes]
data, err = mc.buf.readNext(pktLen) data, err = mc.buf.readNext(pktLen)
if err != nil { if err != nil {
errLog.Print(err) errLog.Print(err)
@ -61,18 +68,17 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
return nil, driver.ErrBadConn return nil, driver.ErrBadConn
} }
isLastPacket := (pktLen < maxPacketSize) // return data if this was the last packet
if pktLen < maxPacketSize {
// zero allocations for non-split packets
if prevData == nil {
return data, nil
}
// Zero allocations for non-splitting packets return append(prevData, data...), nil
if isLastPacket && payload == nil {
return data, nil
} }
payload = append(payload, data...) prevData = append(prevData, data...)
if isLastPacket {
return payload, nil
}
} }
} }
@ -700,11 +706,15 @@ func (rows *textRows) readRow(dest []driver.Value) error {
if data[0] == iEOF && len(data) == 5 { if data[0] == iEOF && len(data) == 5 {
// server_status [2 bytes] // server_status [2 bytes]
rows.mc.status = readStatus(data[3:]) rows.mc.status = readStatus(data[3:])
if err := rows.mc.discardResults(); err != nil { err = rows.mc.discardResults()
return err if err == nil {
err = io.EOF
} else {
// connection unusable
rows.mc.Close()
} }
rows.mc = nil rows.mc = nil
return io.EOF return err
} }
if data[0] == iERR { if data[0] == iERR {
rows.mc = nil rows.mc = nil
@ -1105,11 +1115,15 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
// EOF Packet // EOF Packet
if data[0] == iEOF && len(data) == 5 { if data[0] == iEOF && len(data) == 5 {
rows.mc.status = readStatus(data[3:]) rows.mc.status = readStatus(data[3:])
if err := rows.mc.discardResults(); err != nil { err = rows.mc.discardResults()
return err if err == nil {
err = io.EOF
} else {
// connection unusable
rows.mc.Close()
} }
rows.mc = nil rows.mc = nil
return io.EOF return err
} }
rows.mc = nil rows.mc = nil

View File

@ -24,7 +24,10 @@ type mysqlStmt struct {
func (stmt *mysqlStmt) Close() error { func (stmt *mysqlStmt) Close() error {
if stmt.mc == nil || stmt.mc.netConn == nil { if stmt.mc == nil || stmt.mc.netConn == nil {
errLog.Print(ErrInvalidConn) // driver.Stmt.Close can be called more than once, thus this function
// has to be idempotent.
// See also Issue #450 and golang/go#16019.
//errLog.Print(ErrInvalidConn)
return driver.ErrBadConn return driver.ErrBadConn
} }

View File

@ -82,3 +82,4 @@ Robert Nix <robert@nicerobot.org>
Nathan Youngman <git@nathany.com> Nathan Youngman <git@nathany.com>
Charles Law <charles.law@gmail.com>; <claw@conduce.com> Charles Law <charles.law@gmail.com>; <claw@conduce.com>
Nathan Davies <nathanjamesdavies@gmail.com> Nathan Davies <nathanjamesdavies@gmail.com>
Bo Blanton <bo.blanton@gmail.com>

26
vendor/github.com/gocql/gocql/address_translators.go generated vendored Normal file
View File

@ -0,0 +1,26 @@
package gocql
import "net"
// AddressTranslator provides a way to translate node addresses (and ports) that are
// discovered or received as a node event. This can be useful in an ec2 environment,
// for instance, to translate public IPs to private IPs.
type AddressTranslator interface {
// Translate will translate the provided address and/or port to another
// address and/or port. If no translation is possible, Translate will return the
// address and port provided to it.
Translate(addr net.IP, port int) (net.IP, int)
}
type AddressTranslatorFunc func(addr net.IP, port int) (net.IP, int)
func (fn AddressTranslatorFunc) Translate(addr net.IP, port int) (net.IP, int) {
return fn(addr, port)
}
// IdentityTranslator will do nothing but return what it was provided. It is essentially a no-op.
func IdentityTranslator() AddressTranslator {
return AddressTranslatorFunc(func(addr net.IP, port int) (net.IP, int) {
return addr, port
})
}

View File

@ -6,6 +6,8 @@ package gocql
import ( import (
"errors" "errors"
"log"
"net"
"time" "time"
) )
@ -75,6 +77,10 @@ type ClusterConfig struct {
// via Discovery // via Discovery
HostFilter HostFilter HostFilter HostFilter
// AddressTranslator will translate addresses found on peer discovery and/or
// node change events.
AddressTranslator AddressTranslator
// If IgnorePeerAddr is true and the address in system.peers does not match // If IgnorePeerAddr is true and the address in system.peers does not match
// the supplied host by either initial hosts or discovered via events then the // the supplied host by either initial hosts or discovered via events then the
// host will be replaced with the supplied address. // host will be replaced with the supplied address.
@ -146,6 +152,21 @@ func (cfg *ClusterConfig) CreateSession() (*Session, error) {
return NewSession(*cfg) return NewSession(*cfg)
} }
// translateAddressPort is a helper method that will use the given AddressTranslator
// if defined, to translate the given address and port into a possibly new address
// and port, If no AddressTranslator or if an error occurs, the given address and
// port will be returned.
func (cfg *ClusterConfig) translateAddressPort(addr net.IP, port int) (net.IP, int) {
if cfg.AddressTranslator == nil || len(addr) == 0 {
return addr, port
}
newAddr, newPort := cfg.AddressTranslator.Translate(addr, port)
if gocqlDebug {
log.Printf("gocql: translating address '%v:%d' to '%v:%d'", addr, port, newAddr, newPort)
}
return newAddr, newPort
}
var ( var (
ErrNoHosts = errors.New("no hosts provided") ErrNoHosts = errors.New("no hosts provided")
ErrNoConnectionsStarted = errors.New("no connections were made when creating the session") ErrNoConnectionsStarted = errors.New("no connections were made when creating the session")

View File

@ -172,7 +172,9 @@ func Connect(host *HostInfo, cfg *ConnConfig, errorHandler ConnErrorHandler, ses
} }
// TODO(zariel): handle ipv6 zone // TODO(zariel): handle ipv6 zone
addr := (&net.TCPAddr{IP: host.Peer(), Port: host.Port()}).String() translatedPeer, translatedPort := session.cfg.translateAddressPort(host.Peer(), host.Port())
addr := (&net.TCPAddr{IP: translatedPeer, Port: translatedPort}).String()
//addr := (&net.TCPAddr{IP: host.Peer(), Port: host.Port()}).String()
if cfg.tlsConfig != nil { if cfg.tlsConfig != nil {
// the TLS config is safe to be reused by connections but it must not // the TLS config is safe to be reused by connections but it must not

View File

@ -318,8 +318,8 @@ func (c *controlConn) reconnect(refreshring bool) {
} }
} }
// TODO: should have our own roundrobbin for hosts so that we can try each // TODO: should have our own round-robin for hosts so that we can try each
// in succession and guantee that we get a different host each time. // in succession and guarantee that we get a different host each time.
if newConn == nil { if newConn == nil {
host := c.session.ring.rrHost() host := c.session.ring.rrHost()
if host == nil { if host == nil {

View File

@ -205,7 +205,6 @@ func (s *Session) handleNewNode(ip net.IP, port int, waitForBinary bool) {
s.pool.addHost(hostInfo) s.pool.addHost(hostInfo)
s.policy.AddHost(hostInfo) s.policy.AddHost(hostInfo)
hostInfo.setState(NodeUp) hostInfo.setState(NodeUp)
if s.control != nil && !s.cfg.IgnorePeerAddr { if s.control != nil && !s.cfg.IgnorePeerAddr {
s.hostSource.refreshRing() s.hostSource.refreshRing()
} }

View File

@ -64,7 +64,7 @@ function run_tests() {
local args="-gocql.timeout=60s -runssl -proto=$proto -rf=3 -clusterSize=$clusterSize -autowait=2000ms -compressor=snappy -gocql.cversion=$version -cluster=$(ccm liveset) ./..." local args="-gocql.timeout=60s -runssl -proto=$proto -rf=3 -clusterSize=$clusterSize -autowait=2000ms -compressor=snappy -gocql.cversion=$version -cluster=$(ccm liveset) ./..."
go test -v -tags unit go test -v -tags unit
if [ "$auth" = true ] if [ "$auth" = true ]
then then

View File

@ -204,14 +204,21 @@ func (t *tokenRing) GetHostForToken(token token) *HostInfo {
return nil return nil
} }
l := len(t.tokens)
// no host tokens, no available hosts
if l == 0 {
return nil
}
// find the primary replica // find the primary replica
ringIndex := sort.Search( ringIndex := sort.Search(
len(t.tokens), l,
func(i int) bool { func(i int) bool {
return !t.tokens[i].Less(token) return !t.tokens[i].Less(token)
}, },
) )
if ringIndex == len(t.tokens) {
if ringIndex == l {
// wrap around to the first in the ring // wrap around to the first in the ring
ringIndex = 0 ringIndex = 0
} }

View File

@ -154,6 +154,7 @@ type ExtensionDesc struct {
Field int32 // field number Field int32 // field number
Name string // fully-qualified name of extension, for text formatting Name string // fully-qualified name of extension, for text formatting
Tag string // protobuf tag style Tag string // protobuf tag style
Filename string // name of the file in which the extension is defined
} }
func (ed *ExtensionDesc) repeated() bool { func (ed *ExtensionDesc) repeated() bool {

View File

@ -592,7 +592,11 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
props = oop.Prop props = oop.Prop
nv := reflect.New(oop.Type.Elem()) nv := reflect.New(oop.Type.Elem())
dst = nv.Elem().Field(0) dst = nv.Elem().Field(0)
sv.Field(oop.Field).Set(nv) field := sv.Field(oop.Field)
if !field.IsNil() {
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name)
}
field.Set(nv)
} }
if !dst.IsValid() { if !dst.IsValid() {
return p.errorf("unknown field name %q in %v", name, st) return p.errorf("unknown field name %q in %v", name, st)

View File

@ -55,10 +55,14 @@ func (e *Event) Payload() (payload interface{}) {
payload = &IssueCommentEvent{} payload = &IssueCommentEvent{}
case "IssuesEvent": case "IssuesEvent":
payload = &IssuesEvent{} payload = &IssuesEvent{}
case "LabelEvent":
payload = &LabelEvent{}
case "MemberEvent": case "MemberEvent":
payload = &MemberEvent{} payload = &MemberEvent{}
case "MembershipEvent": case "MembershipEvent":
payload = &MembershipEvent{} payload = &MembershipEvent{}
case "MilestoneEvent":
payload = &MilestoneEvent{}
case "PageBuildEvent": case "PageBuildEvent":
payload = &PageBuildEvent{} payload = &PageBuildEvent{}
case "PublicEvent": case "PublicEvent":

View File

@ -346,9 +346,6 @@ func (s *AuthorizationsService) ListGrants() ([]*Grant, *Response, error) {
return nil, nil, err return nil, nil, err
} }
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview)
grants := []*Grant{} grants := []*Grant{}
resp, err := s.client.Do(req, &grants) resp, err := s.client.Do(req, &grants)
if err != nil { if err != nil {
@ -368,9 +365,6 @@ func (s *AuthorizationsService) GetGrant(id int) (*Grant, *Response, error) {
return nil, nil, err return nil, nil, err
} }
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview)
grant := new(Grant) grant := new(Grant)
resp, err := s.client.Do(req, grant) resp, err := s.client.Do(req, grant)
if err != nil { if err != nil {
@ -392,9 +386,6 @@ func (s *AuthorizationsService) DeleteGrant(id int) (*Response, error) {
return nil, err return nil, err
} }
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview)
return s.client.Do(req, nil) return s.client.Do(req, nil)
} }

View File

@ -209,6 +209,22 @@ type IssuesEvent struct {
Sender *User `json:"sender,omitempty"` Sender *User `json:"sender,omitempty"`
} }
// LabelEvent is triggered when a repository's label is created, edited, or deleted.
// The Webhook event name is "label"
//
// GitHub docs: https://developer.github.com/v3/activity/events/types/#labelevent
type LabelEvent struct {
// Action is the action that was performed. Possible values are:
// "created", "edited", "deleted"
Action *string `json:"action,omitempty"`
Label *Label `json:"label,omitempty"`
// The following fields are only populated by Webhook events.
Changes *EditChange `json:"changes,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Org *Organization `json:"organization,omitempty"`
}
// MemberEvent is triggered when a user is added as a collaborator to a repository. // MemberEvent is triggered when a user is added as a collaborator to a repository.
// The Webhook event name is "member". // The Webhook event name is "member".
// //
@ -243,6 +259,23 @@ type MembershipEvent struct {
Sender *User `json:"sender,omitempty"` Sender *User `json:"sender,omitempty"`
} }
// MilestoneEvent is triggered when a milestone is created, closed, opened, edited, or deleted.
// The Webhook event name is "milestone".
//
// Github docs: https://developer.github.com/v3/activity/events/types/#milestoneevent
type MilestoneEvent struct {
// Action is the action that was performed. Possible values are:
// "created", "closed", "opened", "edited", "deleted"
Action *string `json:"action,omitempty"`
Milestone *Milestone `json:"milestone,omitempty"`
// The following fields are only populated by Webhook events.
Changes *EditChange `json:"changes,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Org *Organization `json:"organization,omitempty"`
}
// PageBuildEvent represents an attempted build of a GitHub Pages site, whether // PageBuildEvent represents an attempted build of a GitHub Pages site, whether
// successful or not. // successful or not.
// The Webhook event name is "page_build". // The Webhook event name is "page_build".

View File

@ -68,6 +68,7 @@ const (
mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview" mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview"
// https://developer.github.com/changes/2016-04-01-squash-api-preview/ // https://developer.github.com/changes/2016-04-01-squash-api-preview/
// https://developer.github.com/changes/2016-09-26-pull-request-merge-api-update/
mediaTypeSquashPreview = "application/vnd.github.polaris-preview+json" mediaTypeSquashPreview = "application/vnd.github.polaris-preview+json"
// https://developer.github.com/changes/2016-04-04-git-signing-api-preview/ // https://developer.github.com/changes/2016-04-04-git-signing-api-preview/
@ -79,9 +80,6 @@ const (
// https://developer.github.com/changes/2016-06-14-repository-invitations/ // https://developer.github.com/changes/2016-06-14-repository-invitations/
mediaTypeRepositoryInvitationsPreview = "application/vnd.github.swamp-thing-preview+json" mediaTypeRepositoryInvitationsPreview = "application/vnd.github.swamp-thing-preview+json"
// https://developer.github.com/changes/2016-04-21-oauth-authorizations-grants-api-preview/
mediaTypeOAuthGrantAuthorizationsPreview = "application/vnd.github.damage-preview+json"
// https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/ // https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/
mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json" mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json"
@ -126,6 +124,7 @@ type Client struct {
Integrations *IntegrationsService Integrations *IntegrationsService
Issues *IssuesService Issues *IssuesService
Organizations *OrganizationsService Organizations *OrganizationsService
Projects *ProjectsService
PullRequests *PullRequestsService PullRequests *PullRequestsService
Repositories *RepositoriesService Repositories *RepositoriesService
Search *SearchService Search *SearchService
@ -199,6 +198,7 @@ func NewClient(httpClient *http.Client) *Client {
c.Licenses = (*LicensesService)(&c.common) c.Licenses = (*LicensesService)(&c.common)
c.Migrations = (*MigrationService)(&c.common) c.Migrations = (*MigrationService)(&c.common)
c.Organizations = (*OrganizationsService)(&c.common) c.Organizations = (*OrganizationsService)(&c.common)
c.Projects = (*ProjectsService)(&c.common)
c.PullRequests = (*PullRequestsService)(&c.common) c.PullRequests = (*PullRequestsService)(&c.common)
c.Reactions = (*ReactionsService)(&c.common) c.Reactions = (*ReactionsService)(&c.common)
c.Repositories = (*RepositoriesService)(&c.common) c.Repositories = (*RepositoriesService)(&c.common)

View File

@ -10,23 +10,44 @@ import "fmt"
// LicensesService handles communication with the license related // LicensesService handles communication with the license related
// methods of the GitHub API. // methods of the GitHub API.
// //
// GitHub API docs: http://developer.github.com/v3/pulls/ // GitHub API docs: https://developer.github.com/v3/licenses/
type LicensesService service type LicensesService service
// RepositoryLicense represents the license for a repository.
type RepositoryLicense struct {
Name *string `json:"name,omitempty"`
Path *string `json:"path,omitempty"`
SHA *string `json:"sha,omitempty"`
Size *int `json:"size,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
GitURL *string `json:"git_url,omitempty"`
DownloadURL *string `json:"download_url,omitempty"`
Type *string `json:"type,omitempty"`
Content *string `json:"content,omitempty"`
Encoding *string `json:"encoding,omitempty"`
License *License `json:"license,omitempty"`
}
func (l RepositoryLicense) String() string {
return Stringify(l)
}
// License represents an open source license. // License represents an open source license.
type License struct { type License struct {
Key *string `json:"key,omitempty"` Key *string `json:"key,omitempty"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
URL *string `json:"url,omitempty"` URL *string `json:"url,omitempty"`
SPDXID *string `json:"spdx_id,omitempty"`
HTMLURL *string `json:"html_url,omitempty"` HTMLURL *string `json:"html_url,omitempty"`
Featured *bool `json:"featured,omitempty"` Featured *bool `json:"featured,omitempty"`
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
Category *string `json:"category,omitempty"`
Implementation *string `json:"implementation,omitempty"` Implementation *string `json:"implementation,omitempty"`
Required *[]string `json:"required,omitempty"` Permissions *[]string `json:"permissions,omitempty"`
Permitted *[]string `json:"permitted,omitempty"` Conditions *[]string `json:"conditions,omitempty"`
Forbidden *[]string `json:"forbidden,omitempty"` Limitations *[]string `json:"limitations,omitempty"`
Body *string `json:"body,omitempty"` Body *string `json:"body,omitempty"`
} }

View File

@ -49,8 +49,10 @@ var (
"integration_installation_repositories": "IntegrationInstallationRepositoriesEvent", "integration_installation_repositories": "IntegrationInstallationRepositoriesEvent",
"issue_comment": "IssueCommentEvent", "issue_comment": "IssueCommentEvent",
"issues": "IssuesEvent", "issues": "IssuesEvent",
"label": "LabelEvent",
"member": "MemberEvent", "member": "MemberEvent",
"membership": "MembershipEvent", "membership": "MembershipEvent",
"milestone": "MilestoneEvent",
"page_build": "PageBuildEvent", "page_build": "PageBuildEvent",
"public": "PublicEvent", "public": "PublicEvent",
"pull_request_review_comment": "PullRequestReviewCommentEvent", "pull_request_review_comment": "PullRequestReviewCommentEvent",

417
vendor/github.com/google/go-github/github/projects.go generated vendored Normal file
View File

@ -0,0 +1,417 @@
// Copyright 2016 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import "fmt"
// ProjectsService provides access to the projects functions in the
// GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/projects/
type ProjectsService service
// Project represents a GitHub Project.
type Project struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
OwnerURL *string `json:"owner_url,omitempty"`
Name *string `json:"name,omitempty"`
Body *string `json:"body,omitempty"`
Number *int `json:"number,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
// The User object that generated the project.
Creator *User `json:"creator,omitempty"`
}
func (p Project) String() string {
return Stringify(p)
}
// GetProject gets a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/projects/#get-a-project
func (s *ProjectsService) GetProject(id int) (*Project, *Response, error) {
u := fmt.Sprintf("/projects/%v", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
project := &Project{}
resp, err := s.client.Do(req, project)
if err != nil {
return nil, resp, err
}
return project, resp, err
}
// ProjectOptions specifies the parameters to the
// RepositoriesService.CreateProject and
// ProjectsService.UpdateProject methods.
type ProjectOptions struct {
// The name of the project. (Required for creation; optional for update.)
Name string `json:"name,omitempty"`
// The body of the project. (Optional.)
Body string `json:"body,omitempty"`
}
// UpdateProject updates a repository project.
//
// GitHub API docs: https://developer.github.com/v3/projects/#update-a-project
func (s *ProjectsService) UpdateProject(id int, opt *ProjectOptions) (*Project, *Response, error) {
u := fmt.Sprintf("/projects/%v", id)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
project := &Project{}
resp, err := s.client.Do(req, project)
if err != nil {
return nil, resp, err
}
return project, resp, err
}
// DeleteProject deletes a GitHub Project from a repository.
//
// GitHub API docs: https://developer.github.com/v3/projects/#delete-a-project
func (s *ProjectsService) DeleteProject(id int) (*Response, error) {
u := fmt.Sprintf("/projects/%v", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(req, nil)
}
// ProjectColumn represents a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/
type ProjectColumn struct {
ID *int `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
ProjectURL *string `json:"project_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
// ListProjectColumns lists the columns of a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#list-project-columns
func (s *ProjectsService) ListProjectColumns(projectId int, opt *ListOptions) ([]*ProjectColumn, *Response, error) {
u := fmt.Sprintf("/projects/%v/columns", projectId)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
columns := []*ProjectColumn{}
resp, err := s.client.Do(req, &columns)
if err != nil {
return nil, resp, err
}
return columns, resp, err
}
// GetProjectColumn gets a column of a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#get-a-project-column
func (s *ProjectsService) GetProjectColumn(id int) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("/projects/columns/%v", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
resp, err := s.client.Do(req, column)
if err != nil {
return nil, resp, err
}
return column, resp, err
}
// ProjectColumnOptions specifies the parameters to the
// ProjectsService.CreateProjectColumn and
// ProjectsService.UpdateProjectColumn methods.
type ProjectColumnOptions struct {
// The name of the project column. (Required for creation and update.)
Name string `json:"name"`
}
// CreateProjectColumn creates a column for the specified (by number) project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#create-a-project-column
func (s *ProjectsService) CreateProjectColumn(projectId int, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("/projects/%v/columns", projectId)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
resp, err := s.client.Do(req, column)
if err != nil {
return nil, resp, err
}
return column, resp, err
}
// UpdateProjectColumn updates a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#update-a-project-column
func (s *ProjectsService) UpdateProjectColumn(columnID int, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("/projects/columns/%v", columnID)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
resp, err := s.client.Do(req, column)
if err != nil {
return nil, resp, err
}
return column, resp, err
}
// DeleteProjectColumn deletes a column from a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#delete-a-project-column
func (s *ProjectsService) DeleteProjectColumn(columnID int) (*Response, error) {
u := fmt.Sprintf("/projects/columns/%v", columnID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(req, nil)
}
// ProjectColumnMoveOptions specifies the parameters to the
// ProjectsService.MoveProjectColumn method.
type ProjectColumnMoveOptions struct {
// Position can be one of "first", "last", or "after:<column-id>", where
// <column-id> is the ID of a column in the same project. (Required.)
Position string `json:"position"`
}
// MoveProjectColumn moves a column within a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#move-a-project-column
func (s *ProjectsService) MoveProjectColumn(columnID int, opt *ProjectColumnMoveOptions) (*Response, error) {
u := fmt.Sprintf("/projects/columns/%v/moves", columnID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(req, nil)
}
// ProjectCard represents a card in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/
type ProjectCard struct {
ColumnURL *string `json:"column_url,omitempty"`
ContentURL *string `json:"content_url,omitempty"`
ID *int `json:"id,omitempty"`
Note *string `json:"note,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
// ListProjectCards lists the cards in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#list-project-cards
func (s *ProjectsService) ListProjectCards(columnID int, opt *ListOptions) ([]*ProjectCard, *Response, error) {
u := fmt.Sprintf("/projects/columns/%v/cards", columnID)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
cards := []*ProjectCard{}
resp, err := s.client.Do(req, &cards)
if err != nil {
return nil, resp, err
}
return cards, resp, err
}
// GetProjectCard gets a card in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card
func (s *ProjectsService) GetProjectCard(columnID int) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("/projects/columns/cards/%v", columnID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
resp, err := s.client.Do(req, card)
if err != nil {
return nil, resp, err
}
return card, resp, err
}
// ProjectCardOptions specifies the parameters to the
// ProjectsService.CreateProjectCard and
// ProjectsService.UpdateProjectCard methods.
type ProjectCardOptions struct {
// The note of the card. Note and ContentID are mutually exclusive.
Note string `json:"note,omitempty"`
// The ID (not Number) of the Issue or Pull Request to associate with this card.
// Note and ContentID are mutually exclusive.
ContentID int `json:"content_id,omitempty"`
// The type of content to associate with this card. Possible values are: "Issue", "PullRequest".
ContentType string `json:"content_type,omitempty"`
}
// CreateProjectCard creates a card in the specified column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#create-a-project-card
func (s *ProjectsService) CreateProjectCard(columnID int, opt *ProjectCardOptions) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("/projects/columns/%v/cards", columnID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
resp, err := s.client.Do(req, card)
if err != nil {
return nil, resp, err
}
return card, resp, err
}
// UpdateProjectCard updates a card of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#update-a-project-card
func (s *ProjectsService) UpdateProjectCard(cardID int, opt *ProjectCardOptions) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("/projects/columns/cards/%v", cardID)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
resp, err := s.client.Do(req, card)
if err != nil {
return nil, resp, err
}
return card, resp, err
}
// DeleteProjectCard deletes a card from a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#delete-a-project-card
func (s *ProjectsService) DeleteProjectCard(cardID int) (*Response, error) {
u := fmt.Sprintf("/projects/columns/cards/%v", cardID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(req, nil)
}
// ProjectCardMoveOptions specifies the parameters to the
// ProjectsService.MoveProjectCard method.
type ProjectCardMoveOptions struct {
// Position can be one of "top", "bottom", or "after:<card-id>", where
// <card-id> is the ID of a card in the same project.
Position string `json:"position"`
// ColumnID is the ID of a column in the same project. Note that ColumnID
// is required when using Position "after:<card-id>" when that card is in
// another column; otherwise it is optional.
ColumnID int `json:"column_id,omitempty"`
}
// MoveProjectCard moves a card within a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#move-a-project-card
func (s *ProjectsService) MoveProjectCard(cardID int, opt *ProjectCardMoveOptions) (*Response, error) {
u := fmt.Sprintf("/projects/columns/cards/%v/moves", cardID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(req, nil)
}

View File

@ -253,12 +253,16 @@ type PullRequestMergeResult struct {
// PullRequestOptions lets you define how a pull request will be merged. // PullRequestOptions lets you define how a pull request will be merged.
type PullRequestOptions struct { type PullRequestOptions struct {
Squash bool CommitTitle string
// The merge method to use. Possible values include: "merge", "squash", and "rebase" with the default being merge.
MergeMethod string
} }
type pullRequestMergeRequest struct { type pullRequestMergeRequest struct {
CommitMessage *string `json:"commit_message"` CommitMessage *string `json:"commit_message"`
Squash *bool `json:"squash,omitempty"` CommitTitle *string `json:"commit_title,omitempty"`
MergeMethod *string `json:"merge_method,omitempty"`
} }
// Merge a pull request (Merge Button™). // Merge a pull request (Merge Button™).
@ -269,7 +273,8 @@ func (s *PullRequestsService) Merge(owner string, repo string, number int, commi
pullRequestBody := &pullRequestMergeRequest{CommitMessage: &commitMessage} pullRequestBody := &pullRequestMergeRequest{CommitMessage: &commitMessage}
if options != nil { if options != nil {
pullRequestBody.Squash = &options.Squash pullRequestBody.CommitTitle = &options.CommitTitle
pullRequestBody.MergeMethod = &options.MergeMethod
} }
req, err := s.client.NewRequest("PUT", u, pullRequestBody) req, err := s.client.NewRequest("PUT", u, pullRequestBody)

View File

@ -5,7 +5,10 @@
package github package github
import "fmt" import (
"fmt"
"strings"
)
// RepositoriesService handles communication with the repository related // RepositoriesService handles communication with the repository related
// methods of the GitHub API. // methods of the GitHub API.
@ -46,6 +49,9 @@ type Repository struct {
Source *Repository `json:"source,omitempty"` Source *Repository `json:"source,omitempty"`
Organization *Organization `json:"organization,omitempty"` Organization *Organization `json:"organization,omitempty"`
Permissions *map[string]bool `json:"permissions,omitempty"` Permissions *map[string]bool `json:"permissions,omitempty"`
AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"`
AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"`
AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"`
// Only provided when using RepositoriesService.Get while in preview // Only provided when using RepositoriesService.Get while in preview
License *License `json:"license,omitempty"` License *License `json:"license,omitempty"`
@ -286,7 +292,8 @@ func (s *RepositoriesService) Get(owner, repo string) (*Repository, *Response, e
// TODO: remove custom Accept header when the license support fully launches // TODO: remove custom Accept header when the license support fully launches
// https://developer.github.com/v3/licenses/#get-a-repositorys-license // https://developer.github.com/v3/licenses/#get-a-repositorys-license
req.Header.Set("Accept", mediaTypeLicensesPreview) acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeSquashPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
repository := new(Repository) repository := new(Repository)
resp, err := s.client.Do(req, repository) resp, err := s.client.Do(req, repository)
@ -330,6 +337,9 @@ func (s *RepositoriesService) Edit(owner, repo string, repository *Repository) (
return nil, nil, err return nil, nil, err
} }
// TODO: Remove this preview header after API is fully vetted.
req.Header.Add("Accept", mediaTypeSquashPreview)
r := new(Repository) r := new(Repository)
resp, err := s.client.Do(req, r) resp, err := s.client.Do(req, r)
if err != nil { if err != nil {
@ -585,18 +595,18 @@ func (s *RepositoriesService) EditBranch(owner, repo, branchName string, branch
// License gets the contents of a repository's license if one is detected. // License gets the contents of a repository's license if one is detected.
// //
// GitHub API docs: https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license // GitHub API docs: https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license
func (s *RepositoriesService) License(owner, repo string) (*License, *Response, error) { func (s *RepositoriesService) License(owner, repo string) (*RepositoryLicense, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/license", owner, repo) u := fmt.Sprintf("repos/%v/%v/license", owner, repo)
req, err := s.client.NewRequest("GET", u, nil) req, err := s.client.NewRequest("GET", u, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
r := &Repository{} r := &RepositoryLicense{}
resp, err := s.client.Do(req, r) resp, err := s.client.Do(req, r)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
return r.License, resp, err return r, resp, err
} }

View File

@ -36,7 +36,7 @@ type PagesBuild struct {
// GetPagesInfo fetches information about a GitHub Pages site. // GetPagesInfo fetches information about a GitHub Pages site.
// //
// GitHub API docs: https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site // GitHub API docs: https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site
func (s *RepositoriesService) GetPagesInfo(owner string, repo string) (*Pages, *Response, error) { func (s *RepositoriesService) GetPagesInfo(owner, repo string) (*Pages, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pages", owner, repo) u := fmt.Sprintf("repos/%v/%v/pages", owner, repo)
req, err := s.client.NewRequest("GET", u, nil) req, err := s.client.NewRequest("GET", u, nil)
if err != nil { if err != nil {
@ -58,7 +58,7 @@ func (s *RepositoriesService) GetPagesInfo(owner string, repo string) (*Pages, *
// ListPagesBuilds lists the builds for a GitHub Pages site. // ListPagesBuilds lists the builds for a GitHub Pages site.
// //
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-pages-builds // GitHub API docs: https://developer.github.com/v3/repos/pages/#list-pages-builds
func (s *RepositoriesService) ListPagesBuilds(owner string, repo string) ([]*PagesBuild, *Response, error) { func (s *RepositoriesService) ListPagesBuilds(owner, repo string) ([]*PagesBuild, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo) u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo)
req, err := s.client.NewRequest("GET", u, nil) req, err := s.client.NewRequest("GET", u, nil)
if err != nil { if err != nil {
@ -77,7 +77,7 @@ func (s *RepositoriesService) ListPagesBuilds(owner string, repo string) ([]*Pag
// GetLatestPagesBuild fetches the latest build information for a GitHub pages site. // GetLatestPagesBuild fetches the latest build information for a GitHub pages site.
// //
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-latest-pages-build // GitHub API docs: https://developer.github.com/v3/repos/pages/#list-latest-pages-build
func (s *RepositoriesService) GetLatestPagesBuild(owner string, repo string) (*PagesBuild, *Response, error) { func (s *RepositoriesService) GetLatestPagesBuild(owner, repo string) (*PagesBuild, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pages/builds/latest", owner, repo) u := fmt.Sprintf("repos/%v/%v/pages/builds/latest", owner, repo)
req, err := s.client.NewRequest("GET", u, nil) req, err := s.client.NewRequest("GET", u, nil)
if err != nil { if err != nil {
@ -93,10 +93,29 @@ func (s *RepositoriesService) GetLatestPagesBuild(owner string, repo string) (*P
return build, resp, err return build, resp, err
} }
// GetPageBuild fetches the specific build information for a GitHub pages site.
//
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-a-specific-pages-build
func (s *RepositoriesService) GetPageBuild(owner, repo string, id int) (*PagesBuild, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pages/builds/%v", owner, repo, id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
build := new(PagesBuild)
resp, err := s.client.Do(req, build)
if err != nil {
return nil, resp, err
}
return build, resp, err
}
// RequestPageBuild requests a build of a GitHub Pages site without needing to push new commit. // RequestPageBuild requests a build of a GitHub Pages site without needing to push new commit.
// //
// GitHub API docs: https://developer.github.com/v3/repos/pages/#request-a-page-build // GitHub API docs: https://developer.github.com/v3/repos/pages/#request-a-page-build
func (s *RepositoriesService) RequestPageBuild(owner string, repo string) (*PagesBuild, *Response, error) { func (s *RepositoriesService) RequestPageBuild(owner, repo string) (*PagesBuild, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo) u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo)
req, err := s.client.NewRequest("POST", u, nil) req, err := s.client.NewRequest("POST", u, nil)
if err != nil { if err != nil {

View File

@ -5,34 +5,11 @@
package github package github
import ( import "fmt"
"fmt"
)
// Project represents a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/
type Project struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
OwnerURL *string `json:"owner_url,omitempty"`
Name *string `json:"name,omitempty"`
Body *string `json:"body,omitempty"`
Number *int `json:"number,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
// The User object that generated the project.
Creator *User `json:"creator,omitempty"`
}
func (p Project) String() string {
return Stringify(p)
}
// ListProjects lists the projects for a repo. // ListProjects lists the projects for a repo.
// //
// GitHub API docs: https://developer.github.com/v3/repos/projects/#list-projects // GitHub API docs: https://developer.github.com/v3/projects/#list-repository-projects
func (s *RepositoriesService) ListProjects(owner, repo string, opt *ListOptions) ([]*Project, *Response, error) { func (s *RepositoriesService) ListProjects(owner, repo string, opt *ListOptions) ([]*Project, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects", owner, repo) u := fmt.Sprintf("repos/%v/%v/projects", owner, repo)
u, err := addOptions(u, opt) u, err := addOptions(u, opt)
@ -57,44 +34,12 @@ func (s *RepositoriesService) ListProjects(owner, repo string, opt *ListOptions)
return projects, resp, err return projects, resp, err
} }
// GetProject gets a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#get-a-project
func (s *RepositoriesService) GetProject(owner, repo string, number int) (*Project, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/%v", owner, repo, number)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
project := &Project{}
resp, err := s.client.Do(req, project)
if err != nil {
return nil, resp, err
}
return project, resp, err
}
// ProjectOptions specifies the parameters to the
// RepositoriesService.CreateProject and
// RepositoriesService.UpdateProject methods.
type ProjectOptions struct {
// The name of the project. (Required for creation; optional for update.)
Name string `json:"name,omitempty"`
// The body of the project. (Optional.)
Body string `json:"body,omitempty"`
}
// CreateProject creates a GitHub Project for the specified repository. // CreateProject creates a GitHub Project for the specified repository.
// //
// GitHub API docs: https://developer.github.com/v3/repos/projects/#create-a-project // GitHub API docs: https://developer.github.com/v3/projects/#create-a-repository-project
func (s *RepositoriesService) CreateProject(owner, repo string, projectOptions *ProjectOptions) (*Project, *Response, error) { func (s *RepositoriesService) CreateProject(owner, repo string, opt *ProjectOptions) (*Project, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects", owner, repo) u := fmt.Sprintf("repos/%v/%v/projects", owner, repo)
req, err := s.client.NewRequest("POST", u, projectOptions) req, err := s.client.NewRequest("POST", u, opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -110,355 +55,3 @@ func (s *RepositoriesService) CreateProject(owner, repo string, projectOptions *
return project, resp, err return project, resp, err
} }
// UpdateProject updates a repository project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#update-a-project
func (s *RepositoriesService) UpdateProject(owner, repo string, number int, projectOptions *ProjectOptions) (*Project, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/%v", owner, repo, number)
req, err := s.client.NewRequest("PATCH", u, projectOptions)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
project := &Project{}
resp, err := s.client.Do(req, project)
if err != nil {
return nil, resp, err
}
return project, resp, err
}
// DeleteProject deletes a GitHub Project from a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#delete-a-project
func (s *RepositoriesService) DeleteProject(owner, repo string, number int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/%v", owner, repo, number)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(req, nil)
}
// ProjectColumn represents a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/
type ProjectColumn struct {
ID *int `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
ProjectURL *string `json:"project_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
// ListProjectColumns lists the columns of a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#list-columns
func (s *RepositoriesService) ListProjectColumns(owner, repo string, number int, opt *ListOptions) ([]*ProjectColumn, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/%v/columns", owner, repo, number)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
columns := []*ProjectColumn{}
resp, err := s.client.Do(req, &columns)
if err != nil {
return nil, resp, err
}
return columns, resp, err
}
// GetProjectColumn gets a column of a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#get-a-column
func (s *RepositoriesService) GetProjectColumn(owner, repo string, columnID int) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v", owner, repo, columnID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
resp, err := s.client.Do(req, column)
if err != nil {
return nil, resp, err
}
return column, resp, err
}
// ProjectColumnOptions specifies the parameters to the
// RepositoriesService.CreateProjectColumn and
// RepositoriesService.UpdateProjectColumn methods.
type ProjectColumnOptions struct {
// The name of the project column. (Required for creation and update.)
Name string `json:"name"`
}
// CreateProjectColumn creates a column for the specified (by number) project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#create-a-column
func (s *RepositoriesService) CreateProjectColumn(owner, repo string, number int, columnOptions *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/%v/columns", owner, repo, number)
req, err := s.client.NewRequest("POST", u, columnOptions)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
resp, err := s.client.Do(req, column)
if err != nil {
return nil, resp, err
}
return column, resp, err
}
// UpdateProjectColumn updates a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#update-a-column
func (s *RepositoriesService) UpdateProjectColumn(owner, repo string, columnID int, columnOptions *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v", owner, repo, columnID)
req, err := s.client.NewRequest("PATCH", u, columnOptions)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
resp, err := s.client.Do(req, column)
if err != nil {
return nil, resp, err
}
return column, resp, err
}
// DeleteProjectColumn deletes a column from a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#delete-a-column
func (s *RepositoriesService) DeleteProjectColumn(owner, repo string, columnID int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v", owner, repo, columnID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(req, nil)
}
// ProjectColumnMoveOptions specifies the parameters to the
// RepositoriesService.MoveProjectColumn method.
type ProjectColumnMoveOptions struct {
// Position can be one of "first", "last", or "after:<column-id>", where
// <column-id> is the ID of a column in the same project. (Required.)
Position string `json:"position"`
}
// MoveProjectColumn moves a column within a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#move-a-column
func (s *RepositoriesService) MoveProjectColumn(owner, repo string, columnID int, moveOptions *ProjectColumnMoveOptions) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v/moves", owner, repo, columnID)
req, err := s.client.NewRequest("POST", u, moveOptions)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(req, nil)
}
// ProjectCard represents a card in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/
type ProjectCard struct {
ColumnURL *string `json:"column_url,omitempty"`
ContentURL *string `json:"content_url,omitempty"`
ID *int `json:"id,omitempty"`
Note *string `json:"note,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
// ListProjectCards lists the cards in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#list-projects-cards
func (s *RepositoriesService) ListProjectCards(owner, repo string, columnID int, opt *ListOptions) ([]*ProjectCard, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v/cards", owner, repo, columnID)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
cards := []*ProjectCard{}
resp, err := s.client.Do(req, &cards)
if err != nil {
return nil, resp, err
}
return cards, resp, err
}
// GetProjectCard gets a card in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#get-a-project-card
func (s *RepositoriesService) GetProjectCard(owner, repo string, columnID int) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/columns/cards/%v", owner, repo, columnID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
resp, err := s.client.Do(req, card)
if err != nil {
return nil, resp, err
}
return card, resp, err
}
// ProjectCardOptions specifies the parameters to the
// RepositoriesService.CreateProjectCard and
// RepositoriesService.UpdateProjectCard methods.
type ProjectCardOptions struct {
// The note of the card. Note and ContentID are mutually exclusive.
Note string `json:"note,omitempty"`
// The ID (not Number) of the Issue or Pull Request to associate with this card.
// Note and ContentID are mutually exclusive.
ContentID int `json:"content_id,omitempty"`
// The type of content to associate with this card. Possible values are: "Issue", "PullRequest".
ContentType string `json:"content_type,omitempty"`
}
// CreateProjectCard creates a card in the specified column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#create-a-project-card
func (s *RepositoriesService) CreateProjectCard(owner, repo string, columnID int, cardOptions *ProjectCardOptions) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/columns/%v/cards", owner, repo, columnID)
req, err := s.client.NewRequest("POST", u, cardOptions)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
resp, err := s.client.Do(req, card)
if err != nil {
return nil, resp, err
}
return card, resp, err
}
// UpdateProjectCard updates a card of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#update-a-project-card
func (s *RepositoriesService) UpdateProjectCard(owner, repo string, cardID int, cardOptions *ProjectCardOptions) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/columns/cards/%v", owner, repo, cardID)
req, err := s.client.NewRequest("PATCH", u, cardOptions)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
resp, err := s.client.Do(req, card)
if err != nil {
return nil, resp, err
}
return card, resp, err
}
// DeleteProjectCard deletes a card from a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#delete-a-project-card
func (s *RepositoriesService) DeleteProjectCard(owner, repo string, cardID int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/columns/cards/%v", owner, repo, cardID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(req, nil)
}
// ProjectCardMoveOptions specifies the parameters to the
// RepositoriesService.MoveProjectCard method.
type ProjectCardMoveOptions struct {
// Position can be one of "top", "bottom", or "after:<card-id>", where
// <card-id> is the ID of a card in the same project.
Position string `json:"position"`
// ColumnID is the ID of a column in the same project. Note that ColumnID
// is required when using Position "after:<card-id>" when that card is in
// another column; otherwise it is optional.
ColumnID int `json:"column_id,omitempty"`
}
// MoveProjectCard moves a card within a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/#move-a-project-card
func (s *RepositoriesService) MoveProjectCard(owner, repo string, cardID int, moveOptions *ProjectCardMoveOptions) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/projects/columns/cards/%v/moves", owner, repo, cardID)
req, err := s.client.NewRequest("POST", u, moveOptions)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(req, nil)
}

View File

@ -73,6 +73,7 @@ type AgentServiceCheck struct {
HTTP string `json:",omitempty"` HTTP string `json:",omitempty"`
TCP string `json:",omitempty"` TCP string `json:",omitempty"`
Status string `json:",omitempty"` Status string `json:",omitempty"`
TLSSkipVerify string `json:",omitempty"`
// In Consul 0.7 and later, checks that are associated with a service // In Consul 0.7 and later, checks that are associated with a service
// may also contain this optional DeregisterCriticalServiceAfter field, // may also contain this optional DeregisterCriticalServiceAfter field,

View File

@ -156,7 +156,7 @@ func (k *KV) Keys(prefix, separator string, q *QueryOptions) ([]string, *QueryMe
} }
func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) { func (k *KV) getInternal(key string, params map[string]string, q *QueryOptions) (*http.Response, *QueryMeta, error) {
r := k.c.newRequest("GET", "/v1/kv/"+key) r := k.c.newRequest("GET", "/v1/kv/"+strings.TrimPrefix(key, "/"))
r.setQueryOptions(q) r.setQueryOptions(q)
for param, val := range params { for param, val := range params {
r.params.Set(param, val) r.params.Set(param, val)
@ -277,7 +277,7 @@ func (k *KV) DeleteTree(prefix string, w *WriteOptions) (*WriteMeta, error) {
} }
func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) { func (k *KV) deleteInternal(key string, params map[string]string, q *WriteOptions) (bool, *WriteMeta, error) {
r := k.c.newRequest("DELETE", "/v1/kv/"+key) r := k.c.newRequest("DELETE", "/v1/kv/"+strings.TrimPrefix(key, "/"))
r.setWriteOptions(q) r.setWriteOptions(q)
for param, val := range params { for param, val := range params {
r.params.Set(param, val) r.params.Set(param, val)

View File

@ -167,19 +167,18 @@ func (c *PreparedQuery) Get(queryID string, q *QueryOptions) ([]*PreparedQueryDe
} }
// Delete is used to delete a specific prepared query. // Delete is used to delete a specific prepared query.
func (c *PreparedQuery) Delete(queryID string, q *QueryOptions) (*QueryMeta, error) { func (c *PreparedQuery) Delete(queryID string, q *WriteOptions) (*WriteMeta, error) {
r := c.c.newRequest("DELETE", "/v1/query/"+queryID) r := c.c.newRequest("DELETE", "/v1/query/"+queryID)
r.setQueryOptions(q) r.setWriteOptions(q)
rtt, resp, err := requireOK(c.c.doRequest(r)) rtt, resp, err := requireOK(c.c.doRequest(r))
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
qm := &QueryMeta{} wm := &WriteMeta{}
parseQueryMeta(resp, qm) wm.RequestTime = rtt
qm.RequestTime = rtt return wm, nil
return qm, nil
} }
// Execute is used to execute a specific prepared query. You can execute using // Execute is used to execute a specific prepared query. You can execute using

View File

@ -12,12 +12,16 @@ type ErrorFormatFunc func([]error) string
// ListFormatFunc is a basic formatter that outputs the number of errors // ListFormatFunc is a basic formatter that outputs the number of errors
// that occurred along with a bullet point list of the errors. // that occurred along with a bullet point list of the errors.
func ListFormatFunc(es []error) string { func ListFormatFunc(es []error) string {
if len(es) == 1 {
return fmt.Sprintf("1 error occurred:\n\n* %s", es[0])
}
points := make([]string, len(es)) points := make([]string, len(es))
for i, err := range es { for i, err := range es {
points[i] = fmt.Sprintf("* %s", err) points[i] = fmt.Sprintf("* %s", err)
} }
return fmt.Sprintf( return fmt.Sprintf(
"%d error(s) occurred:\n\n%s", "%d errors occurred:\n\n%s",
len(es), strings.Join(points, "\n")) len(es), strings.Join(points, "\n"))
} }

View File

@ -46,7 +46,7 @@ func Unquote(s string) (t string, err error) {
for len(s) > 0 { for len(s) > 0 {
// If we're starting a '${}' then let it through un-unquoted. // If we're starting a '${}' then let it through un-unquoted.
// Specifically: we don't unquote any characters within the `${}` // Specifically: we don't unquote any characters within the `${}`
// section, except for escaped backslashes, which we handle specifically. // section.
if s[0] == '$' && len(s) > 1 && s[1] == '{' { if s[0] == '$' && len(s) > 1 && s[1] == '{' {
buf = append(buf, '$', '{') buf = append(buf, '$', '{')
s = s[2:] s = s[2:]
@ -61,16 +61,6 @@ func Unquote(s string) (t string, err error) {
s = s[size:] s = s[size:]
// We special case escaped backslashes in interpolations, converting
// them to their unescaped equivalents.
if r == '\\' {
q, _ := utf8.DecodeRuneInString(s)
switch q {
case '\\':
continue
}
}
n := utf8.EncodeRune(runeTmp[:], r) n := utf8.EncodeRune(runeTmp[:], r)
buf = append(buf, runeTmp[:n]...) buf = append(buf, runeTmp[:n]...)

11
vendor/github.com/lib/pq/conn.go generated vendored
View File

@ -1507,12 +1507,23 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
dest[i] = decode(&conn.parameterStatus, rs.rb.next(l), rs.colTyps[i], rs.colFmts[i]) dest[i] = decode(&conn.parameterStatus, rs.rb.next(l), rs.colTyps[i], rs.colFmts[i])
} }
return return
case 'T':
rs.colNames, rs.colFmts, rs.colTyps = parsePortalRowDescribe(&rs.rb)
return io.EOF
default: default:
errorf("unexpected message after execute: %q", t) errorf("unexpected message after execute: %q", t)
} }
} }
} }
func (rs *rows) HasNextResultSet() bool {
return !rs.done
}
func (rs *rows) NextResultSet() error {
return nil
}
// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be // QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be
// used as part of an SQL statement. For example: // used as part of an SQL statement. For example:
// //

View File

@ -7,6 +7,7 @@ import (
"os" "os"
) )
// NewColorable return new instance of Writer which handle escape sequence.
func NewColorable(file *os.File) io.Writer { func NewColorable(file *os.File) io.Writer {
if file == nil { if file == nil {
panic("nil passed instead of *os.File to NewColorable()") panic("nil passed instead of *os.File to NewColorable()")
@ -15,10 +16,12 @@ func NewColorable(file *os.File) io.Writer {
return file return file
} }
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
func NewColorableStdout() io.Writer { func NewColorableStdout() io.Writer {
return os.Stdout return os.Stdout
} }
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
func NewColorableStderr() io.Writer { func NewColorableStderr() io.Writer {
return os.Stderr return os.Stderr
} }

View File

@ -75,6 +75,7 @@ type Writer struct {
oldpos coord oldpos coord
} }
// NewColorable return new instance of Writer which handle escape sequence from File.
func NewColorable(file *os.File) io.Writer { func NewColorable(file *os.File) io.Writer {
if file == nil { if file == nil {
panic("nil passed instead of *os.File to NewColorable()") panic("nil passed instead of *os.File to NewColorable()")
@ -90,10 +91,12 @@ func NewColorable(file *os.File) io.Writer {
} }
} }
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
func NewColorableStdout() io.Writer { func NewColorableStdout() io.Writer {
return NewColorable(os.Stdout) return NewColorable(os.Stdout)
} }
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
func NewColorableStderr() io.Writer { func NewColorableStderr() io.Writer {
return NewColorable(os.Stderr) return NewColorable(os.Stderr)
} }
@ -357,6 +360,7 @@ var color256 = map[int]int{
255: 0xeeeeee, 255: 0xeeeeee,
} }
// Write write data on console
func (w *Writer) Write(data []byte) (n int, err error) { func (w *Writer) Write(data []byte) (n int, err error) {
var csbi consoleScreenBufferInfo var csbi consoleScreenBufferInfo
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))

View File

@ -5,15 +5,18 @@ import (
"io" "io"
) )
// NonColorable hold writer but remove escape sequence.
type NonColorable struct { type NonColorable struct {
out io.Writer out io.Writer
lastbuf bytes.Buffer lastbuf bytes.Buffer
} }
// NewNonColorable return new instance of Writer which remove escape sequence from Writer.
func NewNonColorable(w io.Writer) io.Writer { func NewNonColorable(w io.Writer) io.Writer {
return &NonColorable{out: w} return &NonColorable{out: w}
} }
// Write write data on console
func (w *NonColorable) Write(data []byte) (n int, err error) { func (w *NonColorable) Write(data []byte) (n int, err error) {
er := bytes.NewReader(data) er := bytes.NewReader(data)
var bw [1]byte var bw [1]byte

View File

@ -36,6 +36,103 @@
// Contexts. // Contexts.
package context // import "golang.org/x/net/context" package context // import "golang.org/x/net/context"
import "time"
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}
// Background returns a non-nil, empty Context. It is never canceled, has no // Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function, // values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming // initialization, and tests, and as the top-level Context for incoming
@ -52,3 +149,8 @@ func Background() Context {
func TODO() Context { func TODO() Context {
return todo return todo
} }
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()

View File

@ -1,22 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.8
package context
import (
"context" // standard library's context, as of Go 1.7
)
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context => context.Context
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc => context.CancelFunc

View File

@ -1,109 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.8
package context
import "time"
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}

View File

@ -35,3 +35,7 @@ func configureServer18(h1 *http.Server, h2 *Server) error {
} }
return nil return nil
} }
func shouldLogPanic(panicValue interface{}) bool {
return panicValue != nil && panicValue != http.ErrAbortHandler
}

View File

@ -12,3 +12,7 @@ func configureServer18(h1 *http.Server, h2 *Server) error {
// No IdleTimeout to sync prior to Go 1.8. // No IdleTimeout to sync prior to Go 1.8.
return nil return nil
} }
func shouldLogPanic(panicValue interface{}) bool {
return panicValue != nil
}

View File

@ -188,9 +188,6 @@ func ConfigureServer(s *http.Server, conf *Server) error {
if !haveNPN { if !haveNPN {
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, NextProtoTLS) s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, NextProtoTLS)
} }
// h2-14 is temporary (as of 2015-03-05) while we wait for all browsers
// to switch to "h2".
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2-14")
if s.TLSNextProto == nil { if s.TLSNextProto == nil {
s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){} s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){}
@ -205,7 +202,6 @@ func ConfigureServer(s *http.Server, conf *Server) error {
}) })
} }
s.TLSNextProto[NextProtoTLS] = protoHandler s.TLSNextProto[NextProtoTLS] = protoHandler
s.TLSNextProto["h2-14"] = protoHandler // temporary; see above.
return nil return nil
} }
@ -390,8 +386,8 @@ type serverConn struct {
advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
curClientStreams uint32 // number of open streams initiated by the client curClientStreams uint32 // number of open streams initiated by the client
curPushedStreams uint32 // number of open streams initiated by server push curPushedStreams uint32 // number of open streams initiated by server push
maxStreamID uint32 // max ever seen from client maxClientStreamID uint32 // max ever seen from client (odd), or 0 if there have been no client requests
maxPushPromiseID uint32 // ID of the last push promise, or 0 if there have been no pushes maxPushPromiseID uint32 // ID of the last push promise (even), or 0 if there have been no pushes
streams map[uint32]*stream streams map[uint32]*stream
initialWindowSize int32 initialWindowSize int32
maxFrameSize int32 maxFrameSize int32
@ -481,8 +477,14 @@ func (sc *serverConn) state(streamID uint32) (streamState, *stream) {
// a client sends a HEADERS frame on stream 7 without ever sending a // a client sends a HEADERS frame on stream 7 without ever sending a
// frame on stream 5, then stream 5 transitions to the "closed" // frame on stream 5, then stream 5 transitions to the "closed"
// state when the first frame for stream 7 is sent or received." // state when the first frame for stream 7 is sent or received."
if streamID <= sc.maxStreamID { if streamID%2 == 1 {
return stateClosed, nil if streamID <= sc.maxClientStreamID {
return stateClosed, nil
}
} else {
if streamID <= sc.maxPushPromiseID {
return stateClosed, nil
}
} }
return stateIdle, nil return stateIdle, nil
} }
@ -740,7 +742,7 @@ func (sc *serverConn) serve() {
return return
case <-gracefulShutdownCh: case <-gracefulShutdownCh:
gracefulShutdownCh = nil gracefulShutdownCh = nil
sc.goAwayIn(ErrCodeNo, 0) sc.startGracefulShutdown()
case <-sc.shutdownTimerCh: case <-sc.shutdownTimerCh:
sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr()) sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
return return
@ -1015,7 +1017,7 @@ func (sc *serverConn) scheduleFrameWrite() {
sc.needToSendGoAway = false sc.needToSendGoAway = false
sc.startFrameWrite(FrameWriteRequest{ sc.startFrameWrite(FrameWriteRequest{
write: &writeGoAway{ write: &writeGoAway{
maxStreamID: sc.maxStreamID, maxStreamID: sc.maxClientStreamID,
code: sc.goAwayCode, code: sc.goAwayCode,
}, },
}) })
@ -1042,6 +1044,13 @@ func (sc *serverConn) scheduleFrameWrite() {
sc.inFrameScheduleLoop = false sc.inFrameScheduleLoop = false
} }
// startGracefulShutdown sends a GOAWAY with ErrCodeNo to tell the
// client we're gracefully shutting down. The connection isn't closed
// until all current streams are done.
func (sc *serverConn) startGracefulShutdown() {
sc.goAwayIn(ErrCodeNo, 0)
}
func (sc *serverConn) goAway(code ErrCode) { func (sc *serverConn) goAway(code ErrCode) {
sc.serveG.check() sc.serveG.check()
var forceCloseIn time.Duration var forceCloseIn time.Duration
@ -1164,6 +1173,8 @@ func (sc *serverConn) processFrame(f Frame) error {
return sc.processResetStream(f) return sc.processResetStream(f)
case *PriorityFrame: case *PriorityFrame:
return sc.processPriority(f) return sc.processPriority(f)
case *GoAwayFrame:
return sc.processGoAway(f)
case *PushPromiseFrame: case *PushPromiseFrame:
// A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE // A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE
// frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. // frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
@ -1189,7 +1200,7 @@ func (sc *serverConn) processPing(f *PingFrame) error {
// PROTOCOL_ERROR." // PROTOCOL_ERROR."
return ConnectionError(ErrCodeProtocol) return ConnectionError(ErrCodeProtocol)
} }
if sc.inGoAway { if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
return nil return nil
} }
sc.writeFrame(FrameWriteRequest{write: writePingAck{f}}) sc.writeFrame(FrameWriteRequest{write: writePingAck{f}})
@ -1198,9 +1209,6 @@ func (sc *serverConn) processPing(f *PingFrame) error {
func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error { func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
sc.serveG.check() sc.serveG.check()
if sc.inGoAway {
return nil
}
switch { switch {
case f.StreamID != 0: // stream-level flow control case f.StreamID != 0: // stream-level flow control
state, st := sc.state(f.StreamID) state, st := sc.state(f.StreamID)
@ -1233,9 +1241,6 @@ func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
func (sc *serverConn) processResetStream(f *RSTStreamFrame) error { func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
sc.serveG.check() sc.serveG.check()
if sc.inGoAway {
return nil
}
state, st := sc.state(f.StreamID) state, st := sc.state(f.StreamID)
if state == stateIdle { if state == stateIdle {
@ -1265,12 +1270,15 @@ func (sc *serverConn) closeStream(st *stream, err error) {
} else { } else {
sc.curClientStreams-- sc.curClientStreams--
} }
if sc.curClientStreams+sc.curPushedStreams == 0 {
sc.setConnState(http.StateIdle)
}
delete(sc.streams, st.id) delete(sc.streams, st.id)
if len(sc.streams) == 0 && sc.srv.IdleTimeout != 0 { if len(sc.streams) == 0 {
sc.idleTimer.Reset(sc.srv.IdleTimeout) sc.setConnState(http.StateIdle)
if sc.srv.IdleTimeout != 0 {
sc.idleTimer.Reset(sc.srv.IdleTimeout)
}
if h1ServerKeepAlivesDisabled(sc.hs) {
sc.startGracefulShutdown()
}
} }
if p := st.body; p != nil { if p := st.body; p != nil {
// Return any buffered unread bytes worth of conn-level flow control. // Return any buffered unread bytes worth of conn-level flow control.
@ -1295,9 +1303,6 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
} }
return nil return nil
} }
if sc.inGoAway {
return nil
}
if err := f.ForeachSetting(sc.processSetting); err != nil { if err := f.ForeachSetting(sc.processSetting); err != nil {
return err return err
} }
@ -1369,7 +1374,7 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
func (sc *serverConn) processData(f *DataFrame) error { func (sc *serverConn) processData(f *DataFrame) error {
sc.serveG.check() sc.serveG.check()
if sc.inGoAway { if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
return nil return nil
} }
data := f.Data() data := f.Data()
@ -1448,6 +1453,20 @@ func (sc *serverConn) processData(f *DataFrame) error {
return nil return nil
} }
func (sc *serverConn) processGoAway(f *GoAwayFrame) error {
sc.serveG.check()
if f.ErrCode != ErrCodeNo {
sc.logf("http2: received GOAWAY %+v, starting graceful shutdown", f)
} else {
sc.vlogf("http2: received GOAWAY %+v, starting graceful shutdown", f)
}
sc.startGracefulShutdown()
// http://tools.ietf.org/html/rfc7540#section-6.8
// We should not create any new streams, which means we should disable push.
sc.pushEnabled = false
return nil
}
// isPushed reports whether the stream is server-initiated. // isPushed reports whether the stream is server-initiated.
func (st *stream) isPushed() bool { func (st *stream) isPushed() bool {
return st.id%2 == 0 return st.id%2 == 0
@ -1508,10 +1527,10 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
// endpoint has opened or reserved. [...] An endpoint that // endpoint has opened or reserved. [...] An endpoint that
// receives an unexpected stream identifier MUST respond with // receives an unexpected stream identifier MUST respond with
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR. // a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
if id <= sc.maxStreamID { if id <= sc.maxClientStreamID {
return ConnectionError(ErrCodeProtocol) return ConnectionError(ErrCodeProtocol)
} }
sc.maxStreamID = id sc.maxClientStreamID = id
if sc.idleTimer != nil { if sc.idleTimer != nil {
sc.idleTimer.Stop() sc.idleTimer.Stop()
@ -1847,15 +1866,17 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler
rw.rws.stream.cancelCtx() rw.rws.stream.cancelCtx()
if didPanic { if didPanic {
e := recover() e := recover()
// Same as net/http:
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
sc.writeFrameFromHandler(FrameWriteRequest{ sc.writeFrameFromHandler(FrameWriteRequest{
write: handlerPanicRST{rw.rws.stream.id}, write: handlerPanicRST{rw.rws.stream.id},
stream: rw.rws.stream, stream: rw.rws.stream,
}) })
sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf) // Same as net/http:
if shouldLogPanic(e) {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
}
return return
} }
rw.handlerDone() rw.handlerDone()
@ -2267,8 +2288,9 @@ func (w *responseWriter) CloseNotify() <-chan bool {
if ch == nil { if ch == nil {
ch = make(chan bool, 1) ch = make(chan bool, 1)
rws.closeNotifierCh = ch rws.closeNotifierCh = ch
cw := rws.stream.cw
go func() { go func() {
rws.stream.cw.Wait() // wait for close cw.Wait() // wait for close
ch <- true ch <- true
}() }()
} }
@ -2421,7 +2443,7 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
} }
for k := range opts.Header { for k := range opts.Header {
if strings.HasPrefix(k, ":") { if strings.HasPrefix(k, ":") {
return fmt.Errorf("promised request headers cannot include psuedo header %q", k) return fmt.Errorf("promised request headers cannot include pseudo header %q", k)
} }
// These headers are meaningful only if the request has a body, // These headers are meaningful only if the request has a body,
// but PUSH_PROMISE requests cannot have a body. // but PUSH_PROMISE requests cannot have a body.
@ -2514,6 +2536,12 @@ func (sc *serverConn) startPush(msg startPushRequest) {
// http://tools.ietf.org/html/rfc7540#section-5.1.1. // http://tools.ietf.org/html/rfc7540#section-5.1.1.
// Streams initiated by the server MUST use even-numbered identifiers. // Streams initiated by the server MUST use even-numbered identifiers.
// A server that is unable to establish a new stream identifier can send a GOAWAY
// frame so that the client is forced to open a new connection for new streams.
if sc.maxPushPromiseID+2 >= 1<<31 {
sc.startGracefulShutdown()
return 0, ErrPushLimitReached
}
sc.maxPushPromiseID += 2 sc.maxPushPromiseID += 2
promisedID := sc.maxPushPromiseID promisedID := sc.maxPushPromiseID
@ -2660,3 +2688,17 @@ func h1ServerShutdownChan(hs *http.Server) <-chan struct{} {
// optional test hook for h1ServerShutdownChan. // optional test hook for h1ServerShutdownChan.
var testh1ServerShutdownChan func(hs *http.Server) <-chan struct{} var testh1ServerShutdownChan func(hs *http.Server) <-chan struct{}
// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
// disabled. See comments on h1ServerShutdownChan above for why
// the code is written this way.
func h1ServerKeepAlivesDisabled(hs *http.Server) bool {
var x interface{} = hs
type I interface {
doKeepAlives() bool
}
if hs, ok := x.(I); ok {
return !hs.doKeepAlives()
}
return false
}

View File

@ -199,6 +199,7 @@ type clientStream struct {
bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read
readErr error // sticky read error; owned by transportResponseBody.Read readErr error // sticky read error; owned by transportResponseBody.Read
stopReqBody error // if non-nil, stop writing req body; guarded by cc.mu stopReqBody error // if non-nil, stop writing req body; guarded by cc.mu
didReset bool // whether we sent a RST_STREAM to the server; guarded by cc.mu
peerReset chan struct{} // closed on peer reset peerReset chan struct{} // closed on peer reset
resetErr error // populated before peerReset is closed resetErr error // populated before peerReset is closed
@ -226,15 +227,26 @@ func (cs *clientStream) awaitRequestCancel(req *http.Request) {
} }
select { select {
case <-req.Cancel: case <-req.Cancel:
cs.cancelStream()
cs.bufPipe.CloseWithError(errRequestCanceled) cs.bufPipe.CloseWithError(errRequestCanceled)
cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
case <-ctx.Done(): case <-ctx.Done():
cs.cancelStream()
cs.bufPipe.CloseWithError(ctx.Err()) cs.bufPipe.CloseWithError(ctx.Err())
cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
case <-cs.done: case <-cs.done:
} }
} }
func (cs *clientStream) cancelStream() {
cs.cc.mu.Lock()
didReset := cs.didReset
cs.didReset = true
cs.cc.mu.Unlock()
if !didReset {
cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
}
}
// checkResetOrDone reports any error sent in a RST_STREAM frame by the // checkResetOrDone reports any error sent in a RST_STREAM frame by the
// server, or errStreamClosed if the stream is complete. // server, or errStreamClosed if the stream is complete.
func (cs *clientStream) checkResetOrDone() error { func (cs *clientStream) checkResetOrDone() error {
@ -1666,9 +1678,10 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
cc.bw.Flush() cc.bw.Flush()
cc.wmu.Unlock() cc.wmu.Unlock()
} }
didReset := cs.didReset
cc.mu.Unlock() cc.mu.Unlock()
if len(data) > 0 { if len(data) > 0 && !didReset {
if _, err := cs.bufPipe.Write(data); err != nil { if _, err := cs.bufPipe.Write(data); err != nil {
rl.endStreamError(cs, err) rl.endStreamError(cs, err)
return err return err

View File

@ -25,7 +25,9 @@ type WriteScheduler interface {
// https://tools.ietf.org/html/rfc7540#section-5.1 // https://tools.ietf.org/html/rfc7540#section-5.1
AdjustStream(streamID uint32, priority PriorityParam) AdjustStream(streamID uint32, priority PriorityParam)
// Push queues a frame in the scheduler. // Push queues a frame in the scheduler. In most cases, this will not be
// called with wr.StreamID()!=0 unless that stream is currently open. The one
// exception is RST_STREAM frames, which may be sent on idle or closed streams.
Push(wr FrameWriteRequest) Push(wr FrameWriteRequest)
// Pop dequeues the next frame to write. Returns false if no frames can // Pop dequeues the next frame to write. Returns false if no frames can
@ -62,6 +64,13 @@ type FrameWriteRequest struct {
// 0 is used for non-stream frames such as PING and SETTINGS. // 0 is used for non-stream frames such as PING and SETTINGS.
func (wr FrameWriteRequest) StreamID() uint32 { func (wr FrameWriteRequest) StreamID() uint32 {
if wr.stream == nil { if wr.stream == nil {
if se, ok := wr.write.(StreamError); ok {
// (*serverConn).resetStream doesn't set
// stream because it doesn't necessarily have
// one. So special case this type of write
// message.
return se.StreamID
}
return 0 return 0
} }
return wr.stream.id return wr.stream.id
@ -142,17 +151,13 @@ func (wr FrameWriteRequest) Consume(n int32) (FrameWriteRequest, FrameWriteReque
// String is for debugging only. // String is for debugging only.
func (wr FrameWriteRequest) String() string { func (wr FrameWriteRequest) String() string {
var streamID uint32
if wr.stream != nil {
streamID = wr.stream.id
}
var des string var des string
if s, ok := wr.write.(fmt.Stringer); ok { if s, ok := wr.write.(fmt.Stringer); ok {
des = s.String() des = s.String()
} else { } else {
des = fmt.Sprintf("%T", wr.write) des = fmt.Sprintf("%T", wr.write)
} }
return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", streamID, wr.done != nil, des) return fmt.Sprintf("[FrameWriteRequest stream=%d, ch=%v, writer=%v]", wr.StreamID(), wr.done != nil, des)
} }
// writeQueue is used by implementations of WriteScheduler. // writeQueue is used by implementations of WriteScheduler.

View File

@ -388,7 +388,15 @@ func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) {
} else { } else {
n = ws.nodes[id] n = ws.nodes[id]
if n == nil { if n == nil {
panic("add on non-open stream") // id is an idle or closed stream. wr should not be a HEADERS or
// DATA frame. However, wr can be a RST_STREAM. In this case, we
// push wr onto the root, rather than creating a new priorityNode,
// since RST_STREAM is tiny and the stream's priority is unknown
// anyway. See issue #17919.
if wr.DataSize() > 0 {
panic("add DATA on non-open stream")
}
n = &ws.root
} }
} }
n.q.push(wr) n.q.push(wr)

View File

@ -752,7 +752,7 @@ func (tr *trace) addEvent(x interface{}, recyclable, sensitive bool) {
and very unlikely to be the fault of this code. and very unlikely to be the fault of this code.
The most likely scenario is that some code elsewhere is using The most likely scenario is that some code elsewhere is using
a requestz.Trace after its Finish method is called. a trace.Trace after its Finish method is called.
You can temporarily set the DebugUseAfterFinish var You can temporarily set the DebugUseAfterFinish var
to help discover where that is; do not leave that var set, to help discover where that is; do not leave that var set,
since it makes this package much less efficient. since it makes this package much less efficient.

View File

@ -899,6 +899,7 @@ func Getpgrp() (pid int) {
//sysnb Getppid() (ppid int) //sysnb Getppid() (ppid int)
//sys Getpriority(which int, who int) (prio int, err error) //sys Getpriority(which int, who int) (prio int, err error)
//sysnb Getrusage(who int, rusage *Rusage) (err error) //sysnb Getrusage(who int, rusage *Rusage) (err error)
//sysnb Getsid(pid int) (sid int, err error)
//sysnb Gettid() (tid int) //sysnb Gettid() (tid int)
//sys Getxattr(path string, attr string, dest []byte) (sz int, err error) //sys Getxattr(path string, attr string, dest []byte) (sz int, err error)
//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) //sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error)
@ -911,7 +912,7 @@ func Getpgrp() (pid int) {
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error) //sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT //sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
//sysnb prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) = SYS_PRLIMIT64 //sysnb prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64
//sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error) //sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error)
//sys read(fd int, p []byte) (n int, err error) //sys read(fd int, p []byte) (n int, err error)
//sys Removexattr(path string, attr string) (err error) //sys Removexattr(path string, attr string) (err error)

View File

@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, err error) {
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) { func Gettid() (tid int) {
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0) r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0) tid = int(r0)
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View File

@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, err error) {
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) { func Gettid() (tid int) {
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0) r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0) tid = int(r0)
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View File

@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, err error) {
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) { func Gettid() (tid int) {
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0) r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0) tid = int(r0)
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View File

@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, err error) {
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) { func Gettid() (tid int) {
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0) r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0) tid = int(r0)
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View File

@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, err error) {
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) { func Gettid() (tid int) {
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0) r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0) tid = int(r0)
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View File

@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, err error) {
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) { func Gettid() (tid int) {
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0) r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0) tid = int(r0)
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View File

@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, err error) {
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) { func Gettid() (tid int) {
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0) r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0) tid = int(r0)
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View File

@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, err error) {
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) { func Gettid() (tid int) {
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0) r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0) tid = int(r0)
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View File

@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, err error) {
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) { func Gettid() (tid int) {
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0) r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0) tid = int(r0)
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View File

@ -572,6 +572,17 @@ func Getrusage(who int, rusage *Rusage) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Getsid(pid int) (sid int, err error) {
r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0)
sid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Gettid() (tid int) { func Gettid() (tid int) {
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0) r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
tid = int(r0) tid = int(r0)
@ -762,8 +773,8 @@ func PivotRoot(newroot string, putold string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) { func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0) _, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
if e1 != 0 { if e1 != 0 {
err = errnoErr(e1) err = errnoErr(e1)
} }

View File

@ -576,6 +576,9 @@ var logLevelName = map[int64]string{
} }
func logf(c *context, level int64, format string, args ...interface{}) { func logf(c *context, level int64, format string, args ...interface{}) {
if c == nil {
panic("not an App Engine context")
}
s := fmt.Sprintf(format, args...) s := fmt.Sprintf(format, args...)
s = strings.TrimRight(s, "\n") // Remove any trailing newline characters. s = strings.TrimRight(s, "\n") // Remove any trailing newline characters.
c.addLogLine(&logpb.UserAppLogLine{ c.addLogLine(&logpb.UserAppLogLine{

View File

@ -18,6 +18,22 @@ Prerequisites
This requires Go 1.5 or later. This requires Go 1.5 or later.
A note on the version used: significant performance improvements in benchmarks
of grpc-go have been seen by upgrading the go version from 1.5 to the latest
1.7.1.
From https://golang.org/doc/install, one way to install the latest version of go is:
```
$ GO_VERSION=1.7.1
$ OS=linux
$ ARCH=amd64
$ curl -O https://storage.googleapis.com/golang/go${GO_VERSION}.${OS}-${ARCH}.tar.gz
$ sudo tar -C /usr/local -xzf go$GO_VERSION.$OS-$ARCH.tar.gz
$ # Put go on the PATH, keep the usual installation dir
$ sudo ln -s /usr/local/go/bin/go /usr/bin/go
$ rm go$GO_VERSION.$OS-$ARCH.tar.gz
```
Constraints Constraints
----------- -----------
The grpc package should only depend on standard Go packages and a small number of exceptions. If your contribution introduces new dependencies which are NOT in the [list](http://godoc.org/google.golang.org/grpc?imports), you need a discussion with gRPC-Go authors and consultants. The grpc package should only depend on standard Go packages and a small number of exceptions. If your contribution introduces new dependencies which are NOT in the [list](http://godoc.org/google.golang.org/grpc?imports), you need a discussion with gRPC-Go authors and consultants.
@ -30,3 +46,12 @@ Status
------ ------
GA GA
FAQ
---
#### Compiling error, undefined: grpc.SupportPackageIsVersion
Please update proto package, gRPC package and rebuild the proto files:
- `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`
- `go get -u google.golang.org/grpc`
- `protoc --go_out=plugins=grpc:. *.proto`

View File

@ -42,6 +42,7 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/net/trace" "golang.org/x/net/trace"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/stats"
"google.golang.org/grpc/transport" "google.golang.org/grpc/transport"
) )
@ -49,7 +50,8 @@ import (
// On error, it returns the error and indicates whether the call should be retried. // On error, it returns the error and indicates whether the call should be retried.
// //
// TODO(zhaoq): Check whether the received message sequence is valid. // TODO(zhaoq): Check whether the received message sequence is valid.
func recvResponse(dopts dialOptions, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) (err error) { // TODO ctx is used for stats collection and processing. It is the context passed from the application.
func recvResponse(ctx context.Context, dopts dialOptions, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) (err error) {
// Try to acquire header metadata from the server if there is any. // Try to acquire header metadata from the server if there is any.
defer func() { defer func() {
if err != nil { if err != nil {
@ -63,14 +65,25 @@ func recvResponse(dopts dialOptions, t transport.ClientTransport, c *callInfo, s
return return
} }
p := &parser{r: stream} p := &parser{r: stream}
var inPayload *stats.InPayload
if stats.On() {
inPayload = &stats.InPayload{
Client: true,
}
}
for { for {
if err = recv(p, dopts.codec, stream, dopts.dc, reply, math.MaxInt32); err != nil { if err = recv(p, dopts.codec, stream, dopts.dc, reply, math.MaxInt32, inPayload); err != nil {
if err == io.EOF { if err == io.EOF {
break break
} }
return return
} }
} }
if inPayload != nil && err == io.EOF && stream.StatusCode() == codes.OK {
// TODO in the current implementation, inTrailer may be handled before inPayload in some cases.
// Fix the order if necessary.
stats.Handle(ctx, inPayload)
}
c.trailerMD = stream.Trailer() c.trailerMD = stream.Trailer()
return nil return nil
} }
@ -89,15 +102,27 @@ func sendRequest(ctx context.Context, codec Codec, compressor Compressor, callHd
} }
} }
}() }()
var cbuf *bytes.Buffer var (
cbuf *bytes.Buffer
outPayload *stats.OutPayload
)
if compressor != nil { if compressor != nil {
cbuf = new(bytes.Buffer) cbuf = new(bytes.Buffer)
} }
outBuf, err := encode(codec, args, compressor, cbuf) if stats.On() {
outPayload = &stats.OutPayload{
Client: true,
}
}
outBuf, err := encode(codec, args, compressor, cbuf, outPayload)
if err != nil { if err != nil {
return nil, Errorf(codes.Internal, "grpc: %v", err) return nil, Errorf(codes.Internal, "grpc: %v", err)
} }
err = t.Write(stream, outBuf, opts) err = t.Write(stream, outBuf, opts)
if err == nil && outPayload != nil {
outPayload.SentTime = time.Now()
stats.Handle(ctx, outPayload)
}
// t.NewStream(...) could lead to an early rejection of the RPC (e.g., the service/method // t.NewStream(...) could lead to an early rejection of the RPC (e.g., the service/method
// does not exist.) so that t.Write could get io.EOF from wait(...). Leave the following // does not exist.) so that t.Write could get io.EOF from wait(...). Leave the following
// recvResponse to get the final status. // recvResponse to get the final status.
@ -118,7 +143,7 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
return invoke(ctx, method, args, reply, cc, opts...) return invoke(ctx, method, args, reply, cc, opts...)
} }
func invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) (err error) { func invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) (e error) {
c := defaultCallInfo c := defaultCallInfo
for _, o := range opts { for _, o := range opts {
if err := o.before(&c); err != nil { if err := o.before(&c); err != nil {
@ -140,12 +165,30 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
c.traceInfo.tr.LazyLog(&c.traceInfo.firstLine, false) c.traceInfo.tr.LazyLog(&c.traceInfo.firstLine, false)
// TODO(dsymonds): Arrange for c.traceInfo.firstLine.remoteAddr to be set. // TODO(dsymonds): Arrange for c.traceInfo.firstLine.remoteAddr to be set.
defer func() { defer func() {
if err != nil { if e != nil {
c.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) c.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{e}}, true)
c.traceInfo.tr.SetError() c.traceInfo.tr.SetError()
} }
}() }()
} }
if stats.On() {
begin := &stats.Begin{
Client: true,
BeginTime: time.Now(),
FailFast: c.failFast,
}
stats.Handle(ctx, begin)
}
defer func() {
if stats.On() {
end := &stats.End{
Client: true,
EndTime: time.Now(),
Error: e,
}
stats.Handle(ctx, end)
}
}()
topts := &transport.Options{ topts := &transport.Options{
Last: true, Last: true,
Delay: false, Delay: false,
@ -205,7 +248,7 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
} }
return toRPCErr(err) return toRPCErr(err)
} }
err = recvResponse(cc.dopts, t, &c, stream, reply) err = recvResponse(ctx, cc.dopts, t, &c, stream, reply)
if err != nil { if err != nil {
if put != nil { if put != nil {
put() put()

View File

@ -199,6 +199,8 @@ func WithTimeout(d time.Duration) DialOption {
} }
// WithDialer returns a DialOption that specifies a function to use for dialing network addresses. // WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
// If FailOnNonTempDialError() is set to true, and an error is returned by f, gRPC checks the error's
// Temporary() method to decide if it should try to reconnect to the network address.
func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption { func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
return func(o *dialOptions) { return func(o *dialOptions) {
o.copts.Dialer = func(ctx context.Context, addr string) (net.Conn, error) { o.copts.Dialer = func(ctx context.Context, addr string) (net.Conn, error) {
@ -210,6 +212,17 @@ func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
} }
} }
// FailOnNonTempDialError returns a DialOption that specified if gRPC fails on non-temporary dial errors.
// If f is true, and dialer returns a non-temporary error, gRPC will fail the connection to the network
// address and won't try to reconnect.
// The default value of FailOnNonTempDialError is false.
// This is an EXPERIMENTAL API.
func FailOnNonTempDialError(f bool) DialOption {
return func(o *dialOptions) {
o.copts.FailOnNonTempDialError = f
}
}
// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs. // WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
func WithUserAgent(s string) DialOption { func WithUserAgent(s string) DialOption {
return func(o *dialOptions) { return func(o *dialOptions) {

View File

@ -42,11 +42,13 @@ import (
"io/ioutil" "io/ioutil"
"math" "math"
"os" "os"
"time"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/stats"
"google.golang.org/grpc/transport" "google.golang.org/grpc/transport"
) )
@ -255,9 +257,11 @@ func (p *parser) recvMsg(maxMsgSize int) (pf payloadFormat, msg []byte, err erro
// encode serializes msg and prepends the message header. If msg is nil, it // encode serializes msg and prepends the message header. If msg is nil, it
// generates the message header of 0 message length. // generates the message header of 0 message length.
func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte, error) { func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer, outPayload *stats.OutPayload) ([]byte, error) {
var b []byte var (
var length uint b []byte
length uint
)
if msg != nil { if msg != nil {
var err error var err error
// TODO(zhaoq): optimize to reduce memory alloc and copying. // TODO(zhaoq): optimize to reduce memory alloc and copying.
@ -265,6 +269,12 @@ func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte
if err != nil { if err != nil {
return nil, err return nil, err
} }
if outPayload != nil {
outPayload.Payload = msg
// TODO truncate large payload.
outPayload.Data = b
outPayload.Length = len(b)
}
if cp != nil { if cp != nil {
if err := cp.Do(cbuf, b); err != nil { if err := cp.Do(cbuf, b); err != nil {
return nil, err return nil, err
@ -295,6 +305,10 @@ func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte
// Copy encoded msg to buf // Copy encoded msg to buf
copy(buf[5:], b) copy(buf[5:], b)
if outPayload != nil {
outPayload.WireLength = len(buf)
}
return buf, nil return buf, nil
} }
@ -311,11 +325,14 @@ func checkRecvPayload(pf payloadFormat, recvCompress string, dc Decompressor) er
return nil return nil
} }
func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{}, maxMsgSize int) error { func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{}, maxMsgSize int, inPayload *stats.InPayload) error {
pf, d, err := p.recvMsg(maxMsgSize) pf, d, err := p.recvMsg(maxMsgSize)
if err != nil { if err != nil {
return err return err
} }
if inPayload != nil {
inPayload.WireLength = len(d)
}
if err := checkRecvPayload(pf, s.RecvCompress(), dc); err != nil { if err := checkRecvPayload(pf, s.RecvCompress(), dc); err != nil {
return err return err
} }
@ -333,6 +350,13 @@ func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{
if err := c.Unmarshal(d, m); err != nil { if err := c.Unmarshal(d, m); err != nil {
return Errorf(codes.Internal, "grpc: failed to unmarshal the received message %v", err) return Errorf(codes.Internal, "grpc: failed to unmarshal the received message %v", err)
} }
if inPayload != nil {
inPayload.RecvTime = time.Now()
inPayload.Payload = m
// TODO truncate large payload.
inPayload.Data = d
inPayload.Length = len(d)
}
return nil return nil
} }
@ -448,10 +472,10 @@ func convertCode(err error) codes.Code {
return codes.Unknown return codes.Unknown
} }
// SupportPackageIsVersion3 is referenced from generated protocol buffer files // SupportPackageIsVersion4 is referenced from generated protocol buffer files
// to assert that that code is compatible with this version of the grpc package. // to assert that that code is compatible with this version of the grpc package.
// //
// This constant may be renamed in the future if a change in the generated code // This constant may be renamed in the future if a change in the generated code
// requires a synchronised update of grpc-go and protoc-gen-go. This constant // requires a synchronised update of grpc-go and protoc-gen-go. This constant
// should not be referenced from any other code. // should not be referenced from any other code.
const SupportPackageIsVersion3 = true const SupportPackageIsVersion4 = true

View File

@ -54,6 +54,8 @@ import (
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal" "google.golang.org/grpc/internal"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/stats"
"google.golang.org/grpc/tap"
"google.golang.org/grpc/transport" "google.golang.org/grpc/transport"
) )
@ -110,6 +112,7 @@ type options struct {
maxMsgSize int maxMsgSize int
unaryInt UnaryServerInterceptor unaryInt UnaryServerInterceptor
streamInt StreamServerInterceptor streamInt StreamServerInterceptor
inTapHandle tap.ServerInHandle
maxConcurrentStreams uint32 maxConcurrentStreams uint32
useHandlerImpl bool // use http.Handler-based server useHandlerImpl bool // use http.Handler-based server
} }
@ -186,6 +189,17 @@ func StreamInterceptor(i StreamServerInterceptor) ServerOption {
} }
} }
// InTapHandle returns a ServerOption that sets the tap handle for all the server
// transport to be created. Only one can be installed.
func InTapHandle(h tap.ServerInHandle) ServerOption {
return func(o *options) {
if o.inTapHandle != nil {
panic("The tap handle has been set.")
}
o.inTapHandle = h
}
}
// NewServer creates a gRPC server which has no service registered and has not // NewServer creates a gRPC server which has no service registered and has not
// started to accept requests yet. // started to accept requests yet.
func NewServer(opt ...ServerOption) *Server { func NewServer(opt ...ServerOption) *Server {
@ -412,17 +426,22 @@ func (s *Server) handleRawConn(rawConn net.Conn) {
if s.opts.useHandlerImpl { if s.opts.useHandlerImpl {
s.serveUsingHandler(conn) s.serveUsingHandler(conn)
} else { } else {
s.serveNewHTTP2Transport(conn, authInfo) s.serveHTTP2Transport(conn, authInfo)
} }
} }
// serveNewHTTP2Transport sets up a new http/2 transport (using the // serveHTTP2Transport sets up a http/2 transport (using the
// gRPC http2 server transport in transport/http2_server.go) and // gRPC http2 server transport in transport/http2_server.go) and
// serves streams on it. // serves streams on it.
// This is run in its own goroutine (it does network I/O in // This is run in its own goroutine (it does network I/O in
// transport.NewServerTransport). // transport.NewServerTransport).
func (s *Server) serveNewHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) { func (s *Server) serveHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) {
st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo) config := &transport.ServerConfig{
MaxStreams: s.opts.maxConcurrentStreams,
AuthInfo: authInfo,
InTapHandle: s.opts.inTapHandle,
}
st, err := transport.NewServerTransport("http2", c, config)
if err != nil { if err != nil {
s.mu.Lock() s.mu.Lock()
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err) s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
@ -448,6 +467,12 @@ func (s *Server) serveStreams(st transport.ServerTransport) {
defer wg.Done() defer wg.Done()
s.handleStream(st, stream, s.traceInfo(st, stream)) s.handleStream(st, stream, s.traceInfo(st, stream))
}() }()
}, func(ctx context.Context, method string) context.Context {
if !EnableTracing {
return ctx
}
tr := trace.New("grpc.Recv."+methodFamily(method), method)
return trace.NewContext(ctx, tr)
}) })
wg.Wait() wg.Wait()
} }
@ -497,15 +522,17 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled. // traceInfo returns a traceInfo and associates it with stream, if tracing is enabled.
// If tracing is not enabled, it returns nil. // If tracing is not enabled, it returns nil.
func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) { func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
if !EnableTracing { tr, ok := trace.FromContext(stream.Context())
if !ok {
return nil return nil
} }
trInfo = &traceInfo{ trInfo = &traceInfo{
tr: trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()), tr: tr,
} }
trInfo.firstLine.client = false trInfo.firstLine.client = false
trInfo.firstLine.remoteAddr = st.RemoteAddr() trInfo.firstLine.remoteAddr = st.RemoteAddr()
stream.TraceContext(trInfo.tr)
if dl, ok := stream.Context().Deadline(); ok { if dl, ok := stream.Context().Deadline(); ok {
trInfo.firstLine.deadline = dl.Sub(time.Now()) trInfo.firstLine.deadline = dl.Sub(time.Now())
} }
@ -532,11 +559,17 @@ func (s *Server) removeConn(c io.Closer) {
} }
func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options) error { func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options) error {
var cbuf *bytes.Buffer var (
cbuf *bytes.Buffer
outPayload *stats.OutPayload
)
if cp != nil { if cp != nil {
cbuf = new(bytes.Buffer) cbuf = new(bytes.Buffer)
} }
p, err := encode(s.opts.codec, msg, cp, cbuf) if stats.On() {
outPayload = &stats.OutPayload{}
}
p, err := encode(s.opts.codec, msg, cp, cbuf, outPayload)
if err != nil { if err != nil {
// This typically indicates a fatal issue (e.g., memory // This typically indicates a fatal issue (e.g., memory
// corruption or hardware faults) the application program // corruption or hardware faults) the application program
@ -547,10 +580,32 @@ func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Str
// the optimal option. // the optimal option.
grpclog.Fatalf("grpc: Server failed to encode response %v", err) grpclog.Fatalf("grpc: Server failed to encode response %v", err)
} }
return t.Write(stream, p, opts) err = t.Write(stream, p, opts)
if err == nil && outPayload != nil {
outPayload.SentTime = time.Now()
stats.Handle(stream.Context(), outPayload)
}
return err
} }
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) { func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) {
if stats.On() {
begin := &stats.Begin{
BeginTime: time.Now(),
}
stats.Handle(stream.Context(), begin)
}
defer func() {
if stats.On() {
end := &stats.End{
EndTime: time.Now(),
}
if err != nil && err != io.EOF {
end.Error = toRPCErr(err)
}
stats.Handle(stream.Context(), end)
}
}()
if trInfo != nil { if trInfo != nil {
defer trInfo.tr.Finish() defer trInfo.tr.Finish()
trInfo.firstLine.client = false trInfo.firstLine.client = false
@ -579,14 +634,14 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
if err != nil { if err != nil {
switch err := err.(type) { switch err := err.(type) {
case *rpcError: case *rpcError:
if err := t.WriteStatus(stream, err.code, err.desc); err != nil { if e := t.WriteStatus(stream, err.code, err.desc); e != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err) grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
} }
case transport.ConnectionError: case transport.ConnectionError:
// Nothing to do here. // Nothing to do here.
case transport.StreamError: case transport.StreamError:
if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil { if e := t.WriteStatus(stream, err.Code, err.Desc); e != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err) grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
} }
default: default:
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err)) panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
@ -597,20 +652,29 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil { if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil {
switch err := err.(type) { switch err := err.(type) {
case *rpcError: case *rpcError:
if err := t.WriteStatus(stream, err.code, err.desc); err != nil { if e := t.WriteStatus(stream, err.code, err.desc); e != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err) grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
} }
return err
default: default:
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil { if e := t.WriteStatus(stream, codes.Internal, err.Error()); e != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err) grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
} }
// TODO checkRecvPayload always return RPC error. Add a return here if necessary.
}
}
var inPayload *stats.InPayload
if stats.On() {
inPayload = &stats.InPayload{
RecvTime: time.Now(),
} }
return err
} }
statusCode := codes.OK statusCode := codes.OK
statusDesc := "" statusDesc := ""
df := func(v interface{}) error { df := func(v interface{}) error {
if inPayload != nil {
inPayload.WireLength = len(req)
}
if pf == compressionMade { if pf == compressionMade {
var err error var err error
req, err = s.opts.dc.Do(bytes.NewReader(req)) req, err = s.opts.dc.Do(bytes.NewReader(req))
@ -618,7 +682,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil { if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err) grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
} }
return err return Errorf(codes.Internal, err.Error())
} }
} }
if len(req) > s.opts.maxMsgSize { if len(req) > s.opts.maxMsgSize {
@ -630,6 +694,12 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
if err := s.opts.codec.Unmarshal(req, v); err != nil { if err := s.opts.codec.Unmarshal(req, v); err != nil {
return err return err
} }
if inPayload != nil {
inPayload.Payload = v
inPayload.Data = req
inPayload.Length = len(req)
stats.Handle(stream.Context(), inPayload)
}
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true) trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true)
} }
@ -650,9 +720,8 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
} }
if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil { if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err) grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
return err
} }
return nil return Errorf(statusCode, statusDesc)
} }
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(stringer("OK"), false) trInfo.tr.LazyLog(stringer("OK"), false)
@ -677,11 +746,32 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true) trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
} }
return t.WriteStatus(stream, statusCode, statusDesc) errWrite := t.WriteStatus(stream, statusCode, statusDesc)
if statusCode != codes.OK {
return Errorf(statusCode, statusDesc)
}
return errWrite
} }
} }
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) { func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) {
if stats.On() {
begin := &stats.Begin{
BeginTime: time.Now(),
}
stats.Handle(stream.Context(), begin)
}
defer func() {
if stats.On() {
end := &stats.End{
EndTime: time.Now(),
}
if err != nil && err != io.EOF {
end.Error = toRPCErr(err)
}
stats.Handle(stream.Context(), end)
}
}()
if s.opts.cp != nil { if s.opts.cp != nil {
stream.SetSendCompress(s.opts.cp.Type()) stream.SetSendCompress(s.opts.cp.Type())
} }
@ -744,7 +834,11 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
} }
ss.mu.Unlock() ss.mu.Unlock()
} }
return t.WriteStatus(ss.s, ss.statusCode, ss.statusDesc) errWrite := t.WriteStatus(ss.s, ss.statusCode, ss.statusDesc)
if ss.statusCode != codes.OK {
return Errorf(ss.statusCode, ss.statusDesc)
}
return errWrite
} }
@ -759,7 +853,8 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
trInfo.tr.LazyLog(&fmtStringer{"Malformed method name %q", []interface{}{sm}}, true) trInfo.tr.LazyLog(&fmtStringer{"Malformed method name %q", []interface{}{sm}}, true)
trInfo.tr.SetError() trInfo.tr.SetError()
} }
if err := t.WriteStatus(stream, codes.InvalidArgument, fmt.Sprintf("malformed method name: %q", stream.Method())); err != nil { errDesc := fmt.Sprintf("malformed method name: %q", stream.Method())
if err := t.WriteStatus(stream, codes.InvalidArgument, errDesc); err != nil {
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
trInfo.tr.SetError() trInfo.tr.SetError()
@ -779,7 +874,8 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
trInfo.tr.LazyLog(&fmtStringer{"Unknown service %v", []interface{}{service}}, true) trInfo.tr.LazyLog(&fmtStringer{"Unknown service %v", []interface{}{service}}, true)
trInfo.tr.SetError() trInfo.tr.SetError()
} }
if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown service %v", service)); err != nil { errDesc := fmt.Sprintf("unknown service %v", service)
if err := t.WriteStatus(stream, codes.Unimplemented, errDesc); err != nil {
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
trInfo.tr.SetError() trInfo.tr.SetError()
@ -804,7 +900,8 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
trInfo.tr.LazyLog(&fmtStringer{"Unknown method %v", []interface{}{method}}, true) trInfo.tr.LazyLog(&fmtStringer{"Unknown method %v", []interface{}{method}}, true)
trInfo.tr.SetError() trInfo.tr.SetError()
} }
if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown method %v", method)); err != nil { errDesc := fmt.Sprintf("unknown method %v", method)
if err := t.WriteStatus(stream, codes.Unimplemented, errDesc); err != nil {
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
trInfo.tr.SetError() trInfo.tr.SetError()

219
vendor/google.golang.org/grpc/stats/stats.go generated vendored Normal file
View File

@ -0,0 +1,219 @@
/*
*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package stats is for collecting and reporting various network and RPC stats.
// This package is for monitoring purpose only. All fields are read-only.
// All APIs are experimental.
package stats // import "google.golang.org/grpc/stats"
import (
"net"
"sync/atomic"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc/grpclog"
)
// RPCStats contains stats information about RPCs.
// All stats types in this package implements this interface.
type RPCStats interface {
// IsClient returns true if this RPCStats is from client side.
IsClient() bool
}
// Begin contains stats when an RPC begins.
// FailFast are only valid if Client is true.
type Begin struct {
// Client is true if this Begin is from client side.
Client bool
// BeginTime is the time when the RPC begins.
BeginTime time.Time
// FailFast indicates if this RPC is failfast.
FailFast bool
}
// IsClient indicates if this is from client side.
func (s *Begin) IsClient() bool { return s.Client }
// InPayload contains the information for an incoming payload.
type InPayload struct {
// Client is true if this InPayload is from client side.
Client bool
// Payload is the payload with original type.
Payload interface{}
// Data is the serialized message payload.
Data []byte
// Length is the length of uncompressed data.
Length int
// WireLength is the length of data on wire (compressed, signed, encrypted).
WireLength int
// RecvTime is the time when the payload is received.
RecvTime time.Time
}
// IsClient indicates if this is from client side.
func (s *InPayload) IsClient() bool { return s.Client }
// InHeader contains stats when a header is received.
// FullMethod, addresses and Compression are only valid if Client is false.
type InHeader struct {
// Client is true if this InHeader is from client side.
Client bool
// WireLength is the wire length of header.
WireLength int
// FullMethod is the full RPC method string, i.e., /package.service/method.
FullMethod string
// RemoteAddr is the remote address of the corresponding connection.
RemoteAddr net.Addr
// LocalAddr is the local address of the corresponding connection.
LocalAddr net.Addr
// Compression is the compression algorithm used for the RPC.
Compression string
}
// IsClient indicates if this is from client side.
func (s *InHeader) IsClient() bool { return s.Client }
// InTrailer contains stats when a trailer is received.
type InTrailer struct {
// Client is true if this InTrailer is from client side.
Client bool
// WireLength is the wire length of trailer.
WireLength int
}
// IsClient indicates if this is from client side.
func (s *InTrailer) IsClient() bool { return s.Client }
// OutPayload contains the information for an outgoing payload.
type OutPayload struct {
// Client is true if this OutPayload is from client side.
Client bool
// Payload is the payload with original type.
Payload interface{}
// Data is the serialized message payload.
Data []byte
// Length is the length of uncompressed data.
Length int
// WireLength is the length of data on wire (compressed, signed, encrypted).
WireLength int
// SentTime is the time when the payload is sent.
SentTime time.Time
}
// IsClient indicates if this is from client side.
func (s *OutPayload) IsClient() bool { return s.Client }
// OutHeader contains stats when a header is sent.
// FullMethod, addresses and Compression are only valid if Client is true.
type OutHeader struct {
// Client is true if this OutHeader is from client side.
Client bool
// WireLength is the wire length of header.
WireLength int
// FullMethod is the full RPC method string, i.e., /package.service/method.
FullMethod string
// RemoteAddr is the remote address of the corresponding connection.
RemoteAddr net.Addr
// LocalAddr is the local address of the corresponding connection.
LocalAddr net.Addr
// Compression is the compression algorithm used for the RPC.
Compression string
}
// IsClient indicates if this is from client side.
func (s *OutHeader) IsClient() bool { return s.Client }
// OutTrailer contains stats when a trailer is sent.
type OutTrailer struct {
// Client is true if this OutTrailer is from client side.
Client bool
// WireLength is the wire length of trailer.
WireLength int
}
// IsClient indicates if this is from client side.
func (s *OutTrailer) IsClient() bool { return s.Client }
// End contains stats when an RPC ends.
type End struct {
// Client is true if this End is from client side.
Client bool
// EndTime is the time when the RPC ends.
EndTime time.Time
// Error is the error just happened. Its type is gRPC error.
Error error
}
// IsClient indicates if this is from client side.
func (s *End) IsClient() bool { return s.Client }
var (
on = new(int32)
handler func(context.Context, RPCStats)
)
// On indicates whether stats is started.
func On() bool {
return atomic.CompareAndSwapInt32(on, 1, 1)
}
// Handle processes the stats using the call back function registered by user.
func Handle(ctx context.Context, s RPCStats) {
handler(ctx, s)
}
// RegisterHandler registers the user handler function.
// If another handler was registered before, this new handler will overwrite the old one.
// This handler function will be called to process the stats.
func RegisterHandler(f func(context.Context, RPCStats)) {
handler = f
}
// Start starts the stats collection and reporting if there is a registered stats handle.
func Start() {
if handler == nil {
grpclog.Println("handler is nil when starting stats. Stats is not started")
return
}
atomic.StoreInt32(on, 1)
}
// Stop stops the stats collection and processing.
// Stop does not unregister handler.
func Stop() {
atomic.StoreInt32(on, 0)
}

View File

@ -45,6 +45,7 @@ import (
"golang.org/x/net/trace" "golang.org/x/net/trace"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/stats"
"google.golang.org/grpc/transport" "google.golang.org/grpc/transport"
) )
@ -97,7 +98,7 @@ type ClientStream interface {
// NewClientStream creates a new Stream for the client side. This is called // NewClientStream creates a new Stream for the client side. This is called
// by generated code. // by generated code.
func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error) { func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) {
if cc.dopts.streamInt != nil { if cc.dopts.streamInt != nil {
return cc.dopts.streamInt(ctx, desc, cc, method, newClientStream, opts...) return cc.dopts.streamInt(ctx, desc, cc, method, newClientStream, opts...)
} }
@ -143,6 +144,24 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
} }
}() }()
} }
if stats.On() {
begin := &stats.Begin{
Client: true,
BeginTime: time.Now(),
FailFast: c.failFast,
}
stats.Handle(ctx, begin)
}
defer func() {
if err != nil && stats.On() {
// Only handle end stats if err != nil.
end := &stats.End{
Client: true,
Error: err,
}
stats.Handle(ctx, end)
}
}()
gopts := BalancerGetOptions{ gopts := BalancerGetOptions{
BlockingWait: !c.failFast, BlockingWait: !c.failFast,
} }
@ -194,6 +213,8 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
tracing: EnableTracing, tracing: EnableTracing,
trInfo: trInfo, trInfo: trInfo,
statsCtx: ctx,
} }
if cc.dopts.cp != nil { if cc.dopts.cp != nil {
cs.cbuf = new(bytes.Buffer) cs.cbuf = new(bytes.Buffer)
@ -246,6 +267,11 @@ type clientStream struct {
// trInfo.tr is set when the clientStream is created (if EnableTracing is true), // trInfo.tr is set when the clientStream is created (if EnableTracing is true),
// and is set to nil when the clientStream's finish method is called. // and is set to nil when the clientStream's finish method is called.
trInfo traceInfo trInfo traceInfo
// statsCtx keeps the user context for stats handling.
// All stats collection should use the statsCtx (instead of the stream context)
// so that all the generated stats for a particular RPC can be associated in the processing phase.
statsCtx context.Context
} }
func (cs *clientStream) Context() context.Context { func (cs *clientStream) Context() context.Context {
@ -274,6 +300,8 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
} }
cs.mu.Unlock() cs.mu.Unlock()
} }
// TODO Investigate how to signal the stats handling party.
// generate error stats if err != nil && err != io.EOF?
defer func() { defer func() {
if err != nil { if err != nil {
cs.finish(err) cs.finish(err)
@ -296,7 +324,13 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
} }
err = toRPCErr(err) err = toRPCErr(err)
}() }()
out, err := encode(cs.codec, m, cs.cp, cs.cbuf) var outPayload *stats.OutPayload
if stats.On() {
outPayload = &stats.OutPayload{
Client: true,
}
}
out, err := encode(cs.codec, m, cs.cp, cs.cbuf, outPayload)
defer func() { defer func() {
if cs.cbuf != nil { if cs.cbuf != nil {
cs.cbuf.Reset() cs.cbuf.Reset()
@ -305,11 +339,37 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
if err != nil { if err != nil {
return Errorf(codes.Internal, "grpc: %v", err) return Errorf(codes.Internal, "grpc: %v", err)
} }
return cs.t.Write(cs.s, out, &transport.Options{Last: false}) err = cs.t.Write(cs.s, out, &transport.Options{Last: false})
if err == nil && outPayload != nil {
outPayload.SentTime = time.Now()
stats.Handle(cs.statsCtx, outPayload)
}
return err
} }
func (cs *clientStream) RecvMsg(m interface{}) (err error) { func (cs *clientStream) RecvMsg(m interface{}) (err error) {
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32) defer func() {
if err != nil && stats.On() {
// Only generate End if err != nil.
// If err == nil, it's not the last RecvMsg.
// The last RecvMsg gets either an RPC error or io.EOF.
end := &stats.End{
Client: true,
EndTime: time.Now(),
}
if err != io.EOF {
end.Error = toRPCErr(err)
}
stats.Handle(cs.statsCtx, end)
}
}()
var inPayload *stats.InPayload
if stats.On() {
inPayload = &stats.InPayload{
Client: true,
}
}
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32, inPayload)
defer func() { defer func() {
// err != nil indicates the termination of the stream. // err != nil indicates the termination of the stream.
if err != nil { if err != nil {
@ -324,11 +384,15 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
} }
cs.mu.Unlock() cs.mu.Unlock()
} }
if inPayload != nil {
stats.Handle(cs.statsCtx, inPayload)
}
if !cs.desc.ClientStreams || cs.desc.ServerStreams { if !cs.desc.ClientStreams || cs.desc.ServerStreams {
return return
} }
// Special handling for client streaming rpc. // Special handling for client streaming rpc.
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32) // This recv expects EOF or errors, so we don't collect inPayload.
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32, nil)
cs.closeTransportStream(err) cs.closeTransportStream(err)
if err == nil { if err == nil {
return toRPCErr(errors.New("grpc: client streaming protocol violation: get <nil>, want <EOF>")) return toRPCErr(errors.New("grpc: client streaming protocol violation: get <nil>, want <EOF>"))
@ -482,7 +546,11 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
ss.mu.Unlock() ss.mu.Unlock()
} }
}() }()
out, err := encode(ss.codec, m, ss.cp, ss.cbuf) var outPayload *stats.OutPayload
if stats.On() {
outPayload = &stats.OutPayload{}
}
out, err := encode(ss.codec, m, ss.cp, ss.cbuf, outPayload)
defer func() { defer func() {
if ss.cbuf != nil { if ss.cbuf != nil {
ss.cbuf.Reset() ss.cbuf.Reset()
@ -495,6 +563,10 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
if err := ss.t.Write(ss.s, out, &transport.Options{Last: false}); err != nil { if err := ss.t.Write(ss.s, out, &transport.Options{Last: false}); err != nil {
return toRPCErr(err) return toRPCErr(err)
} }
if outPayload != nil {
outPayload.SentTime = time.Now()
stats.Handle(ss.s.Context(), outPayload)
}
return nil return nil
} }
@ -513,7 +585,11 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
ss.mu.Unlock() ss.mu.Unlock()
} }
}() }()
if err := recv(ss.p, ss.codec, ss.s, ss.dc, m, ss.maxMsgSize); err != nil { var inPayload *stats.InPayload
if stats.On() {
inPayload = &stats.InPayload{}
}
if err := recv(ss.p, ss.codec, ss.s, ss.dc, m, ss.maxMsgSize, inPayload); err != nil {
if err == io.EOF { if err == io.EOF {
return err return err
} }
@ -522,5 +598,8 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
} }
return toRPCErr(err) return toRPCErr(err)
} }
if inPayload != nil {
stats.Handle(ss.s.Context(), inPayload)
}
return nil return nil
} }

54
vendor/google.golang.org/grpc/tap/tap.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
/*
*
* Copyright 2016, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package tap defines the function handles which are executed on the transport
// layer of gRPC-Go and related information. Everything here is EXPERIMENTAL.
package tap
import (
"golang.org/x/net/context"
)
// Info defines the relevant information needed by the handles.
type Info struct {
// FullMethodName is the string of grpc method (in the format of
// /package.service/method).
FullMethodName string
// TODO: More to be added.
}
// ServerInHandle defines the function which runs when a new stream is created
// on the server side. Note that it is executed in the per-connection I/O goroutine(s) instead
// of per-RPC goroutine. Therefore, users should NOT have any blocking/time-consuming
// work in this handle. Otherwise all the RPCs would slow down.
type ServerInHandle func(ctx context.Context, info *Info) (context.Context, error)

View File

@ -268,7 +268,7 @@ func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
}) })
} }
func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream)) { func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), traceCtx func(context.Context, string) context.Context) {
// With this transport type there will be exactly 1 stream: this HTTP request. // With this transport type there will be exactly 1 stream: this HTTP request.
var ctx context.Context var ctx context.Context

View File

@ -51,16 +51,19 @@ import (
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer" "google.golang.org/grpc/peer"
"google.golang.org/grpc/stats"
) )
// http2Client implements the ClientTransport interface with HTTP2. // http2Client implements the ClientTransport interface with HTTP2.
type http2Client struct { type http2Client struct {
target string // server name/addr target string // server name/addr
userAgent string userAgent string
md interface{} md interface{}
conn net.Conn // underlying communication channel conn net.Conn // underlying communication channel
authInfo credentials.AuthInfo // auth info about the connection remoteAddr net.Addr
nextID uint32 // the next stream ID to be used localAddr net.Addr
authInfo credentials.AuthInfo // auth info about the connection
nextID uint32 // the next stream ID to be used
// writableChan synchronizes write access to the transport. // writableChan synchronizes write access to the transport.
// A writer acquires the write lock by sending a value on writableChan // A writer acquires the write lock by sending a value on writableChan
@ -150,6 +153,9 @@ func newHTTP2Client(ctx context.Context, addr TargetInfo, opts ConnectOptions) (
scheme := "http" scheme := "http"
conn, err := dial(ctx, opts.Dialer, addr.Addr) conn, err := dial(ctx, opts.Dialer, addr.Addr)
if err != nil { if err != nil {
if opts.FailOnNonTempDialError {
return nil, connectionErrorf(isTemporary(err), err, "transport: %v", err)
}
return nil, connectionErrorf(true, err, "transport: %v", err) return nil, connectionErrorf(true, err, "transport: %v", err)
} }
// Any further errors will close the underlying connection // Any further errors will close the underlying connection
@ -175,11 +181,13 @@ func newHTTP2Client(ctx context.Context, addr TargetInfo, opts ConnectOptions) (
} }
var buf bytes.Buffer var buf bytes.Buffer
t := &http2Client{ t := &http2Client{
target: addr.Addr, target: addr.Addr,
userAgent: ua, userAgent: ua,
md: addr.Metadata, md: addr.Metadata,
conn: conn, conn: conn,
authInfo: authInfo, remoteAddr: conn.RemoteAddr(),
localAddr: conn.LocalAddr(),
authInfo: authInfo,
// The client initiated stream id is odd starting from 1. // The client initiated stream id is odd starting from 1.
nextID: 1, nextID: 1,
writableChan: make(chan int, 1), writableChan: make(chan int, 1),
@ -270,12 +278,13 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
// streams. // streams.
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) { func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
pr := &peer.Peer{ pr := &peer.Peer{
Addr: t.conn.RemoteAddr(), Addr: t.remoteAddr,
} }
// Attach Auth info if there is any. // Attach Auth info if there is any.
if t.authInfo != nil { if t.authInfo != nil {
pr.AuthInfo = t.authInfo pr.AuthInfo = t.authInfo
} }
userCtx := ctx
ctx = peer.NewContext(ctx, pr) ctx = peer.NewContext(ctx, pr)
authData := make(map[string]string) authData := make(map[string]string)
for _, c := range t.creds { for _, c := range t.creds {
@ -347,6 +356,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
return nil, ErrConnClosing return nil, ErrConnClosing
} }
s := t.newStream(ctx, callHdr) s := t.newStream(ctx, callHdr)
s.clientStatsCtx = userCtx
t.activeStreams[s.id] = s t.activeStreams[s.id] = s
// This stream is not counted when applySetings(...) initialize t.streamsQuota. // This stream is not counted when applySetings(...) initialize t.streamsQuota.
@ -413,6 +423,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
} }
} }
first := true first := true
bufLen := t.hBuf.Len()
// Sends the headers in a single batch even when they span multiple frames. // Sends the headers in a single batch even when they span multiple frames.
for !endHeaders { for !endHeaders {
size := t.hBuf.Len() size := t.hBuf.Len()
@ -447,6 +458,17 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
return nil, connectionErrorf(true, err, "transport: %v", err) return nil, connectionErrorf(true, err, "transport: %v", err)
} }
} }
if stats.On() {
outHeader := &stats.OutHeader{
Client: true,
WireLength: bufLen,
FullMethod: callHdr.Method,
RemoteAddr: t.remoteAddr,
LocalAddr: t.localAddr,
Compression: callHdr.SendCompress,
}
stats.Handle(s.clientStatsCtx, outHeader)
}
t.writableChan <- 0 t.writableChan <- 0
return s, nil return s, nil
} }
@ -874,6 +896,24 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
} }
endStream := frame.StreamEnded() endStream := frame.StreamEnded()
var isHeader bool
defer func() {
if stats.On() {
if isHeader {
inHeader := &stats.InHeader{
Client: true,
WireLength: int(frame.Header().Length),
}
stats.Handle(s.clientStatsCtx, inHeader)
} else {
inTrailer := &stats.InTrailer{
Client: true,
WireLength: int(frame.Header().Length),
}
stats.Handle(s.clientStatsCtx, inTrailer)
}
}
}()
s.mu.Lock() s.mu.Lock()
if !endStream { if !endStream {
@ -885,6 +925,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
} }
close(s.headerChan) close(s.headerChan)
s.headerDone = true s.headerDone = true
isHeader = true
} }
if !endStream || s.state == streamDone { if !endStream || s.state == streamDone {
s.mu.Unlock() s.mu.Unlock()

View File

@ -50,6 +50,8 @@ import (
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer" "google.golang.org/grpc/peer"
"google.golang.org/grpc/stats"
"google.golang.org/grpc/tap"
) )
// ErrIllegalHeaderWrite indicates that setting header is illegal because of // ErrIllegalHeaderWrite indicates that setting header is illegal because of
@ -59,8 +61,11 @@ var ErrIllegalHeaderWrite = errors.New("transport: the stream is done or WriteHe
// http2Server implements the ServerTransport interface with HTTP2. // http2Server implements the ServerTransport interface with HTTP2.
type http2Server struct { type http2Server struct {
conn net.Conn conn net.Conn
remoteAddr net.Addr
localAddr net.Addr
maxStreamID uint32 // max stream ID ever seen maxStreamID uint32 // max stream ID ever seen
authInfo credentials.AuthInfo // auth info about the connection authInfo credentials.AuthInfo // auth info about the connection
inTapHandle tap.ServerInHandle
// writableChan synchronizes write access to the transport. // writableChan synchronizes write access to the transport.
// A writer acquires the write lock by receiving a value on writableChan // A writer acquires the write lock by receiving a value on writableChan
// and releases it by sending on writableChan. // and releases it by sending on writableChan.
@ -91,12 +96,13 @@ type http2Server struct {
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is // newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
// returned if something goes wrong. // returned if something goes wrong.
func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo) (_ ServerTransport, err error) { func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err error) {
framer := newFramer(conn) framer := newFramer(conn)
// Send initial settings as connection preface to client. // Send initial settings as connection preface to client.
var settings []http2.Setting var settings []http2.Setting
// TODO(zhaoq): Have a better way to signal "no limit" because 0 is // TODO(zhaoq): Have a better way to signal "no limit" because 0 is
// permitted in the HTTP2 spec. // permitted in the HTTP2 spec.
maxStreams := config.MaxStreams
if maxStreams == 0 { if maxStreams == 0 {
maxStreams = math.MaxUint32 maxStreams = math.MaxUint32
} else { } else {
@ -122,11 +128,14 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI
var buf bytes.Buffer var buf bytes.Buffer
t := &http2Server{ t := &http2Server{
conn: conn, conn: conn,
authInfo: authInfo, remoteAddr: conn.RemoteAddr(),
localAddr: conn.LocalAddr(),
authInfo: config.AuthInfo,
framer: framer, framer: framer,
hBuf: &buf, hBuf: &buf,
hEnc: hpack.NewEncoder(&buf), hEnc: hpack.NewEncoder(&buf),
maxStreams: maxStreams, maxStreams: maxStreams,
inTapHandle: config.InTapHandle,
controlBuf: newRecvBuffer(), controlBuf: newRecvBuffer(),
fc: &inFlow{limit: initialConnWindowSize}, fc: &inFlow{limit: initialConnWindowSize},
sendQuotaPool: newQuotaPool(defaultWindowSize), sendQuotaPool: newQuotaPool(defaultWindowSize),
@ -142,7 +151,7 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI
} }
// operateHeader takes action on the decoded headers. // operateHeader takes action on the decoded headers.
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream)) (close bool) { func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) (close bool) {
buf := newRecvBuffer() buf := newRecvBuffer()
s := &Stream{ s := &Stream{
id: frame.Header().StreamID, id: frame.Header().StreamID,
@ -173,7 +182,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
s.ctx, s.cancel = context.WithCancel(context.TODO()) s.ctx, s.cancel = context.WithCancel(context.TODO())
} }
pr := &peer.Peer{ pr := &peer.Peer{
Addr: t.conn.RemoteAddr(), Addr: t.remoteAddr,
} }
// Attach Auth info if there is any. // Attach Auth info if there is any.
if t.authInfo != nil { if t.authInfo != nil {
@ -195,6 +204,18 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
} }
s.recvCompress = state.encoding s.recvCompress = state.encoding
s.method = state.method s.method = state.method
if t.inTapHandle != nil {
var err error
info := &tap.Info{
FullMethodName: state.method,
}
s.ctx, err = t.inTapHandle(s.ctx, info)
if err != nil {
// TODO: Log the real error.
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream})
return
}
}
t.mu.Lock() t.mu.Lock()
if t.state != reachable { if t.state != reachable {
t.mu.Unlock() t.mu.Unlock()
@ -218,13 +239,25 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
s.windowHandler = func(n int) { s.windowHandler = func(n int) {
t.updateWindow(s, uint32(n)) t.updateWindow(s, uint32(n))
} }
s.ctx = traceCtx(s.ctx, s.method)
if stats.On() {
inHeader := &stats.InHeader{
FullMethod: s.method,
RemoteAddr: t.remoteAddr,
LocalAddr: t.localAddr,
Compression: s.recvCompress,
WireLength: int(frame.Header().Length),
}
stats.Handle(s.ctx, inHeader)
}
handle(s) handle(s)
return return
} }
// HandleStreams receives incoming streams using the given handler. This is // HandleStreams receives incoming streams using the given handler. This is
// typically run in a separate goroutine. // typically run in a separate goroutine.
func (t *http2Server) HandleStreams(handle func(*Stream)) { // traceCtx attaches trace to ctx and returns the new context.
func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.Context, string) context.Context) {
// Check the validity of client preface. // Check the validity of client preface.
preface := make([]byte, len(clientPreface)) preface := make([]byte, len(clientPreface))
if _, err := io.ReadFull(t.conn, preface); err != nil { if _, err := io.ReadFull(t.conn, preface); err != nil {
@ -279,7 +312,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
} }
switch frame := frame.(type) { switch frame := frame.(type) {
case *http2.MetaHeadersFrame: case *http2.MetaHeadersFrame:
if t.operateHeaders(frame, handle) { if t.operateHeaders(frame, handle, traceCtx) {
t.Close() t.Close()
break break
} }
@ -492,9 +525,16 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry}) t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
} }
} }
bufLen := t.hBuf.Len()
if err := t.writeHeaders(s, t.hBuf, false); err != nil { if err := t.writeHeaders(s, t.hBuf, false); err != nil {
return err return err
} }
if stats.On() {
outHeader := &stats.OutHeader{
WireLength: bufLen,
}
stats.Handle(s.Context(), outHeader)
}
t.writableChan <- 0 t.writableChan <- 0
return nil return nil
} }
@ -547,10 +587,17 @@ func (t *http2Server) WriteStatus(s *Stream, statusCode codes.Code, statusDesc s
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry}) t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
} }
} }
bufLen := t.hBuf.Len()
if err := t.writeHeaders(s, t.hBuf, true); err != nil { if err := t.writeHeaders(s, t.hBuf, true); err != nil {
t.Close() t.Close()
return err return err
} }
if stats.On() {
outTrailer := &stats.OutTrailer{
WireLength: bufLen,
}
stats.Handle(s.Context(), outTrailer)
}
t.closeStream(s) t.closeStream(s)
t.writableChan <- 0 t.writableChan <- 0
return nil return nil
@ -767,7 +814,7 @@ func (t *http2Server) closeStream(s *Stream) {
} }
func (t *http2Server) RemoteAddr() net.Addr { func (t *http2Server) RemoteAddr() net.Addr {
return t.conn.RemoteAddr() return t.remoteAddr
} }
func (t *http2Server) Drain() { func (t *http2Server) Drain() {

View File

@ -45,10 +45,10 @@ import (
"sync" "sync"
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/net/trace"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/tap"
) )
// recvMsg represents the received msg from the transport. All transport // recvMsg represents the received msg from the transport. All transport
@ -167,6 +167,11 @@ type Stream struct {
id uint32 id uint32
// nil for client side Stream. // nil for client side Stream.
st ServerTransport st ServerTransport
// clientStatsCtx keeps the user context for stats handling.
// It's only valid on client side. Server side stats context is same as s.ctx.
// All client side stats collection should use the clientStatsCtx (instead of the stream context)
// so that all the generated stats for a particular RPC can be associated in the processing phase.
clientStatsCtx context.Context
// ctx is the associated context of the stream. // ctx is the associated context of the stream.
ctx context.Context ctx context.Context
// cancel is always nil for client side Stream. // cancel is always nil for client side Stream.
@ -266,11 +271,6 @@ func (s *Stream) Context() context.Context {
return s.ctx return s.ctx
} }
// TraceContext recreates the context of s with a trace.Trace.
func (s *Stream) TraceContext(tr trace.Trace) {
s.ctx = trace.NewContext(s.ctx, tr)
}
// Method returns the method for the stream. // Method returns the method for the stream.
func (s *Stream) Method() string { func (s *Stream) Method() string {
return s.method return s.method
@ -355,10 +355,17 @@ const (
draining draining
) )
// ServerConfig consists of all the configurations to establish a server transport.
type ServerConfig struct {
MaxStreams uint32
AuthInfo credentials.AuthInfo
InTapHandle tap.ServerInHandle
}
// NewServerTransport creates a ServerTransport with conn or non-nil error // NewServerTransport creates a ServerTransport with conn or non-nil error
// if it fails. // if it fails.
func NewServerTransport(protocol string, conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo) (ServerTransport, error) { func NewServerTransport(protocol string, conn net.Conn, config *ServerConfig) (ServerTransport, error) {
return newHTTP2Server(conn, maxStreams, authInfo) return newHTTP2Server(conn, config)
} }
// ConnectOptions covers all relevant options for communicating with the server. // ConnectOptions covers all relevant options for communicating with the server.
@ -367,6 +374,8 @@ type ConnectOptions struct {
UserAgent string UserAgent string
// Dialer specifies how to dial a network address. // Dialer specifies how to dial a network address.
Dialer func(context.Context, string) (net.Conn, error) Dialer func(context.Context, string) (net.Conn, error)
// FailOnNonTempDialError specifies if gRPC fails on non-temporary dial errors.
FailOnNonTempDialError bool
// PerRPCCredentials stores the PerRPCCredentials required to issue RPCs. // PerRPCCredentials stores the PerRPCCredentials required to issue RPCs.
PerRPCCredentials []credentials.PerRPCCredentials PerRPCCredentials []credentials.PerRPCCredentials
// TransportCredentials stores the Authenticator required to setup a client connection. // TransportCredentials stores the Authenticator required to setup a client connection.
@ -466,7 +475,7 @@ type ClientTransport interface {
// Write methods for a given Stream will be called serially. // Write methods for a given Stream will be called serially.
type ServerTransport interface { type ServerTransport interface {
// HandleStreams receives incoming streams using the given handler. // HandleStreams receives incoming streams using the given handler.
HandleStreams(func(*Stream)) HandleStreams(func(*Stream), func(context.Context, string) context.Context)
// WriteHeader sends the header metadata for the given stream. // WriteHeader sends the header metadata for the given stream.
// WriteHeader may not be called on all streams. // WriteHeader may not be called on all streams.

428
vendor/vendor.json vendored
View File

@ -19,16 +19,16 @@
"revision": "" "revision": ""
}, },
{ {
"checksumSHA1": "M30X+Wqn7AnUr1numUOkQRI7ET0=", "checksumSHA1": "Xt8qcx6rrE1w8HkRSoxJDlNxswA=",
"path": "github.com/Azure/azure-sdk-for-go/storage", "path": "github.com/Azure/azure-sdk-for-go/storage",
"revision": "bd73d950fa4440dae889bd9917bff7cef539f86e", "revision": "27ae5c8b5bc5d90ab0540b4c5d0f2632c8db8b57",
"revisionTime": "2016-10-28T18:31:11Z" "revisionTime": "2016-11-10T23:35:32Z"
}, },
{ {
"checksumSHA1": "eWlbQbXextN77vRiVuIBV6kSwuE=", "checksumSHA1": "9BfDgevpBgdiP4B6DT9SvcuVyLs=",
"path": "github.com/Jeffail/gabs", "path": "github.com/Jeffail/gabs",
"revision": "855034b6b7a3b7144977efcaefe72d2c64b0d039", "revision": "2a3aa15961d5fee6047b8151b67ac2f08ba2c48c",
"revisionTime": "2016-08-09T16:55:30Z" "revisionTime": "2016-11-16T21:39:02Z"
}, },
{ {
"checksumSHA1": "LLVyR2dAgkihu0+HdZF+JK0gMMs=", "checksumSHA1": "LLVyR2dAgkihu0+HdZF+JK0gMMs=",
@ -43,16 +43,16 @@
"revisionTime": "2015-08-30T18:26:16Z" "revisionTime": "2015-08-30T18:26:16Z"
}, },
{ {
"checksumSHA1": "d6798KSc0jDg2MHNxKdgyNfMK7A=", "checksumSHA1": "6HjBxlrYOUnFZo6jPok9ZfQZ/dM=",
"path": "github.com/armon/go-metrics", "path": "github.com/armon/go-metrics",
"revision": "3df31a1ada83e310c2e24b267c8e8b68836547b4", "revision": "97c69685293dce4c0a2d0b19535179bbc976e4d2",
"revisionTime": "2016-07-17T04:34:58Z" "revisionTime": "2016-11-05T01:02:38Z"
}, },
{ {
"checksumSHA1": "OmqT9Y1mAHvlAKeJh0jBHC9SH78=", "checksumSHA1": "OmqT9Y1mAHvlAKeJh0jBHC9SH78=",
"path": "github.com/armon/go-metrics/circonus", "path": "github.com/armon/go-metrics/circonus",
"revision": "3df31a1ada83e310c2e24b267c8e8b68836547b4", "revision": "97c69685293dce4c0a2d0b19535179bbc976e4d2",
"revisionTime": "2016-07-17T04:34:58Z" "revisionTime": "2016-11-05T01:02:38Z"
}, },
{ {
"checksumSHA1": "gNO0JNpLzYOdInGeq7HqMZUzx9M=", "checksumSHA1": "gNO0JNpLzYOdInGeq7HqMZUzx9M=",
@ -67,196 +67,196 @@
"revisionTime": "2016-10-01T16:31:30Z" "revisionTime": "2016-10-01T16:31:30Z"
}, },
{ {
"checksumSHA1": "QEah7lNQcDSLQinY80RVj5CrF3k=", "checksumSHA1": "zCEK2WbnAElGz7x27gMGEyNY19A=",
"path": "github.com/aws/aws-sdk-go/aws", "path": "github.com/aws/aws-sdk-go/aws",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "Y9W+4GimK4Fuxq+vyIskVYFRnX4=", "checksumSHA1": "Y9W+4GimK4Fuxq+vyIskVYFRnX4=",
"path": "github.com/aws/aws-sdk-go/aws/awserr", "path": "github.com/aws/aws-sdk-go/aws/awserr",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "+q4vdl3l1Wom8K1wfIpJ4jlFsbY=", "checksumSHA1": "+q4vdl3l1Wom8K1wfIpJ4jlFsbY=",
"path": "github.com/aws/aws-sdk-go/aws/awsutil", "path": "github.com/aws/aws-sdk-go/aws/awsutil",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "/232RBWA3KnT7U+wciPS2+wmvR0=", "checksumSHA1": "/232RBWA3KnT7U+wciPS2+wmvR0=",
"path": "github.com/aws/aws-sdk-go/aws/client", "path": "github.com/aws/aws-sdk-go/aws/client",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "ieAJ+Cvp/PKv1LpUEnUXpc3OI6E=", "checksumSHA1": "ieAJ+Cvp/PKv1LpUEnUXpc3OI6E=",
"path": "github.com/aws/aws-sdk-go/aws/client/metadata", "path": "github.com/aws/aws-sdk-go/aws/client/metadata",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "c1N3Loy3AS9zD+m5CzpPNAED39U=", "checksumSHA1": "c1N3Loy3AS9zD+m5CzpPNAED39U=",
"path": "github.com/aws/aws-sdk-go/aws/corehandlers", "path": "github.com/aws/aws-sdk-go/aws/corehandlers",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "zu5C95rmCZff6NYZb62lEaT5ibE=", "checksumSHA1": "zu5C95rmCZff6NYZb62lEaT5ibE=",
"path": "github.com/aws/aws-sdk-go/aws/credentials", "path": "github.com/aws/aws-sdk-go/aws/credentials",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "KQiUK/zr3mqnAXD7x/X55/iNme0=", "checksumSHA1": "u3GOAJLmdvbuNUeUEcZSEAOeL/0=",
"path": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds", "path": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "NUJUTWlc1sV8b7WjfiYc4JZbXl0=", "checksumSHA1": "NUJUTWlc1sV8b7WjfiYc4JZbXl0=",
"path": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds", "path": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "4Ipx+5xN0gso+cENC2MHMWmQlR4=", "checksumSHA1": "4Ipx+5xN0gso+cENC2MHMWmQlR4=",
"path": "github.com/aws/aws-sdk-go/aws/credentials/stscreds", "path": "github.com/aws/aws-sdk-go/aws/credentials/stscreds",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "DwhFsNluCFEwqzyp3hbJR3q2Wqs=", "checksumSHA1": "DwhFsNluCFEwqzyp3hbJR3q2Wqs=",
"path": "github.com/aws/aws-sdk-go/aws/defaults", "path": "github.com/aws/aws-sdk-go/aws/defaults",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "8E0fEBUJY/1lJOyVxzTxMGQGInk=", "checksumSHA1": "/EXbk/z2TWjWc1Hvb4QYs3Wmhb8=",
"path": "github.com/aws/aws-sdk-go/aws/ec2metadata", "path": "github.com/aws/aws-sdk-go/aws/ec2metadata",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "5Ac22YMTBmrX/CXaEIXzWljr8UY=", "checksumSHA1": "5Ac22YMTBmrX/CXaEIXzWljr8UY=",
"path": "github.com/aws/aws-sdk-go/aws/request", "path": "github.com/aws/aws-sdk-go/aws/request",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "eOo6evLMAxQfo7Qkc5/h5euN1Sw=", "checksumSHA1": "eOo6evLMAxQfo7Qkc5/h5euN1Sw=",
"path": "github.com/aws/aws-sdk-go/aws/session", "path": "github.com/aws/aws-sdk-go/aws/session",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "diXvBs1LRC0RJ9WK6sllWKdzC04=", "checksumSHA1": "9K5Bpjvynki7Req1reyD1W3/uWA=",
"path": "github.com/aws/aws-sdk-go/aws/signer/v4", "path": "github.com/aws/aws-sdk-go/aws/signer/v4",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "Esab5F8KswqkTdB4TtjSvZgs56k=", "checksumSHA1": "Esab5F8KswqkTdB4TtjSvZgs56k=",
"path": "github.com/aws/aws-sdk-go/private/endpoints", "path": "github.com/aws/aws-sdk-go/private/endpoints",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "wk7EyvDaHwb5qqoOP/4d3cV0708=", "checksumSHA1": "wk7EyvDaHwb5qqoOP/4d3cV0708=",
"path": "github.com/aws/aws-sdk-go/private/protocol", "path": "github.com/aws/aws-sdk-go/private/protocol",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "1QmQ3FqV37w0Zi44qv8pA1GeR0A=", "checksumSHA1": "1QmQ3FqV37w0Zi44qv8pA1GeR0A=",
"path": "github.com/aws/aws-sdk-go/private/protocol/ec2query", "path": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "pNeF0Ey7TfBArH5LBQhKOQXQbLY=", "checksumSHA1": "pNeF0Ey7TfBArH5LBQhKOQXQbLY=",
"path": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil", "path": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "R00RL5jJXRYq1iiK1+PGvMfvXyM=", "checksumSHA1": "R00RL5jJXRYq1iiK1+PGvMfvXyM=",
"path": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc", "path": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "ZqY5RWavBLWTo6j9xqdyBEaNFRk=", "checksumSHA1": "ZqY5RWavBLWTo6j9xqdyBEaNFRk=",
"path": "github.com/aws/aws-sdk-go/private/protocol/query", "path": "github.com/aws/aws-sdk-go/private/protocol/query",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "5xzix1R8prUyWxgLnzUQoxTsfik=", "checksumSHA1": "5xzix1R8prUyWxgLnzUQoxTsfik=",
"path": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil", "path": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "TW/7U+/8ormL7acf6z2rv2hDD+s=", "checksumSHA1": "mLxtfPJvWIHdYPRY0f19kFuJ3u4=",
"path": "github.com/aws/aws-sdk-go/private/protocol/rest", "path": "github.com/aws/aws-sdk-go/private/protocol/rest",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "ODo+ko8D6unAxZuN1jGzMcN4QCc=", "checksumSHA1": "ODo+ko8D6unAxZuN1jGzMcN4QCc=",
"path": "github.com/aws/aws-sdk-go/private/protocol/restxml", "path": "github.com/aws/aws-sdk-go/private/protocol/restxml",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "eUEkjyMPAuekKBE4ou+nM9tXEas=", "checksumSHA1": "eUEkjyMPAuekKBE4ou+nM9tXEas=",
"path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil", "path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "Eo9yODN5U99BK0pMzoqnBm7PCrY=", "checksumSHA1": "Eo9yODN5U99BK0pMzoqnBm7PCrY=",
"path": "github.com/aws/aws-sdk-go/private/waiter", "path": "github.com/aws/aws-sdk-go/private/waiter",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "E5qjR1pDa/V2LEhXP36kZH2w91o=", "checksumSHA1": "E5qjR1pDa/V2LEhXP36kZH2w91o=",
"path": "github.com/aws/aws-sdk-go/service/dynamodb", "path": "github.com/aws/aws-sdk-go/service/dynamodb",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "nNF0ucfWGzZz7PQVe9da0J7Di7w=", "checksumSHA1": "nNF0ucfWGzZz7PQVe9da0J7Di7w=",
"path": "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute", "path": "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "6h4tJ9wVtbYb9wG4srtUxyPoAYM=", "checksumSHA1": "6h4tJ9wVtbYb9wG4srtUxyPoAYM=",
"path": "github.com/aws/aws-sdk-go/service/ec2", "path": "github.com/aws/aws-sdk-go/service/ec2",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "hQquEMm59E2CwVwfBvKRTVzBj/8=", "checksumSHA1": "hQquEMm59E2CwVwfBvKRTVzBj/8=",
"path": "github.com/aws/aws-sdk-go/service/iam", "path": "github.com/aws/aws-sdk-go/service/iam",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "HtKiIAPKsBg2s1c5ytRkdZ/lqO8=", "checksumSHA1": "HtKiIAPKsBg2s1c5ytRkdZ/lqO8=",
"path": "github.com/aws/aws-sdk-go/service/s3", "path": "github.com/aws/aws-sdk-go/service/s3",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "ouwhxcAsIYQ6oJbMRdLW/Ys/iyg=", "checksumSHA1": "ouwhxcAsIYQ6oJbMRdLW/Ys/iyg=",
"path": "github.com/aws/aws-sdk-go/service/sts", "path": "github.com/aws/aws-sdk-go/service/sts",
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9", "revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
"revisionTime": "2016-11-01T23:30:21Z" "revisionTime": "2016-11-16T23:00:31Z"
}, },
{ {
"checksumSHA1": "Isa9x3nvIJ12hvgdvUUBty+yplU=", "checksumSHA1": "Isa9x3nvIJ12hvgdvUUBty+yplU=",
@ -291,44 +291,44 @@
{ {
"checksumSHA1": "vcncrPAdKKpLjl5O5lAdrFU6NOc=", "checksumSHA1": "vcncrPAdKKpLjl5O5lAdrFU6NOc=",
"path": "github.com/coreos/etcd/client", "path": "github.com/coreos/etcd/client",
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73", "revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
"revisionTime": "2016-11-02T17:34:52Z" "revisionTime": "2016-11-16T00:44:50Z"
}, },
{ {
"checksumSHA1": "ga3jt9r+dQBMSXG0gnpNcXp2xYA=", "checksumSHA1": "ga3jt9r+dQBMSXG0gnpNcXp2xYA=",
"path": "github.com/coreos/etcd/pkg/fileutil", "path": "github.com/coreos/etcd/pkg/fileutil",
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73", "revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
"revisionTime": "2016-11-02T17:34:52Z" "revisionTime": "2016-11-16T00:44:50Z"
}, },
{ {
"checksumSHA1": "mKIXx1kDwmVmdIpZ3pJtRBuUKso=", "checksumSHA1": "mKIXx1kDwmVmdIpZ3pJtRBuUKso=",
"path": "github.com/coreos/etcd/pkg/pathutil", "path": "github.com/coreos/etcd/pkg/pathutil",
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73", "revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
"revisionTime": "2016-11-02T17:34:52Z" "revisionTime": "2016-11-16T00:44:50Z"
}, },
{ {
"checksumSHA1": "rMyIh9PsSvPs6Yd+YgKITQzQJx8=", "checksumSHA1": "rMyIh9PsSvPs6Yd+YgKITQzQJx8=",
"path": "github.com/coreos/etcd/pkg/tlsutil", "path": "github.com/coreos/etcd/pkg/tlsutil",
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73", "revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
"revisionTime": "2016-11-02T17:34:52Z" "revisionTime": "2016-11-16T00:44:50Z"
}, },
{ {
"checksumSHA1": "985rsigkh9SZlDXm6qK6cBloQg0=", "checksumSHA1": "985rsigkh9SZlDXm6qK6cBloQg0=",
"path": "github.com/coreos/etcd/pkg/transport", "path": "github.com/coreos/etcd/pkg/transport",
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73", "revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
"revisionTime": "2016-11-02T17:34:52Z" "revisionTime": "2016-11-16T00:44:50Z"
}, },
{ {
"checksumSHA1": "gx1gJIMU6T0UNQ0bPZ/drQ8cpCI=", "checksumSHA1": "gx1gJIMU6T0UNQ0bPZ/drQ8cpCI=",
"path": "github.com/coreos/etcd/pkg/types", "path": "github.com/coreos/etcd/pkg/types",
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73", "revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
"revisionTime": "2016-11-02T17:34:52Z" "revisionTime": "2016-11-16T00:44:50Z"
}, },
{ {
"checksumSHA1": "d50/+u/LFlXvEV10HiEoXB9OsGg=", "checksumSHA1": "d50/+u/LFlXvEV10HiEoXB9OsGg=",
"path": "github.com/coreos/go-systemd/journal", "path": "github.com/coreos/go-systemd/journal",
"revision": "64d5cd7cb947834ef93874e82745c42ad6de4d0e", "revision": "48702e0da86bd25e76cfef347e2adeb434a0d0a6",
"revisionTime": "2016-11-02T17:07:22Z" "revisionTime": "2016-11-14T12:22:54Z"
}, },
{ {
"checksumSHA1": "wqEybhAfMhCq2uOu4bT737mqu6U=", "checksumSHA1": "wqEybhAfMhCq2uOu4bT737mqu6U=",
@ -391,40 +391,40 @@
"revisionTime": "2016-09-26T17:55:29Z" "revisionTime": "2016-09-26T17:55:29Z"
}, },
{ {
"checksumSHA1": "7Di9l3MwdZliLqFKCU9ql2s+Rnw=", "checksumSHA1": "W7OFi8RC+99JY3lVFkqL8dVhUT0=",
"path": "github.com/go-sql-driver/mysql", "path": "github.com/go-sql-driver/mysql",
"revision": "ce924a41eea897745442daaa1739089b0f3f561d", "revision": "665b83488b90b902ce0a305ef6652e599771cdf9",
"revisionTime": "2016-11-01T11:13:14Z" "revisionTime": "2016-11-16T07:07:42Z"
}, },
{ {
"checksumSHA1": "Ea5tZxe8OjKyrXAaw5FTrpxiBWw=", "checksumSHA1": "9dGSivFlYCZvic4tBgE0xtWF0x0=",
"path": "github.com/gocql/gocql", "path": "github.com/gocql/gocql",
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55", "revision": "47f897f054183a4c891f27b235f11221683982dc",
"revisionTime": "2016-11-01T09:30:54Z" "revisionTime": "2016-11-07T21:46:42Z"
}, },
{ {
"checksumSHA1": "Z3N6HDGWcvcNu0FloZRq54uO3h4=", "checksumSHA1": "Z3N6HDGWcvcNu0FloZRq54uO3h4=",
"path": "github.com/gocql/gocql/internal/lru", "path": "github.com/gocql/gocql/internal/lru",
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55", "revision": "47f897f054183a4c891f27b235f11221683982dc",
"revisionTime": "2016-11-01T09:30:54Z" "revisionTime": "2016-11-07T21:46:42Z"
}, },
{ {
"checksumSHA1": "ctK9mwZKnt/8dHxx2Ef6nZTljZs=", "checksumSHA1": "ctK9mwZKnt/8dHxx2Ef6nZTljZs=",
"path": "github.com/gocql/gocql/internal/murmur", "path": "github.com/gocql/gocql/internal/murmur",
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55", "revision": "47f897f054183a4c891f27b235f11221683982dc",
"revisionTime": "2016-11-01T09:30:54Z" "revisionTime": "2016-11-07T21:46:42Z"
}, },
{ {
"checksumSHA1": "tZQDfMMTKrYMXqen0zjJWLtOf1A=", "checksumSHA1": "tZQDfMMTKrYMXqen0zjJWLtOf1A=",
"path": "github.com/gocql/gocql/internal/streams", "path": "github.com/gocql/gocql/internal/streams",
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55", "revision": "47f897f054183a4c891f27b235f11221683982dc",
"revisionTime": "2016-11-01T09:30:54Z" "revisionTime": "2016-11-07T21:46:42Z"
}, },
{ {
"checksumSHA1": "SVXOQdpDBh0ihdZ5aIflgdA+Rpw=", "checksumSHA1": "QlFGL6EkbK8an+9M+EupFAKe4uM=",
"path": "github.com/golang/protobuf/proto", "path": "github.com/golang/protobuf/proto",
"revision": "98fa357170587e470c5f27d3c3ea0947b71eb455", "revision": "224aaba33b1ac32a92a165f27489409fb8133d08",
"revisionTime": "2016-10-12T20:53:35Z" "revisionTime": "2016-11-16T19:48:24Z"
}, },
{ {
"checksumSHA1": "W+E/2xXcE1GmJ0Qb784ald0Fn6I=", "checksumSHA1": "W+E/2xXcE1GmJ0Qb784ald0Fn6I=",
@ -433,10 +433,10 @@
"revisionTime": "2016-05-29T05:00:41Z" "revisionTime": "2016-05-29T05:00:41Z"
}, },
{ {
"checksumSHA1": "TSS9EqiOoJcVUMNb46zVngVrAO8=", "checksumSHA1": "5+cRIgrrCwJ/BhOG34jUcNyvDPI=",
"path": "github.com/google/go-github/github", "path": "github.com/google/go-github/github",
"revision": "f7fcf6f52ff94adf1cc0ded41e7768d2ad729972", "revision": "d4f1b2d029be1730fd349ca929cc9c0da4a27007",
"revisionTime": "2016-10-28T15:10:40Z" "revisionTime": "2016-11-14T19:06:42Z"
}, },
{ {
"checksumSHA1": "yyAzHoiVLu+xywYI2BDyRq6sOqE=", "checksumSHA1": "yyAzHoiVLu+xywYI2BDyRq6sOqE=",
@ -451,16 +451,16 @@
"revisionTime": "2016-01-25T11:53:50Z" "revisionTime": "2016-01-25T11:53:50Z"
}, },
{ {
"checksumSHA1": "LclVLJYrBi03PBjsVPpgoMbUDQ8=", "checksumSHA1": "DHgZ2o3MR8CjlrzkgrjlbyvK/cw=",
"path": "github.com/hashicorp/consul/api", "path": "github.com/hashicorp/consul/api",
"revision": "fb7c03cbb1b3fa71b0746e2f19c442c63e686382", "revision": "74cfcd301485b5dd658715f0282c4e5dff232fca",
"revisionTime": "2016-11-01T13:11:20Z" "revisionTime": "2016-11-16T15:58:24Z"
}, },
{ {
"checksumSHA1": "0DPAA2cTBjrCGgXaxXil0vILcFs=", "checksumSHA1": "0DPAA2cTBjrCGgXaxXil0vILcFs=",
"path": "github.com/hashicorp/consul/lib", "path": "github.com/hashicorp/consul/lib",
"revision": "fb7c03cbb1b3fa71b0746e2f19c442c63e686382", "revision": "74cfcd301485b5dd658715f0282c4e5dff232fca",
"revisionTime": "2016-11-01T13:11:20Z" "revisionTime": "2016-11-16T15:58:24Z"
}, },
{ {
"checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=", "checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=",
@ -481,10 +481,10 @@
"revisionTime": "2015-05-18T23:42:57Z" "revisionTime": "2015-05-18T23:42:57Z"
}, },
{ {
"checksumSHA1": "KS9lmJV8Z7KHdtSIhbafQLU1hC4=", "checksumSHA1": "jplwF0kQyDhu2Wqq7cmZu0SHjOI=",
"path": "github.com/hashicorp/go-multierror", "path": "github.com/hashicorp/go-multierror",
"revision": "8c5f0ad9360406a3807ce7de6bc73269a91a6e51", "revision": "8484912a3b9987857bac52e0c5fec2b95f419628",
"revisionTime": "2016-08-11T01:57:21Z" "revisionTime": "2016-11-06T17:22:40Z"
}, },
{ {
"checksumSHA1": "ErJHGU6AVPZM9yoY/xV11TwSjQs=", "checksumSHA1": "ErJHGU6AVPZM9yoY/xV11TwSjQs=",
@ -525,56 +525,56 @@
{ {
"checksumSHA1": "8OPDk+bKyRGJoKcS4QNw9F7dpE8=", "checksumSHA1": "8OPDk+bKyRGJoKcS4QNw9F7dpE8=",
"path": "github.com/hashicorp/hcl", "path": "github.com/hashicorp/hcl",
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc", "revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
"revisionTime": "2016-11-01T18:00:25Z" "revisionTime": "2016-11-12T23:52:43Z"
}, },
{ {
"checksumSHA1": "XQmjDva9JCGGkIecOgwtBEMCJhU=", "checksumSHA1": "XQmjDva9JCGGkIecOgwtBEMCJhU=",
"path": "github.com/hashicorp/hcl/hcl/ast", "path": "github.com/hashicorp/hcl/hcl/ast",
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc", "revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
"revisionTime": "2016-11-01T18:00:25Z" "revisionTime": "2016-11-12T23:52:43Z"
}, },
{ {
"checksumSHA1": "croNloscHsjX87X+4/cKOURf1EY=", "checksumSHA1": "croNloscHsjX87X+4/cKOURf1EY=",
"path": "github.com/hashicorp/hcl/hcl/parser", "path": "github.com/hashicorp/hcl/hcl/parser",
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc", "revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
"revisionTime": "2016-11-01T18:00:25Z" "revisionTime": "2016-11-12T23:52:43Z"
}, },
{ {
"checksumSHA1": "lgR7PSAZ0RtvAc9OCtCnNsF/x8g=", "checksumSHA1": "lgR7PSAZ0RtvAc9OCtCnNsF/x8g=",
"path": "github.com/hashicorp/hcl/hcl/scanner", "path": "github.com/hashicorp/hcl/hcl/scanner",
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc", "revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
"revisionTime": "2016-11-01T18:00:25Z" "revisionTime": "2016-11-12T23:52:43Z"
}, },
{ {
"checksumSHA1": "JlZmnzqdmFFyb1+2afLyR3BOE/8=", "checksumSHA1": "/e0ULfQnGeUKiM1+iMnQhImo62k=",
"path": "github.com/hashicorp/hcl/hcl/strconv", "path": "github.com/hashicorp/hcl/hcl/strconv",
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc", "revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
"revisionTime": "2016-11-01T18:00:25Z" "revisionTime": "2016-11-12T23:52:43Z"
}, },
{ {
"checksumSHA1": "c6yprzj06ASwCo18TtbbNNBHljA=", "checksumSHA1": "c6yprzj06ASwCo18TtbbNNBHljA=",
"path": "github.com/hashicorp/hcl/hcl/token", "path": "github.com/hashicorp/hcl/hcl/token",
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc", "revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
"revisionTime": "2016-11-01T18:00:25Z" "revisionTime": "2016-11-12T23:52:43Z"
}, },
{ {
"checksumSHA1": "138aCV5n8n7tkGYMsMVQQnnLq+0=", "checksumSHA1": "138aCV5n8n7tkGYMsMVQQnnLq+0=",
"path": "github.com/hashicorp/hcl/json/parser", "path": "github.com/hashicorp/hcl/json/parser",
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc", "revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
"revisionTime": "2016-11-01T18:00:25Z" "revisionTime": "2016-11-12T23:52:43Z"
}, },
{ {
"checksumSHA1": "YdvFsNOMSWMLnY6fcliWQa0O5Fw=", "checksumSHA1": "YdvFsNOMSWMLnY6fcliWQa0O5Fw=",
"path": "github.com/hashicorp/hcl/json/scanner", "path": "github.com/hashicorp/hcl/json/scanner",
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc", "revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
"revisionTime": "2016-11-01T18:00:25Z" "revisionTime": "2016-11-12T23:52:43Z"
}, },
{ {
"checksumSHA1": "fNlXQCQEnb+B3k5UDL/r15xtSJY=", "checksumSHA1": "fNlXQCQEnb+B3k5UDL/r15xtSJY=",
"path": "github.com/hashicorp/hcl/json/token", "path": "github.com/hashicorp/hcl/json/token",
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc", "revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
"revisionTime": "2016-11-01T18:00:25Z" "revisionTime": "2016-11-12T23:52:43Z"
}, },
{ {
"checksumSHA1": "qnlqWJYV81ENr61SZk9c65R1mDo=", "checksumSHA1": "qnlqWJYV81ENr61SZk9c65R1mDo=",
@ -597,8 +597,8 @@
{ {
"checksumSHA1": "E3Xcanc9ouQwL+CZGOUyA/+giLg=", "checksumSHA1": "E3Xcanc9ouQwL+CZGOUyA/+giLg=",
"path": "github.com/hashicorp/serf/coordinate", "path": "github.com/hashicorp/serf/coordinate",
"revision": "e0680ca729b018b791af1add413faebe3e1c147e", "revision": "f679d7594a349263f6118db40d87122d3a474e7d",
"revisionTime": "2016-10-31T21:31:47Z" "revisionTime": "2016-11-09T18:57:27Z"
}, },
{ {
"checksumSHA1": "ZhK6IO2XN81Y+3RAjTcVm1Ic7oU=", "checksumSHA1": "ZhK6IO2XN81Y+3RAjTcVm1Ic7oU=",
@ -673,22 +673,22 @@
"revisionTime": "2016-10-04T15:35:44Z" "revisionTime": "2016-10-04T15:35:44Z"
}, },
{ {
"checksumSHA1": "T6yD9DYjn6yO+Kz70bO+sDj20oE=", "checksumSHA1": "avqi4lkviHdrNJ92cXCwrw9x870=",
"path": "github.com/lib/pq", "path": "github.com/lib/pq",
"revision": "a37edb86214894fa6c6c3401a4c4976b02176dd3", "revision": "d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b",
"revisionTime": "2016-11-02T07:48:14Z" "revisionTime": "2016-11-03T02:43:54Z"
}, },
{ {
"checksumSHA1": "xppHi82MLqVx1eyQmbhTesAEjx8=", "checksumSHA1": "xppHi82MLqVx1eyQmbhTesAEjx8=",
"path": "github.com/lib/pq/oid", "path": "github.com/lib/pq/oid",
"revision": "a37edb86214894fa6c6c3401a4c4976b02176dd3", "revision": "d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b",
"revisionTime": "2016-11-02T07:48:14Z" "revisionTime": "2016-11-03T02:43:54Z"
}, },
{ {
"checksumSHA1": "WFbWfoslftbEoKx6ZNVTMcU0EzA=", "checksumSHA1": "I4njd26dG5hxFT2nawuByM4pxzY=",
"path": "github.com/mattn/go-colorable", "path": "github.com/mattn/go-colorable",
"revision": "6e26b354bd2b0fc420cb632b0d878abccdc6544c", "revision": "d228849504861217f796da67fae4f6e347643f15",
"revisionTime": "2016-11-02T08:09:25Z" "revisionTime": "2016-11-03T16:00:40Z"
}, },
{ {
"checksumSHA1": "xZuhljnmBysJPta/lMyYmJdujCg=", "checksumSHA1": "xZuhljnmBysJPta/lMyYmJdujCg=",
@ -853,160 +853,172 @@
"revisionTime": "2016-10-31T15:37:30Z" "revisionTime": "2016-10-31T15:37:30Z"
}, },
{ {
"checksumSHA1": "4hQNaJUg67lF/QcO0NKzUeqlaew=", "checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=",
"path": "golang.org/x/net/context", "path": "golang.org/x/net/context",
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95", "revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
"revisionTime": "2016-10-31T16:36:42Z" "revisionTime": "2016-11-15T21:05:04Z"
}, },
{ {
"checksumSHA1": "r+7Ol7uTCa/i5W8pNej9M8xZxWg=", "checksumSHA1": "pLsZUQhI8jm3W9R/4JO9D/L1cUA=",
"path": "golang.org/x/net/http2", "path": "golang.org/x/net/http2",
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95", "revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
"revisionTime": "2016-10-31T16:36:42Z" "revisionTime": "2016-11-15T21:05:04Z"
}, },
{ {
"checksumSHA1": "HzuGD7AwgC0p1az1WAQnEFnEk98=", "checksumSHA1": "HzuGD7AwgC0p1az1WAQnEFnEk98=",
"path": "golang.org/x/net/http2/hpack", "path": "golang.org/x/net/http2/hpack",
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95", "revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
"revisionTime": "2016-10-31T16:36:42Z" "revisionTime": "2016-11-15T21:05:04Z"
}, },
{ {
"checksumSHA1": "GIGmSrYACByf5JDIP9ByBZksY80=", "checksumSHA1": "GIGmSrYACByf5JDIP9ByBZksY80=",
"path": "golang.org/x/net/idna", "path": "golang.org/x/net/idna",
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95", "revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
"revisionTime": "2016-10-31T16:36:42Z" "revisionTime": "2016-11-15T21:05:04Z"
}, },
{ {
"checksumSHA1": "/k7k6eJDkxXx6K9Zpo/OwNm58XM=", "checksumSHA1": "/k7k6eJDkxXx6K9Zpo/OwNm58XM=",
"path": "golang.org/x/net/internal/timeseries", "path": "golang.org/x/net/internal/timeseries",
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95", "revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
"revisionTime": "2016-10-31T16:36:42Z" "revisionTime": "2016-11-15T21:05:04Z"
}, },
{ {
"checksumSHA1": "3xyuaSNmClqG4YWC7g0isQIbUTc=", "checksumSHA1": "3xyuaSNmClqG4YWC7g0isQIbUTc=",
"path": "golang.org/x/net/lex/httplex", "path": "golang.org/x/net/lex/httplex",
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95", "revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
"revisionTime": "2016-10-31T16:36:42Z" "revisionTime": "2016-11-15T21:05:04Z"
}, },
{ {
"checksumSHA1": "4MMbG0LI3ghvWooRn36RmDrFIB0=", "checksumSHA1": "P9qTIn8a6L6Q9wd1IJBCuhno1Q8=",
"path": "golang.org/x/net/trace", "path": "golang.org/x/net/trace",
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95", "revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
"revisionTime": "2016-10-31T16:36:42Z" "revisionTime": "2016-11-15T21:05:04Z"
}, },
{ {
"checksumSHA1": "XH7CgbL5Z8COUc+MKrYqS3FFosY=", "checksumSHA1": "XH7CgbL5Z8COUc+MKrYqS3FFosY=",
"path": "golang.org/x/oauth2", "path": "golang.org/x/oauth2",
"revision": "25b4fb1468cb89700c7c060cb99f30581a61f5e3", "revision": "d5040cddfc0da40b408c9a1da4728662435176a9",
"revisionTime": "2016-10-25T17:59:40Z" "revisionTime": "2016-11-03T22:50:36Z"
}, },
{ {
"checksumSHA1": "D3v/aqfB9swlaZcSksCoF+lbOqo=", "checksumSHA1": "D3v/aqfB9swlaZcSksCoF+lbOqo=",
"path": "golang.org/x/oauth2/internal", "path": "golang.org/x/oauth2/internal",
"revision": "25b4fb1468cb89700c7c060cb99f30581a61f5e3", "revision": "d5040cddfc0da40b408c9a1da4728662435176a9",
"revisionTime": "2016-10-25T17:59:40Z" "revisionTime": "2016-11-03T22:50:36Z"
}, },
{ {
"checksumSHA1": "aVgPDgwY3/t4J/JOw9H3FVMHqh0=", "checksumSHA1": "KqecwXo3OO+p4N+E9RhlHvl9I+w=",
"path": "golang.org/x/sys/unix", "path": "golang.org/x/sys/unix",
"revision": "c200b10b5d5e122be351b67af224adc6128af5bf", "revision": "b699b7032584f0953262cb2788a0ca19bb494703",
"revisionTime": "2016-10-22T18:22:21Z" "revisionTime": "2016-11-10T11:58:56Z"
}, },
{ {
"checksumSHA1": "vIZ71Qe81RHec1vNHpKG+CSx/es=", "checksumSHA1": "gYHoPrPncGO926bN0jr1rzDxBQU=",
"path": "google.golang.org/appengine/internal", "path": "google.golang.org/appengine/internal",
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad", "revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
"revisionTime": "2016-10-25T16:43:32Z" "revisionTime": "2016-11-15T22:01:06Z"
}, },
{ {
"checksumSHA1": "TsNO8P0xUlLNyh3Ic/tzSp/fDWM=", "checksumSHA1": "TsNO8P0xUlLNyh3Ic/tzSp/fDWM=",
"path": "google.golang.org/appengine/internal/base", "path": "google.golang.org/appengine/internal/base",
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad", "revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
"revisionTime": "2016-10-25T16:43:32Z" "revisionTime": "2016-11-15T22:01:06Z"
}, },
{ {
"checksumSHA1": "5QsV5oLGSfKZqTCVXP6NRz5T4Tw=", "checksumSHA1": "5QsV5oLGSfKZqTCVXP6NRz5T4Tw=",
"path": "google.golang.org/appengine/internal/datastore", "path": "google.golang.org/appengine/internal/datastore",
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad", "revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
"revisionTime": "2016-10-25T16:43:32Z" "revisionTime": "2016-11-15T22:01:06Z"
}, },
{ {
"checksumSHA1": "Gep2T9zmVYV8qZfK2gu3zrmG6QE=", "checksumSHA1": "Gep2T9zmVYV8qZfK2gu3zrmG6QE=",
"path": "google.golang.org/appengine/internal/log", "path": "google.golang.org/appengine/internal/log",
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad", "revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
"revisionTime": "2016-10-25T16:43:32Z" "revisionTime": "2016-11-15T22:01:06Z"
}, },
{ {
"checksumSHA1": "a1XY7rz3BieOVqVI2Et6rKiwQCk=", "checksumSHA1": "a1XY7rz3BieOVqVI2Et6rKiwQCk=",
"path": "google.golang.org/appengine/internal/remote_api", "path": "google.golang.org/appengine/internal/remote_api",
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad", "revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
"revisionTime": "2016-10-25T16:43:32Z" "revisionTime": "2016-11-15T22:01:06Z"
}, },
{ {
"checksumSHA1": "QtAbHtHmDzcf6vOV9eqlCpKgjiw=", "checksumSHA1": "QtAbHtHmDzcf6vOV9eqlCpKgjiw=",
"path": "google.golang.org/appengine/internal/urlfetch", "path": "google.golang.org/appengine/internal/urlfetch",
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad", "revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
"revisionTime": "2016-10-25T16:43:32Z" "revisionTime": "2016-11-15T22:01:06Z"
}, },
{ {
"checksumSHA1": "akOV9pYnCbcPA8wJUutSQVibdyg=", "checksumSHA1": "akOV9pYnCbcPA8wJUutSQVibdyg=",
"path": "google.golang.org/appengine/urlfetch", "path": "google.golang.org/appengine/urlfetch",
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad", "revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
"revisionTime": "2016-10-25T16:43:32Z" "revisionTime": "2016-11-15T22:01:06Z"
}, },
{ {
"checksumSHA1": "r2jmzn/o6RN6PT9FRRtBeAfgNEk=", "checksumSHA1": "xyB2Py2ViSKX8Td+oe2hxG6f0Ak=",
"path": "google.golang.org/grpc", "path": "google.golang.org/grpc",
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836", "revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-02T18:01:57Z" "revisionTime": "2016-11-15T20:54:09Z"
}, },
{ {
"checksumSHA1": "08icuA15HRkdYCt6H+Cs90RPQsY=", "checksumSHA1": "08icuA15HRkdYCt6H+Cs90RPQsY=",
"path": "google.golang.org/grpc/codes", "path": "google.golang.org/grpc/codes",
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836", "revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-02T18:01:57Z" "revisionTime": "2016-11-15T20:54:09Z"
}, },
{ {
"checksumSHA1": "Vd1MU+Ojs7GeS6jE52vlxtXvIrI=", "checksumSHA1": "Vd1MU+Ojs7GeS6jE52vlxtXvIrI=",
"path": "google.golang.org/grpc/credentials", "path": "google.golang.org/grpc/credentials",
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836", "revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-02T18:01:57Z" "revisionTime": "2016-11-15T20:54:09Z"
}, },
{ {
"checksumSHA1": "3Lt5hNAG8qJAYSsNghR5uA1zQns=", "checksumSHA1": "3Lt5hNAG8qJAYSsNghR5uA1zQns=",
"path": "google.golang.org/grpc/grpclog", "path": "google.golang.org/grpc/grpclog",
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836", "revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-02T18:01:57Z" "revisionTime": "2016-11-15T20:54:09Z"
}, },
{ {
"checksumSHA1": "T3Q0p8kzvXFnRkMaK/G8mCv6mc0=", "checksumSHA1": "T3Q0p8kzvXFnRkMaK/G8mCv6mc0=",
"path": "google.golang.org/grpc/internal", "path": "google.golang.org/grpc/internal",
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836", "revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-02T18:01:57Z" "revisionTime": "2016-11-15T20:54:09Z"
}, },
{ {
"checksumSHA1": "P64GkSdsTZ8Nxop5HYqZJ6e+iHs=", "checksumSHA1": "P64GkSdsTZ8Nxop5HYqZJ6e+iHs=",
"path": "google.golang.org/grpc/metadata", "path": "google.golang.org/grpc/metadata",
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836", "revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-02T18:01:57Z" "revisionTime": "2016-11-15T20:54:09Z"
}, },
{ {
"checksumSHA1": "4GSUFhOQ0kdFlBH4D5OTeKy78z0=", "checksumSHA1": "4GSUFhOQ0kdFlBH4D5OTeKy78z0=",
"path": "google.golang.org/grpc/naming", "path": "google.golang.org/grpc/naming",
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836", "revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-02T18:01:57Z" "revisionTime": "2016-11-15T20:54:09Z"
}, },
{ {
"checksumSHA1": "3RRoLeH6X2//7tVClOVzxW2bY+E=", "checksumSHA1": "3RRoLeH6X2//7tVClOVzxW2bY+E=",
"path": "google.golang.org/grpc/peer", "path": "google.golang.org/grpc/peer",
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836", "revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-02T18:01:57Z" "revisionTime": "2016-11-15T20:54:09Z"
}, },
{ {
"checksumSHA1": "HQJrtiTtr5eiRsXQLut2R1Q9kuY=", "checksumSHA1": "FCgy+WB249Vt1XEG5pe4Z7plTLs=",
"path": "google.golang.org/grpc/stats",
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-15T20:54:09Z"
},
{
"checksumSHA1": "N0TftT6/CyWqp6VRi2DqDx60+Fo=",
"path": "google.golang.org/grpc/tap",
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-15T20:54:09Z"
},
{
"checksumSHA1": "uVGwQAu6ncFK7qd3BpzbMEJDUP4=",
"path": "google.golang.org/grpc/transport", "path": "google.golang.org/grpc/transport",
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836", "revision": "941cc894cea3c87a12943fd12b594964541b6d28",
"revisionTime": "2016-11-02T18:01:57Z" "revisionTime": "2016-11-15T20:54:09Z"
}, },
{ {
"checksumSHA1": "wSu8owMAP7GixsYoSZ4CmKUVhnU=", "checksumSHA1": "wSu8owMAP7GixsYoSZ4CmKUVhnU=",