Bump deps
This commit is contained in:
parent
6b5327a04d
commit
ddb9a0ce52
|
@ -6,6 +6,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
@ -301,6 +302,65 @@ const (
|
|||
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
|
||||
const (
|
||||
MaxBlobBlockSize = 4 * 1024 * 1024
|
||||
|
@ -416,7 +476,7 @@ func (b BlobStorageClient) createContainer(name string, access ContainerAccessTy
|
|||
|
||||
headers := b.client.getStandardHeaders()
|
||||
if access != "" {
|
||||
headers["x-ms-blob-public-access"] = string(access)
|
||||
headers[ContainerAccessHeader] = string(access)
|
||||
}
|
||||
return b.client.exec(verb, uri, headers, nil)
|
||||
}
|
||||
|
@ -438,6 +498,101 @@ func (b BlobStorageClient) ContainerExists(name string) (bool, error) {
|
|||
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
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
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 {
|
||||
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{})
|
||||
|
||||
headers := b.client.getStandardHeaders()
|
||||
|
@ -1137,7 +1295,39 @@ func (b BlobStorageClient) startBlobCopy(container, name, sourceBlob string) (st
|
|||
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 {
|
||||
props, err := b.GetBlobProperties(container, name)
|
||||
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
|
||||
func (b BlobStorageClient) DeleteBlobIfExists(container, name string, extraHeaders map[string]string) (bool, error) {
|
||||
resp, err := b.deleteBlob(container, name, extraHeaders)
|
||||
if resp != nil && (resp.statusCode == http.StatusAccepted || resp.statusCode == http.StatusNotFound) {
|
||||
return resp.statusCode == http.StatusAccepted, nil
|
||||
if resp != 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
|
||||
}
|
||||
|
||||
|
@ -1210,17 +1402,18 @@ func pathForBlob(container, name string) string {
|
|||
return fmt.Sprintf("/%s/%s", container, name)
|
||||
}
|
||||
|
||||
// GetBlobSASURI creates an URL to the specified blob which contains the Shared
|
||||
// Access Signature with specified permissions and expiration time.
|
||||
// GetBlobSASURIWithSignedIPAndProtocol creates an URL to the specified blob which contains the Shared
|
||||
// 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
|
||||
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 (
|
||||
signedPermissions = permissions
|
||||
blobURL = b.GetBlobURL(container, name)
|
||||
)
|
||||
canonicalizedResource, err := b.client.buildCanonicalizedResource(blobURL)
|
||||
|
||||
if err != nil {
|
||||
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).
|
||||
canonicalizedResource = strings.Replace(canonicalizedResource, "+", "%2b", -1)
|
||||
|
||||
canonicalizedResource, err = url.QueryUnescape(canonicalizedResource)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -1241,7 +1433,11 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
|
|||
signedExpiry := expiry.UTC().Format(time.RFC3339)
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
|
@ -1255,6 +1451,13 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
|
|||
"sig": {sig},
|
||||
}
|
||||
|
||||
if b.client.apiVersion >= "2015-04-05" {
|
||||
sasParams.Add("spr", protocols)
|
||||
if signedIPRange != "" {
|
||||
sasParams.Add("sip", signedIPRange)
|
||||
}
|
||||
}
|
||||
|
||||
sasURL, err := url.Parse(blobURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -1263,16 +1466,89 @@ func (b BlobStorageClient) GetBlobSASURI(container, name string, expiry time.Tim
|
|||
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
|
||||
|
||||
if signedVersion >= "2015-02-21" {
|
||||
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
|
||||
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 "", 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
|
||||
}
|
||||
|
|
|
@ -305,7 +305,7 @@ func (c Client) buildCanonicalizedResourceTable(uri string) (string, error) {
|
|||
cr := "/" + c.getCanonicalizedAccountName()
|
||||
|
||||
if len(u.Path) > 0 {
|
||||
cr += u.Path
|
||||
cr += u.EscapedPath()
|
||||
}
|
||||
|
||||
return cr, nil
|
||||
|
|
|
@ -82,6 +82,24 @@ func (p PeekMessagesParameters) getParameters() url.Values {
|
|||
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
|
||||
// operation.
|
||||
type GetMessagesResponse struct {
|
||||
|
@ -304,3 +322,23 @@ func (c QueueServiceClient) DeleteMessage(queue, messageID, popReceipt string) e
|
|||
defer resp.body.Close()
|
||||
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})
|
||||
}
|
||||
|
|
|
@ -31,8 +31,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
/*---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
// ErrOutOfBounds - Index out of bounds.
|
||||
|
@ -63,40 +62,30 @@ var (
|
|||
ErrInvalidBuffer = errors.New("input buffer contained invalid JSON")
|
||||
)
|
||||
|
||||
/*---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Container - an internal structure that holds a reference to the core interface map of the parsed
|
||||
json. Use this container to move context.
|
||||
*/
|
||||
// Container - an internal structure that holds a reference to the core interface map of the parsed
|
||||
// json. Use this container to move context.
|
||||
type Container struct {
|
||||
object interface{}
|
||||
}
|
||||
|
||||
/*
|
||||
Data - Return the contained data as an interface{}.
|
||||
*/
|
||||
// Data - Return the contained data as an interface{}.
|
||||
func (g *Container) Data() interface{} {
|
||||
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 {
|
||||
return g.Search(strings.Split(path, ".")...)
|
||||
}
|
||||
|
||||
/*
|
||||
Search - Attempt to find and return an object within the JSON structure by specifying the hierarchy
|
||||
of field names to locate the target. If the search encounters an array and has not reached the end
|
||||
target then it will iterate each object of the array for the target and return all of the results in
|
||||
a JSON array.
|
||||
*/
|
||||
// Search - Attempt to find and return an object within the JSON structure by specifying the
|
||||
// hierarchy of field names to locate the target. If the search encounters an array and has not
|
||||
// reached the end target then it will iterate each object of the array for the target and return
|
||||
// all of the results in a JSON array.
|
||||
func (g *Container) Search(hierarchy ...string) *Container {
|
||||
var object interface{}
|
||||
|
||||
|
@ -124,31 +113,22 @@ func (g *Container) Search(hierarchy ...string) *Container {
|
|||
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 {
|
||||
return g.Search(hierarchy...)
|
||||
}
|
||||
|
||||
/*
|
||||
Exists - Checks whether a path exists.
|
||||
*/
|
||||
// Exists - Checks whether a path exists.
|
||||
func (g *Container) Exists(hierarchy ...string) bool {
|
||||
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 {
|
||||
return g.Exists(strings.Split(path, ".")...)
|
||||
}
|
||||
|
||||
/*
|
||||
Index - Attempt to find and return an object with a JSON array by specifying the index of the
|
||||
target.
|
||||
*/
|
||||
// Index - Attempt to find and return an object within a JSON array by index.
|
||||
func (g *Container) Index(index int) *Container {
|
||||
if array, ok := g.Data().([]interface{}); ok {
|
||||
if index >= len(array) {
|
||||
|
@ -159,11 +139,9 @@ func (g *Container) Index(index int) *Container {
|
|||
return &Container{nil}
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
objects this way.
|
||||
*/
|
||||
// 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
|
||||
// objects this way.
|
||||
func (g *Container) Children() ([]*Container, error) {
|
||||
if array, ok := g.Data().([]interface{}); ok {
|
||||
children := make([]*Container, len(array))
|
||||
|
@ -182,9 +160,7 @@ func (g *Container) Children() ([]*Container, error) {
|
|||
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) {
|
||||
if mmap, ok := g.Data().(map[string]interface{}); ok {
|
||||
children := map[string]*Container{}
|
||||
|
@ -196,14 +172,11 @@ func (g *Container) ChildrenMap() (map[string]*Container, error) {
|
|||
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
|
||||
constructed, and if a collision occurs with a non object type whilst iterating the path an error is
|
||||
returned.
|
||||
*/
|
||||
// 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
|
||||
// is returned.
|
||||
func (g *Container) Set(value interface{}, path ...string) (*Container, error) {
|
||||
if len(path) == 0 {
|
||||
g.object = value
|
||||
|
@ -229,16 +202,12 @@ func (g *Container) Set(value interface{}, path ...string) (*Container, error) {
|
|||
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) {
|
||||
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) {
|
||||
if array, ok := g.Data().([]interface{}); ok {
|
||||
if index >= len(array) {
|
||||
|
@ -250,80 +219,60 @@ func (g *Container) SetIndex(value interface{}, index int) (*Container, error) {
|
|||
return &Container{nil}, ErrNotArray
|
||||
}
|
||||
|
||||
/*
|
||||
Object - Create a new JSON object at a path. Returns an error if the path contains a collision with
|
||||
a non object type.
|
||||
*/
|
||||
// Object - Create a new JSON object at a path. Returns an error if the path contains a collision
|
||||
// with a non object type.
|
||||
func (g *Container) Object(path ...string) (*Container, error) {
|
||||
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) {
|
||||
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 array
|
||||
or the index is out of bounds.
|
||||
*/
|
||||
// ObjectI - Create a new JSON object at an array index. Returns an error if the object is not an
|
||||
// array or the index is out of bounds.
|
||||
func (g *Container) ObjectI(index int) (*Container, error) {
|
||||
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
|
||||
a non object type.
|
||||
*/
|
||||
// Array - Create a new JSON array at a path. Returns an error if the path contains a collision with
|
||||
// a non object type.
|
||||
func (g *Container) Array(path ...string) (*Container, error) {
|
||||
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) {
|
||||
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 array
|
||||
or the index is out of bounds.
|
||||
*/
|
||||
// ArrayI - Create a new JSON array at an array index. Returns an error if the object is not an
|
||||
// array or the index is out of bounds.
|
||||
func (g *Container) ArrayI(index int) (*Container, error) {
|
||||
return g.SetIndex([]interface{}{}, index)
|
||||
}
|
||||
|
||||
/*
|
||||
ArrayOfSize - Create a new JSON array of a particular size at a path. Returns an error if the path
|
||||
contains a collision with a non object type.
|
||||
*/
|
||||
// ArrayOfSize - Create a new JSON array of a particular size at a path. Returns an error if the
|
||||
// path contains a collision with a non object type.
|
||||
func (g *Container) ArrayOfSize(size int, path ...string) (*Container, error) {
|
||||
a := make([]interface{}, size)
|
||||
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) {
|
||||
return g.ArrayOfSize(size, strings.Split(path, ".")...)
|
||||
}
|
||||
|
||||
/*
|
||||
ArrayOfSizeI - Create a new JSON array of a particular size at an array index. Returns an error if
|
||||
the object is not an array or the index is out of bounds.
|
||||
*/
|
||||
// ArrayOfSizeI - Create a new JSON array of a particular size at an array index. Returns an error
|
||||
// if the object is not an array or the index is out of bounds.
|
||||
func (g *Container) ArrayOfSizeI(size, index int) (*Container, error) {
|
||||
a := make([]interface{}, size)
|
||||
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 {
|
||||
var object interface{}
|
||||
|
||||
|
@ -346,24 +295,19 @@ func (g *Container) Delete(path ...string) error {
|
|||
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 {
|
||||
return g.Delete(strings.Split(path, ".")...)
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
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 {
|
||||
array, ok := g.Search(path...).Data().([]interface{})
|
||||
if !ok {
|
||||
|
@ -374,16 +318,12 @@ func (g *Container) ArrayAppend(value interface{}, path ...string) error {
|
|||
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 {
|
||||
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 {
|
||||
if index < 0 {
|
||||
return ErrOutOfBounds
|
||||
|
@ -401,16 +341,12 @@ func (g *Container) ArrayRemove(index int, path ...string) error {
|
|||
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 {
|
||||
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) {
|
||||
if index < 0 {
|
||||
return &Container{nil}, ErrOutOfBounds
|
||||
|
@ -425,16 +361,12 @@ func (g *Container) ArrayElement(index int, path ...string) (*Container, error)
|
|||
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) {
|
||||
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) {
|
||||
if array, ok := g.Search(path...).Data().([]interface{}); ok {
|
||||
return len(array), nil
|
||||
|
@ -442,19 +374,14 @@ func (g *Container) ArrayCount(path ...string) (int, error) {
|
|||
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) {
|
||||
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 {
|
||||
if g.object != nil {
|
||||
if bytes, err := json.Marshal(g.object); err == nil {
|
||||
|
@ -464,9 +391,7 @@ func (g *Container) Bytes() []byte {
|
|||
return []byte("{}")
|
||||
}
|
||||
|
||||
/*
|
||||
BytesIndent - Converts the contained object back to a JSON []byte blob formatted with prefix and indent.
|
||||
*/
|
||||
// BytesIndent - Converts the contained object to a JSON []byte blob formatted with prefix, indent.
|
||||
func (g *Container) BytesIndent(prefix string, indent string) []byte {
|
||||
if g.object != 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("{}")
|
||||
}
|
||||
|
||||
/*
|
||||
String - Converts the contained object back to a JSON formatted string.
|
||||
*/
|
||||
// String - Converts the contained object to a JSON formatted string.
|
||||
func (g *Container) String() string {
|
||||
return string(g.Bytes())
|
||||
}
|
||||
|
||||
/*
|
||||
StringIndent - Converts the contained object back to a JSON formatted string with prefix and indent.
|
||||
*/
|
||||
// StringIndent - Converts the contained object back to a JSON formatted string with prefix, indent.
|
||||
func (g *Container) StringIndent(prefix string, indent string) string {
|
||||
return string(g.BytesIndent(prefix, indent))
|
||||
}
|
||||
|
||||
/*
|
||||
New - Create a new gabs JSON object.
|
||||
*/
|
||||
// New - Create a new gabs JSON object.
|
||||
func New() *Container {
|
||||
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) {
|
||||
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) {
|
||||
var gabs Container
|
||||
|
||||
|
@ -517,9 +432,7 @@ func ParseJSON(sample []byte) (*Container, error) {
|
|||
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) {
|
||||
var gabs Container
|
||||
|
||||
|
@ -530,9 +443,7 @@ func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) {
|
|||
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) {
|
||||
if len(path) > 0 {
|
||||
cBytes, err := ioutil.ReadFile(path)
|
||||
|
@ -550,9 +461,7 @@ func ParseJSONFile(path string) (*Container, error) {
|
|||
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) {
|
||||
var gabs Container
|
||||
jsonDecoder := json.NewDecoder(buffer)
|
||||
|
@ -563,83 +472,4 @@ func ParseJSONBuffer(buffer io.Reader) (*Container, error) {
|
|||
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
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -28,39 +28,42 @@ Examples
|
|||
|
||||
Here is an example of using the package:
|
||||
|
||||
func SlowMethod() {
|
||||
// Profiling the runtime of a method
|
||||
defer metrics.MeasureSince([]string{"SlowMethod"}, time.Now())
|
||||
}
|
||||
```go
|
||||
func SlowMethod() {
|
||||
// Profiling the runtime of a method
|
||||
defer metrics.MeasureSince([]string{"SlowMethod"}, time.Now())
|
||||
}
|
||||
|
||||
// Configure a statsite sink as the global metrics sink
|
||||
sink, _ := metrics.NewStatsiteSink("statsite:8125")
|
||||
metrics.NewGlobal(metrics.DefaultConfig("service-name"), sink)
|
||||
|
||||
// Emit a Key/Value pair
|
||||
metrics.EmitKey([]string{"questions", "meaning of life"}, 42)
|
||||
// Configure a statsite sink as the global metrics sink
|
||||
sink, _ := metrics.NewStatsiteSink("statsite:8125")
|
||||
metrics.NewGlobal(metrics.DefaultConfig("service-name"), sink)
|
||||
|
||||
// Emit a Key/Value pair
|
||||
metrics.EmitKey([]string{"questions", "meaning of life"}, 42)
|
||||
```
|
||||
|
||||
Here is an example of setting up an signal handler:
|
||||
|
||||
// Setup the inmem sink and signal handler
|
||||
inm := metrics.NewInmemSink(10*time.Second, time.Minute)
|
||||
sig := metrics.DefaultInmemSignal(inm)
|
||||
metrics.NewGlobal(metrics.DefaultConfig("service-name"), inm)
|
||||
```go
|
||||
// Setup the inmem sink and signal handler
|
||||
inm := metrics.NewInmemSink(10*time.Second, time.Minute)
|
||||
sig := metrics.DefaultInmemSignal(inm)
|
||||
metrics.NewGlobal(metrics.DefaultConfig("service-name"), inm)
|
||||
|
||||
// Run some code
|
||||
inm.SetGauge([]string{"foo"}, 42)
|
||||
inm.EmitKey([]string{"bar"}, 30)
|
||||
// Run some code
|
||||
inm.SetGauge([]string{"foo"}, 42)
|
||||
inm.EmitKey([]string{"bar"}, 30)
|
||||
|
||||
inm.IncrCounter([]string{"baz"}, 42)
|
||||
inm.IncrCounter([]string{"baz"}, 1)
|
||||
inm.IncrCounter([]string{"baz"}, 80)
|
||||
inm.IncrCounter([]string{"baz"}, 42)
|
||||
inm.IncrCounter([]string{"baz"}, 1)
|
||||
inm.IncrCounter([]string{"baz"}, 80)
|
||||
|
||||
inm.AddSample([]string{"method", "wow"}, 42)
|
||||
inm.AddSample([]string{"method", "wow"}, 100)
|
||||
inm.AddSample([]string{"method", "wow"}, 22)
|
||||
inm.AddSample([]string{"method", "wow"}, 42)
|
||||
inm.AddSample([]string{"method", "wow"}, 100)
|
||||
inm.AddSample([]string{"method", "wow"}, 22)
|
||||
|
||||
....
|
||||
....
|
||||
```
|
||||
|
||||
When a signal comes in, output like the following will be dumped to stderr:
|
||||
|
||||
|
|
|
@ -182,6 +182,19 @@ type Config struct {
|
|||
// the delay of a request see the aws/client.DefaultRetryer and
|
||||
// aws/request.Retryer.
|
||||
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
|
||||
|
@ -403,6 +416,10 @@ func mergeInConfig(dst *Config, other *Config) {
|
|||
if other.SleepDelay != nil {
|
||||
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
|
||||
|
|
2
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
2
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
|
@ -111,7 +111,7 @@ func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// A ec2RoleCredRespBody provides the shape for unmarshalling credential
|
||||
// A ec2RoleCredRespBody provides the shape for unmarshaling credential
|
||||
// request responses.
|
||||
type ec2RoleCredRespBody struct {
|
||||
// Success State
|
||||
|
|
|
@ -133,7 +133,7 @@ func (c *EC2Metadata) Available() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// An EC2IAMInfo provides the shape for unmarshalling
|
||||
// An EC2IAMInfo provides the shape for unmarshaling
|
||||
// an IAM info from the metadata API
|
||||
type EC2IAMInfo struct {
|
||||
Code string
|
||||
|
@ -142,7 +142,7 @@ type EC2IAMInfo struct {
|
|||
InstanceProfileID string
|
||||
}
|
||||
|
||||
// An EC2InstanceIdentityDocument provides the shape for unmarshalling
|
||||
// An EC2InstanceIdentityDocument provides the shape for unmarshaling
|
||||
// an instance identity document
|
||||
type EC2InstanceIdentityDocument struct {
|
||||
DevpayProductCodes []string `json:"devpayProductCodes"`
|
||||
|
|
|
@ -567,7 +567,11 @@ func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) {
|
|||
}
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -5,4 +5,4 @@ package aws
|
|||
const SDKName = "aws-sdk-go"
|
||||
|
||||
// SDKVersion is the version of this SDK
|
||||
const SDKVersion = "1.5.0"
|
||||
const SDKVersion = "1.5.6"
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"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()
|
||||
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) {
|
||||
|
@ -193,13 +194,15 @@ func buildQueryString(query url.Values, v reflect.Value, name string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func updatePath(url *url.URL, urlPath string) {
|
||||
func updatePath(url *url.URL, urlPath string, disableRestProtocolURICleaning bool) {
|
||||
scheme, query := url.Scheme, url.RawQuery
|
||||
|
||||
hasSlash := strings.HasSuffix(urlPath, "/")
|
||||
|
||||
// clean up path
|
||||
urlPath = path.Clean(urlPath)
|
||||
if !disableRestProtocolURICleaning {
|
||||
urlPath = path.Clean(urlPath)
|
||||
}
|
||||
if hasSlash && !strings.HasSuffix(urlPath, "/") {
|
||||
urlPath += "/"
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ Lucas Liu <extrafliu at gmail.com>
|
|||
Luke Scott <luke at webconnex.com>
|
||||
Michael Woolnough <michael.woolnough at gmail.com>
|
||||
Nicola Peduzzi <thenikso at gmail.com>
|
||||
Olivier Mengué <dolmen at cpan.org>
|
||||
Paul Bonser <misterpib at gmail.com>
|
||||
Runrioter Wung <runrioter at gmail.com>
|
||||
Soroush Pour <me at soroushjp.com>
|
||||
|
|
|
@ -315,13 +315,21 @@ I/O write timeout. The value must be a decimal number with an unit suffix ( *"ms
|
|||
|
||||
##### System Variables
|
||||
|
||||
All other parameters are interpreted as system variables:
|
||||
* `autocommit`: `"SET autocommit=<value>"`
|
||||
* [`time_zone`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `"SET time_zone=<value>"`
|
||||
* [`tx_isolation`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `"SET tx_isolation=<value>"`
|
||||
* `param`: `"SET <param>=<value>"`
|
||||
Any other parameters are interpreted as system variables:
|
||||
* `<boolean_var>=<value>`: `SET <boolean_var>=<value>`
|
||||
* `<enum_var>=<value>`: `SET <enum_var>=<value>`
|
||||
* `<string_var>=%27<value>%27`: `SET <string_var>='<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
|
||||
```
|
||||
|
|
|
@ -25,9 +25,9 @@ import (
|
|||
|
||||
// Read packet to buffer 'data'
|
||||
func (mc *mysqlConn) readPacket() ([]byte, error) {
|
||||
var payload []byte
|
||||
var prevData []byte
|
||||
for {
|
||||
// Read packet header
|
||||
// read packet header
|
||||
data, err := mc.buf.readNext(4)
|
||||
if err != nil {
|
||||
errLog.Print(err)
|
||||
|
@ -35,16 +35,10 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
|||
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)
|
||||
|
||||
if pktLen < 1 {
|
||||
errLog.Print(ErrMalformPkt)
|
||||
mc.Close()
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
||||
// Check Packet Sync [8 bit]
|
||||
// check packet sync [8 bit]
|
||||
if data[3] != mc.sequence {
|
||||
if data[3] > mc.sequence {
|
||||
return nil, ErrPktSyncMul
|
||||
|
@ -53,7 +47,20 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
|||
}
|
||||
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)
|
||||
if err != nil {
|
||||
errLog.Print(err)
|
||||
|
@ -61,18 +68,17 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
|||
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
|
||||
if isLastPacket && payload == nil {
|
||||
return data, nil
|
||||
return append(prevData, data...), nil
|
||||
}
|
||||
|
||||
payload = append(payload, data...)
|
||||
|
||||
if isLastPacket {
|
||||
return payload, nil
|
||||
}
|
||||
prevData = append(prevData, data...)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -700,11 +706,15 @@ func (rows *textRows) readRow(dest []driver.Value) error {
|
|||
if data[0] == iEOF && len(data) == 5 {
|
||||
// server_status [2 bytes]
|
||||
rows.mc.status = readStatus(data[3:])
|
||||
if err := rows.mc.discardResults(); err != nil {
|
||||
return err
|
||||
err = rows.mc.discardResults()
|
||||
if err == nil {
|
||||
err = io.EOF
|
||||
} else {
|
||||
// connection unusable
|
||||
rows.mc.Close()
|
||||
}
|
||||
rows.mc = nil
|
||||
return io.EOF
|
||||
return err
|
||||
}
|
||||
if data[0] == iERR {
|
||||
rows.mc = nil
|
||||
|
@ -1105,11 +1115,15 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
// EOF Packet
|
||||
if data[0] == iEOF && len(data) == 5 {
|
||||
rows.mc.status = readStatus(data[3:])
|
||||
if err := rows.mc.discardResults(); err != nil {
|
||||
return err
|
||||
err = rows.mc.discardResults()
|
||||
if err == nil {
|
||||
err = io.EOF
|
||||
} else {
|
||||
// connection unusable
|
||||
rows.mc.Close()
|
||||
}
|
||||
rows.mc = nil
|
||||
return io.EOF
|
||||
return err
|
||||
}
|
||||
rows.mc = nil
|
||||
|
||||
|
|
|
@ -24,7 +24,10 @@ type mysqlStmt struct {
|
|||
|
||||
func (stmt *mysqlStmt) Close() error {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -82,3 +82,4 @@ Robert Nix <robert@nicerobot.org>
|
|||
Nathan Youngman <git@nathany.com>
|
||||
Charles Law <charles.law@gmail.com>; <claw@conduce.com>
|
||||
Nathan Davies <nathanjamesdavies@gmail.com>
|
||||
Bo Blanton <bo.blanton@gmail.com>
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
|
@ -6,6 +6,8 @@ package gocql
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -75,6 +77,10 @@ type ClusterConfig struct {
|
|||
// via Discovery
|
||||
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
|
||||
// the supplied host by either initial hosts or discovered via events then the
|
||||
// host will be replaced with the supplied address.
|
||||
|
@ -146,6 +152,21 @@ func (cfg *ClusterConfig) CreateSession() (*Session, error) {
|
|||
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 (
|
||||
ErrNoHosts = errors.New("no hosts provided")
|
||||
ErrNoConnectionsStarted = errors.New("no connections were made when creating the session")
|
||||
|
|
|
@ -172,7 +172,9 @@ func Connect(host *HostInfo, cfg *ConnConfig, errorHandler ConnErrorHandler, ses
|
|||
}
|
||||
|
||||
// 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 {
|
||||
// the TLS config is safe to be reused by connections but it must not
|
||||
|
|
|
@ -318,8 +318,8 @@ func (c *controlConn) reconnect(refreshring bool) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: should have our own roundrobbin for hosts so that we can try each
|
||||
// in succession and guantee that we get a different host each time.
|
||||
// TODO: should have our own round-robin for hosts so that we can try each
|
||||
// in succession and guarantee that we get a different host each time.
|
||||
if newConn == nil {
|
||||
host := c.session.ring.rrHost()
|
||||
if host == nil {
|
||||
|
|
|
@ -205,7 +205,6 @@ func (s *Session) handleNewNode(ip net.IP, port int, waitForBinary bool) {
|
|||
s.pool.addHost(hostInfo)
|
||||
s.policy.AddHost(hostInfo)
|
||||
hostInfo.setState(NodeUp)
|
||||
|
||||
if s.control != nil && !s.cfg.IgnorePeerAddr {
|
||||
s.hostSource.refreshRing()
|
||||
}
|
||||
|
|
|
@ -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) ./..."
|
||||
|
||||
go test -v -tags unit
|
||||
go test -v -tags unit
|
||||
|
||||
if [ "$auth" = true ]
|
||||
then
|
||||
|
|
|
@ -204,14 +204,21 @@ func (t *tokenRing) GetHostForToken(token token) *HostInfo {
|
|||
return nil
|
||||
}
|
||||
|
||||
l := len(t.tokens)
|
||||
// no host tokens, no available hosts
|
||||
if l == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// find the primary replica
|
||||
ringIndex := sort.Search(
|
||||
len(t.tokens),
|
||||
l,
|
||||
func(i int) bool {
|
||||
return !t.tokens[i].Less(token)
|
||||
},
|
||||
)
|
||||
if ringIndex == len(t.tokens) {
|
||||
|
||||
if ringIndex == l {
|
||||
// wrap around to the first in the ring
|
||||
ringIndex = 0
|
||||
}
|
||||
|
|
|
@ -154,6 +154,7 @@ type ExtensionDesc struct {
|
|||
Field int32 // field number
|
||||
Name string // fully-qualified name of extension, for text formatting
|
||||
Tag string // protobuf tag style
|
||||
Filename string // name of the file in which the extension is defined
|
||||
}
|
||||
|
||||
func (ed *ExtensionDesc) repeated() bool {
|
||||
|
|
|
@ -592,7 +592,11 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
|||
props = oop.Prop
|
||||
nv := reflect.New(oop.Type.Elem())
|
||||
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() {
|
||||
return p.errorf("unknown field name %q in %v", name, st)
|
||||
|
|
|
@ -55,10 +55,14 @@ func (e *Event) Payload() (payload interface{}) {
|
|||
payload = &IssueCommentEvent{}
|
||||
case "IssuesEvent":
|
||||
payload = &IssuesEvent{}
|
||||
case "LabelEvent":
|
||||
payload = &LabelEvent{}
|
||||
case "MemberEvent":
|
||||
payload = &MemberEvent{}
|
||||
case "MembershipEvent":
|
||||
payload = &MembershipEvent{}
|
||||
case "MilestoneEvent":
|
||||
payload = &MilestoneEvent{}
|
||||
case "PageBuildEvent":
|
||||
payload = &PageBuildEvent{}
|
||||
case "PublicEvent":
|
||||
|
|
|
@ -346,9 +346,6 @@ func (s *AuthorizationsService) ListGrants() ([]*Grant, *Response, error) {
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
// TODO: remove custom Accept header when this API fully launches.
|
||||
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview)
|
||||
|
||||
grants := []*Grant{}
|
||||
resp, err := s.client.Do(req, &grants)
|
||||
if err != nil {
|
||||
|
@ -368,9 +365,6 @@ func (s *AuthorizationsService) GetGrant(id int) (*Grant, *Response, error) {
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
// TODO: remove custom Accept header when this API fully launches.
|
||||
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview)
|
||||
|
||||
grant := new(Grant)
|
||||
resp, err := s.client.Do(req, grant)
|
||||
if err != nil {
|
||||
|
@ -392,9 +386,6 @@ func (s *AuthorizationsService) DeleteGrant(id int) (*Response, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: remove custom Accept header when this API fully launches.
|
||||
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview)
|
||||
|
||||
return s.client.Do(req, nil)
|
||||
}
|
||||
|
||||
|
|
|
@ -209,6 +209,22 @@ type IssuesEvent struct {
|
|||
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.
|
||||
// The Webhook event name is "member".
|
||||
//
|
||||
|
@ -243,6 +259,23 @@ type MembershipEvent struct {
|
|||
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
|
||||
// successful or not.
|
||||
// The Webhook event name is "page_build".
|
||||
|
|
|
@ -68,6 +68,7 @@ const (
|
|||
mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-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"
|
||||
|
||||
// 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/
|
||||
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/
|
||||
mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json"
|
||||
|
||||
|
@ -126,6 +124,7 @@ type Client struct {
|
|||
Integrations *IntegrationsService
|
||||
Issues *IssuesService
|
||||
Organizations *OrganizationsService
|
||||
Projects *ProjectsService
|
||||
PullRequests *PullRequestsService
|
||||
Repositories *RepositoriesService
|
||||
Search *SearchService
|
||||
|
@ -199,6 +198,7 @@ func NewClient(httpClient *http.Client) *Client {
|
|||
c.Licenses = (*LicensesService)(&c.common)
|
||||
c.Migrations = (*MigrationService)(&c.common)
|
||||
c.Organizations = (*OrganizationsService)(&c.common)
|
||||
c.Projects = (*ProjectsService)(&c.common)
|
||||
c.PullRequests = (*PullRequestsService)(&c.common)
|
||||
c.Reactions = (*ReactionsService)(&c.common)
|
||||
c.Repositories = (*RepositoriesService)(&c.common)
|
||||
|
|
|
@ -10,23 +10,44 @@ import "fmt"
|
|||
// LicensesService handles communication with the license related
|
||||
// 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
|
||||
|
||||
// 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.
|
||||
type License struct {
|
||||
Key *string `json:"key,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
URL *string `json:"url,omitempty"`
|
||||
|
||||
SPDXID *string `json:"spdx_id,omitempty"`
|
||||
HTMLURL *string `json:"html_url,omitempty"`
|
||||
Featured *bool `json:"featured,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Category *string `json:"category,omitempty"`
|
||||
Implementation *string `json:"implementation,omitempty"`
|
||||
Required *[]string `json:"required,omitempty"`
|
||||
Permitted *[]string `json:"permitted,omitempty"`
|
||||
Forbidden *[]string `json:"forbidden,omitempty"`
|
||||
Permissions *[]string `json:"permissions,omitempty"`
|
||||
Conditions *[]string `json:"conditions,omitempty"`
|
||||
Limitations *[]string `json:"limitations,omitempty"`
|
||||
Body *string `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -49,8 +49,10 @@ var (
|
|||
"integration_installation_repositories": "IntegrationInstallationRepositoriesEvent",
|
||||
"issue_comment": "IssueCommentEvent",
|
||||
"issues": "IssuesEvent",
|
||||
"label": "LabelEvent",
|
||||
"member": "MemberEvent",
|
||||
"membership": "MembershipEvent",
|
||||
"milestone": "MilestoneEvent",
|
||||
"page_build": "PageBuildEvent",
|
||||
"public": "PublicEvent",
|
||||
"pull_request_review_comment": "PullRequestReviewCommentEvent",
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -253,12 +253,16 @@ type PullRequestMergeResult struct {
|
|||
|
||||
// PullRequestOptions lets you define how a pull request will be merged.
|
||||
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 {
|
||||
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™).
|
||||
|
@ -269,7 +273,8 @@ func (s *PullRequestsService) Merge(owner string, repo string, number int, commi
|
|||
|
||||
pullRequestBody := &pullRequestMergeRequest{CommitMessage: &commitMessage}
|
||||
if options != nil {
|
||||
pullRequestBody.Squash = &options.Squash
|
||||
pullRequestBody.CommitTitle = &options.CommitTitle
|
||||
pullRequestBody.MergeMethod = &options.MergeMethod
|
||||
}
|
||||
req, err := s.client.NewRequest("PUT", u, pullRequestBody)
|
||||
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
|
||||
package github
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RepositoriesService handles communication with the repository related
|
||||
// methods of the GitHub API.
|
||||
|
@ -46,6 +49,9 @@ type Repository struct {
|
|||
Source *Repository `json:"source,omitempty"`
|
||||
Organization *Organization `json:"organization,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
|
||||
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
|
||||
// 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)
|
||||
resp, err := s.client.Do(req, repository)
|
||||
|
@ -330,6 +337,9 @@ func (s *RepositoriesService) Edit(owner, repo string, repository *Repository) (
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
// TODO: Remove this preview header after API is fully vetted.
|
||||
req.Header.Add("Accept", mediaTypeSquashPreview)
|
||||
|
||||
r := new(Repository)
|
||||
resp, err := s.client.Do(req, r)
|
||||
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.
|
||||
//
|
||||
// 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)
|
||||
req, err := s.client.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
r := &Repository{}
|
||||
r := &RepositoryLicense{}
|
||||
resp, err := s.client.Do(req, r)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return r.License, resp, err
|
||||
return r, resp, err
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ type PagesBuild struct {
|
|||
// GetPagesInfo fetches information about a GitHub 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)
|
||||
req, err := s.client.NewRequest("GET", u, 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.
|
||||
//
|
||||
// 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)
|
||||
req, err := s.client.NewRequest("GET", u, 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.
|
||||
//
|
||||
// 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)
|
||||
req, err := s.client.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
|
@ -93,10 +93,29 @@ func (s *RepositoriesService) GetLatestPagesBuild(owner string, repo string) (*P
|
|||
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.
|
||||
//
|
||||
// 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)
|
||||
req, err := s.client.NewRequest("POST", u, nil)
|
||||
if err != nil {
|
||||
|
|
|
@ -5,34 +5,11 @@
|
|||
|
||||
package github
|
||||
|
||||
import (
|
||||
"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)
|
||||
}
|
||||
import "fmt"
|
||||
|
||||
// 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) {
|
||||
u := fmt.Sprintf("repos/%v/%v/projects", owner, repo)
|
||||
u, err := addOptions(u, opt)
|
||||
|
@ -57,44 +34,12 @@ func (s *RepositoriesService) ListProjects(owner, repo string, opt *ListOptions)
|
|||
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.
|
||||
//
|
||||
// GitHub API docs: https://developer.github.com/v3/repos/projects/#create-a-project
|
||||
func (s *RepositoriesService) CreateProject(owner, repo string, projectOptions *ProjectOptions) (*Project, *Response, error) {
|
||||
// GitHub API docs: https://developer.github.com/v3/projects/#create-a-repository-project
|
||||
func (s *RepositoriesService) CreateProject(owner, repo string, opt *ProjectOptions) (*Project, *Response, error) {
|
||||
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 {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -110,355 +55,3 @@ func (s *RepositoriesService) CreateProject(owner, repo string, projectOptions *
|
|||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ type AgentServiceCheck struct {
|
|||
HTTP string `json:",omitempty"`
|
||||
TCP string `json:",omitempty"`
|
||||
Status string `json:",omitempty"`
|
||||
TLSSkipVerify string `json:",omitempty"`
|
||||
|
||||
// In Consul 0.7 and later, checks that are associated with a service
|
||||
// may also contain this optional DeregisterCriticalServiceAfter field,
|
||||
|
|
|
@ -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) {
|
||||
r := k.c.newRequest("GET", "/v1/kv/"+key)
|
||||
r := k.c.newRequest("GET", "/v1/kv/"+strings.TrimPrefix(key, "/"))
|
||||
r.setQueryOptions(q)
|
||||
for param, val := range params {
|
||||
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) {
|
||||
r := k.c.newRequest("DELETE", "/v1/kv/"+key)
|
||||
r := k.c.newRequest("DELETE", "/v1/kv/"+strings.TrimPrefix(key, "/"))
|
||||
r.setWriteOptions(q)
|
||||
for param, val := range params {
|
||||
r.params.Set(param, val)
|
||||
|
|
|
@ -167,19 +167,18 @@ func (c *PreparedQuery) Get(queryID string, q *QueryOptions) ([]*PreparedQueryDe
|
|||
}
|
||||
|
||||
// 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.setQueryOptions(q)
|
||||
r.setWriteOptions(q)
|
||||
rtt, resp, err := requireOK(c.c.doRequest(r))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
qm := &QueryMeta{}
|
||||
parseQueryMeta(resp, qm)
|
||||
qm.RequestTime = rtt
|
||||
return qm, nil
|
||||
wm := &WriteMeta{}
|
||||
wm.RequestTime = rtt
|
||||
return wm, nil
|
||||
}
|
||||
|
||||
// Execute is used to execute a specific prepared query. You can execute using
|
||||
|
|
|
@ -12,12 +12,16 @@ type ErrorFormatFunc func([]error) string
|
|||
// ListFormatFunc is a basic formatter that outputs the number of errors
|
||||
// that occurred along with a bullet point list of the errors.
|
||||
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))
|
||||
for i, err := range es {
|
||||
points[i] = fmt.Sprintf("* %s", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%d error(s) occurred:\n\n%s",
|
||||
"%d errors occurred:\n\n%s",
|
||||
len(es), strings.Join(points, "\n"))
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ func Unquote(s string) (t string, err error) {
|
|||
for len(s) > 0 {
|
||||
// If we're starting a '${}' then let it through un-unquoted.
|
||||
// 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] == '{' {
|
||||
buf = append(buf, '$', '{')
|
||||
s = s[2:]
|
||||
|
@ -61,16 +61,6 @@ func Unquote(s string) (t string, err error) {
|
|||
|
||||
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)
|
||||
buf = append(buf, runeTmp[:n]...)
|
||||
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
return
|
||||
case 'T':
|
||||
rs.colNames, rs.colFmts, rs.colTyps = parsePortalRowDescribe(&rs.rb)
|
||||
return io.EOF
|
||||
default:
|
||||
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
|
||||
// used as part of an SQL statement. For example:
|
||||
//
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
// NewColorable return new instance of Writer which handle escape sequence.
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
panic("nil passed instead of *os.File to NewColorable()")
|
||||
|
@ -15,10 +16,12 @@ func NewColorable(file *os.File) io.Writer {
|
|||
return file
|
||||
}
|
||||
|
||||
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||
func NewColorableStdout() io.Writer {
|
||||
return os.Stdout
|
||||
}
|
||||
|
||||
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||
func NewColorableStderr() io.Writer {
|
||||
return os.Stderr
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ type Writer struct {
|
|||
oldpos coord
|
||||
}
|
||||
|
||||
// NewColorable return new instance of Writer which handle escape sequence from File.
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
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 {
|
||||
return NewColorable(os.Stdout)
|
||||
}
|
||||
|
||||
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||
func NewColorableStderr() io.Writer {
|
||||
return NewColorable(os.Stderr)
|
||||
}
|
||||
|
@ -357,6 +360,7 @@ var color256 = map[int]int{
|
|||
255: 0xeeeeee,
|
||||
}
|
||||
|
||||
// Write write data on console
|
||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
|
|
@ -5,15 +5,18 @@ import (
|
|||
"io"
|
||||
)
|
||||
|
||||
// NonColorable hold writer but remove escape sequence.
|
||||
type NonColorable struct {
|
||||
out io.Writer
|
||||
lastbuf bytes.Buffer
|
||||
}
|
||||
|
||||
// NewNonColorable return new instance of Writer which remove escape sequence from Writer.
|
||||
func NewNonColorable(w io.Writer) io.Writer {
|
||||
return &NonColorable{out: w}
|
||||
}
|
||||
|
||||
// Write write data on console
|
||||
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
||||
er := bytes.NewReader(data)
|
||||
var bw [1]byte
|
||||
|
|
|
@ -36,6 +36,103 @@
|
|||
// Contexts.
|
||||
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
|
||||
// values, and has no deadline. It is typically used by the main function,
|
||||
// initialization, and tests, and as the top-level Context for incoming
|
||||
|
@ -52,3 +149,8 @@ func Background() Context {
|
|||
func TODO() Context {
|
||||
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()
|
||||
|
|
|
@ -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
|
|
@ -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{}
|
||||
}
|
|
@ -35,3 +35,7 @@ func configureServer18(h1 *http.Server, h2 *Server) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldLogPanic(panicValue interface{}) bool {
|
||||
return panicValue != nil && panicValue != http.ErrAbortHandler
|
||||
}
|
||||
|
|
|
@ -12,3 +12,7 @@ func configureServer18(h1 *http.Server, h2 *Server) error {
|
|||
// No IdleTimeout to sync prior to Go 1.8.
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldLogPanic(panicValue interface{}) bool {
|
||||
return panicValue != nil
|
||||
}
|
||||
|
|
|
@ -188,9 +188,6 @@ func ConfigureServer(s *http.Server, conf *Server) error {
|
|||
if !haveNPN {
|
||||
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 {
|
||||
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["h2-14"] = protoHandler // temporary; see above.
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -390,8 +386,8 @@ type serverConn struct {
|
|||
advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
|
||||
curClientStreams uint32 // number of open streams initiated by the client
|
||||
curPushedStreams uint32 // number of open streams initiated by server push
|
||||
maxStreamID uint32 // max ever seen from client
|
||||
maxPushPromiseID uint32 // ID of the last push promise, or 0 if there have been no pushes
|
||||
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 (even), or 0 if there have been no pushes
|
||||
streams map[uint32]*stream
|
||||
initialWindowSize 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
|
||||
// frame on stream 5, then stream 5 transitions to the "closed"
|
||||
// state when the first frame for stream 7 is sent or received."
|
||||
if streamID <= sc.maxStreamID {
|
||||
return stateClosed, nil
|
||||
if streamID%2 == 1 {
|
||||
if streamID <= sc.maxClientStreamID {
|
||||
return stateClosed, nil
|
||||
}
|
||||
} else {
|
||||
if streamID <= sc.maxPushPromiseID {
|
||||
return stateClosed, nil
|
||||
}
|
||||
}
|
||||
return stateIdle, nil
|
||||
}
|
||||
|
@ -740,7 +742,7 @@ func (sc *serverConn) serve() {
|
|||
return
|
||||
case <-gracefulShutdownCh:
|
||||
gracefulShutdownCh = nil
|
||||
sc.goAwayIn(ErrCodeNo, 0)
|
||||
sc.startGracefulShutdown()
|
||||
case <-sc.shutdownTimerCh:
|
||||
sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
|
||||
return
|
||||
|
@ -1015,7 +1017,7 @@ func (sc *serverConn) scheduleFrameWrite() {
|
|||
sc.needToSendGoAway = false
|
||||
sc.startFrameWrite(FrameWriteRequest{
|
||||
write: &writeGoAway{
|
||||
maxStreamID: sc.maxStreamID,
|
||||
maxStreamID: sc.maxClientStreamID,
|
||||
code: sc.goAwayCode,
|
||||
},
|
||||
})
|
||||
|
@ -1042,6 +1044,13 @@ func (sc *serverConn) scheduleFrameWrite() {
|
|||
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) {
|
||||
sc.serveG.check()
|
||||
var forceCloseIn time.Duration
|
||||
|
@ -1164,6 +1173,8 @@ func (sc *serverConn) processFrame(f Frame) error {
|
|||
return sc.processResetStream(f)
|
||||
case *PriorityFrame:
|
||||
return sc.processPriority(f)
|
||||
case *GoAwayFrame:
|
||||
return sc.processGoAway(f)
|
||||
case *PushPromiseFrame:
|
||||
// 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.
|
||||
|
@ -1189,7 +1200,7 @@ func (sc *serverConn) processPing(f *PingFrame) error {
|
|||
// PROTOCOL_ERROR."
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
if sc.inGoAway {
|
||||
if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
|
||||
return nil
|
||||
}
|
||||
sc.writeFrame(FrameWriteRequest{write: writePingAck{f}})
|
||||
|
@ -1198,9 +1209,6 @@ func (sc *serverConn) processPing(f *PingFrame) error {
|
|||
|
||||
func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
|
||||
sc.serveG.check()
|
||||
if sc.inGoAway {
|
||||
return nil
|
||||
}
|
||||
switch {
|
||||
case f.StreamID != 0: // stream-level flow control
|
||||
state, st := sc.state(f.StreamID)
|
||||
|
@ -1233,9 +1241,6 @@ func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
|
|||
|
||||
func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
|
||||
sc.serveG.check()
|
||||
if sc.inGoAway {
|
||||
return nil
|
||||
}
|
||||
|
||||
state, st := sc.state(f.StreamID)
|
||||
if state == stateIdle {
|
||||
|
@ -1265,12 +1270,15 @@ func (sc *serverConn) closeStream(st *stream, err error) {
|
|||
} else {
|
||||
sc.curClientStreams--
|
||||
}
|
||||
if sc.curClientStreams+sc.curPushedStreams == 0 {
|
||||
sc.setConnState(http.StateIdle)
|
||||
}
|
||||
delete(sc.streams, st.id)
|
||||
if len(sc.streams) == 0 && sc.srv.IdleTimeout != 0 {
|
||||
sc.idleTimer.Reset(sc.srv.IdleTimeout)
|
||||
if len(sc.streams) == 0 {
|
||||
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 {
|
||||
// Return any buffered unread bytes worth of conn-level flow control.
|
||||
|
@ -1295,9 +1303,6 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
if sc.inGoAway {
|
||||
return nil
|
||||
}
|
||||
if err := f.ForeachSetting(sc.processSetting); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1369,7 +1374,7 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
|
|||
|
||||
func (sc *serverConn) processData(f *DataFrame) error {
|
||||
sc.serveG.check()
|
||||
if sc.inGoAway {
|
||||
if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
|
||||
return nil
|
||||
}
|
||||
data := f.Data()
|
||||
|
@ -1448,6 +1453,20 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
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.
|
||||
func (st *stream) isPushed() bool {
|
||||
return st.id%2 == 0
|
||||
|
@ -1508,10 +1527,10 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
|||
// endpoint has opened or reserved. [...] An endpoint that
|
||||
// receives an unexpected stream identifier MUST respond with
|
||||
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
if id <= sc.maxStreamID {
|
||||
if id <= sc.maxClientStreamID {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
sc.maxStreamID = id
|
||||
sc.maxClientStreamID = id
|
||||
|
||||
if sc.idleTimer != nil {
|
||||
sc.idleTimer.Stop()
|
||||
|
@ -1847,15 +1866,17 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler
|
|||
rw.rws.stream.cancelCtx()
|
||||
if didPanic {
|
||||
e := recover()
|
||||
// Same as net/http:
|
||||
const size = 64 << 10
|
||||
buf := make([]byte, size)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
sc.writeFrameFromHandler(FrameWriteRequest{
|
||||
write: handlerPanicRST{rw.rws.stream.id},
|
||||
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
|
||||
}
|
||||
rw.handlerDone()
|
||||
|
@ -2267,8 +2288,9 @@ func (w *responseWriter) CloseNotify() <-chan bool {
|
|||
if ch == nil {
|
||||
ch = make(chan bool, 1)
|
||||
rws.closeNotifierCh = ch
|
||||
cw := rws.stream.cw
|
||||
go func() {
|
||||
rws.stream.cw.Wait() // wait for close
|
||||
cw.Wait() // wait for close
|
||||
ch <- true
|
||||
}()
|
||||
}
|
||||
|
@ -2421,7 +2443,7 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
|
|||
}
|
||||
for k := range opts.Header {
|
||||
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,
|
||||
// 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.
|
||||
// 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
|
||||
promisedID := sc.maxPushPromiseID
|
||||
|
||||
|
@ -2660,3 +2688,17 @@ func h1ServerShutdownChan(hs *http.Server) <-chan struct{} {
|
|||
|
||||
// optional test hook for h1ServerShutdownChan.
|
||||
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
|
||||
}
|
||||
|
|
|
@ -199,6 +199,7 @@ type clientStream struct {
|
|||
bytesRemain int64 // -1 means unknown; 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
|
||||
didReset bool // whether we sent a RST_STREAM to the server; guarded by cc.mu
|
||||
|
||||
peerReset chan struct{} // closed on peer reset
|
||||
resetErr error // populated before peerReset is closed
|
||||
|
@ -226,15 +227,26 @@ func (cs *clientStream) awaitRequestCancel(req *http.Request) {
|
|||
}
|
||||
select {
|
||||
case <-req.Cancel:
|
||||
cs.cancelStream()
|
||||
cs.bufPipe.CloseWithError(errRequestCanceled)
|
||||
cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
|
||||
case <-ctx.Done():
|
||||
cs.cancelStream()
|
||||
cs.bufPipe.CloseWithError(ctx.Err())
|
||||
cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
|
||||
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
|
||||
// server, or errStreamClosed if the stream is complete.
|
||||
func (cs *clientStream) checkResetOrDone() error {
|
||||
|
@ -1666,9 +1678,10 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
|
|||
cc.bw.Flush()
|
||||
cc.wmu.Unlock()
|
||||
}
|
||||
didReset := cs.didReset
|
||||
cc.mu.Unlock()
|
||||
|
||||
if len(data) > 0 {
|
||||
if len(data) > 0 && !didReset {
|
||||
if _, err := cs.bufPipe.Write(data); err != nil {
|
||||
rl.endStreamError(cs, err)
|
||||
return err
|
||||
|
|
|
@ -25,7 +25,9 @@ type WriteScheduler interface {
|
|||
// https://tools.ietf.org/html/rfc7540#section-5.1
|
||||
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)
|
||||
|
||||
// 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.
|
||||
func (wr FrameWriteRequest) StreamID() uint32 {
|
||||
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 wr.stream.id
|
||||
|
@ -142,17 +151,13 @@ func (wr FrameWriteRequest) Consume(n int32) (FrameWriteRequest, FrameWriteReque
|
|||
|
||||
// String is for debugging only.
|
||||
func (wr FrameWriteRequest) String() string {
|
||||
var streamID uint32
|
||||
if wr.stream != nil {
|
||||
streamID = wr.stream.id
|
||||
}
|
||||
var des string
|
||||
if s, ok := wr.write.(fmt.Stringer); ok {
|
||||
des = s.String()
|
||||
} else {
|
||||
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.
|
||||
|
|
|
@ -388,7 +388,15 @@ func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) {
|
|||
} else {
|
||||
n = ws.nodes[id]
|
||||
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)
|
||||
|
|
|
@ -752,7 +752,7 @@ func (tr *trace) addEvent(x interface{}, recyclable, sensitive bool) {
|
|||
and very unlikely to be the fault of this code.
|
||||
|
||||
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
|
||||
to help discover where that is; do not leave that var set,
|
||||
since it makes this package much less efficient.
|
||||
|
|
|
@ -899,6 +899,7 @@ func Getpgrp() (pid int) {
|
|||
//sysnb Getppid() (ppid int)
|
||||
//sys Getpriority(which int, who int) (prio int, err error)
|
||||
//sysnb Getrusage(who int, rusage *Rusage) (err error)
|
||||
//sysnb Getsid(pid int) (sid int, err error)
|
||||
//sysnb Gettid() (tid int)
|
||||
//sys Getxattr(path string, attr string, dest []byte) (sz 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 Nanosleep(time *Timespec, leftover *Timespec) (err error)
|
||||
//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 read(fd int, p []byte) (n int, err error)
|
||||
//sys Removexattr(path string, attr string) (err error)
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||
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
|
||||
|
||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
||||
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||
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
|
||||
|
||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
||||
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||
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
|
||||
|
||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
||||
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||
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
|
||||
|
||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
||||
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||
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
|
||||
|
||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
||||
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||
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
|
||||
|
||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
||||
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||
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
|
||||
|
||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
||||
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||
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
|
||||
|
||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
||||
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||
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
|
||||
|
||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
||||
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
r0, _, _ := RawSyscall(SYS_GETTID, 0, 0, 0)
|
||||
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
|
||||
|
||||
func prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(newlimit)), 0, 0)
|
||||
func prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) {
|
||||
_, _, e1 := RawSyscall6(SYS_PRLIMIT64, uintptr(pid), uintptr(resource), uintptr(unsafe.Pointer(newlimit)), uintptr(unsafe.Pointer(old)), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
|
|
@ -576,6 +576,9 @@ var logLevelName = map[int64]string{
|
|||
}
|
||||
|
||||
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 = strings.TrimRight(s, "\n") // Remove any trailing newline characters.
|
||||
c.addLogLine(&logpb.UserAppLogLine{
|
||||
|
|
|
@ -18,6 +18,22 @@ Prerequisites
|
|||
|
||||
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
|
||||
-----------
|
||||
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
|
||||
|
||||
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`
|
||||
|
|
|
@ -42,6 +42,7 @@ import (
|
|||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/trace"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/stats"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
|
@ -49,7 +50,8 @@ import (
|
|||
// On error, it returns the error and indicates whether the call should be retried.
|
||||
//
|
||||
// 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.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
|
@ -63,14 +65,25 @@ func recvResponse(dopts dialOptions, t transport.ClientTransport, c *callInfo, s
|
|||
return
|
||||
}
|
||||
p := &parser{r: stream}
|
||||
var inPayload *stats.InPayload
|
||||
if stats.On() {
|
||||
inPayload = &stats.InPayload{
|
||||
Client: true,
|
||||
}
|
||||
}
|
||||
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 {
|
||||
break
|
||||
}
|
||||
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()
|
||||
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 {
|
||||
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 {
|
||||
return nil, Errorf(codes.Internal, "grpc: %v", err)
|
||||
}
|
||||
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
|
||||
// does not exist.) so that t.Write could get io.EOF from wait(...). Leave the following
|
||||
// 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...)
|
||||
}
|
||||
|
||||
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
|
||||
for _, o := range opts {
|
||||
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)
|
||||
// TODO(dsymonds): Arrange for c.traceInfo.firstLine.remoteAddr to be set.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
c.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||
if e != nil {
|
||||
c.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{e}}, true)
|
||||
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{
|
||||
Last: true,
|
||||
Delay: false,
|
||||
|
@ -205,7 +248,7 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
|||
}
|
||||
return toRPCErr(err)
|
||||
}
|
||||
err = recvResponse(cc.dopts, t, &c, stream, reply)
|
||||
err = recvResponse(ctx, cc.dopts, t, &c, stream, reply)
|
||||
if err != nil {
|
||||
if put != nil {
|
||||
put()
|
||||
|
|
|
@ -199,6 +199,8 @@ func WithTimeout(d time.Duration) DialOption {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return func(o *dialOptions) {
|
||||
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.
|
||||
func WithUserAgent(s string) DialOption {
|
||||
return func(o *dialOptions) {
|
||||
|
|
|
@ -42,11 +42,13 @@ import (
|
|||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/stats"
|
||||
"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
|
||||
// generates the message header of 0 message length.
|
||||
func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer) ([]byte, error) {
|
||||
var b []byte
|
||||
var length uint
|
||||
func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer, outPayload *stats.OutPayload) ([]byte, error) {
|
||||
var (
|
||||
b []byte
|
||||
length uint
|
||||
)
|
||||
if msg != nil {
|
||||
var err error
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
if outPayload != nil {
|
||||
outPayload.Payload = msg
|
||||
// TODO truncate large payload.
|
||||
outPayload.Data = b
|
||||
outPayload.Length = len(b)
|
||||
}
|
||||
if cp != nil {
|
||||
if err := cp.Do(cbuf, b); err != nil {
|
||||
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(buf[5:], b)
|
||||
|
||||
if outPayload != nil {
|
||||
outPayload.WireLength = len(buf)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
|
@ -311,11 +325,14 @@ func checkRecvPayload(pf payloadFormat, recvCompress string, dc Decompressor) er
|
|||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if inPayload != nil {
|
||||
inPayload.WireLength = len(d)
|
||||
}
|
||||
if err := checkRecvPayload(pf, s.RecvCompress(), dc); err != nil {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -448,10 +472,10 @@ func convertCode(err error) codes.Code {
|
|||
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.
|
||||
//
|
||||
// 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
|
||||
// should not be referenced from any other code.
|
||||
const SupportPackageIsVersion3 = true
|
||||
const SupportPackageIsVersion4 = true
|
||||
|
|
|
@ -54,6 +54,8 @@ import (
|
|||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/internal"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/stats"
|
||||
"google.golang.org/grpc/tap"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
|
@ -110,6 +112,7 @@ type options struct {
|
|||
maxMsgSize int
|
||||
unaryInt UnaryServerInterceptor
|
||||
streamInt StreamServerInterceptor
|
||||
inTapHandle tap.ServerInHandle
|
||||
maxConcurrentStreams uint32
|
||||
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
|
||||
// started to accept requests yet.
|
||||
func NewServer(opt ...ServerOption) *Server {
|
||||
|
@ -412,17 +426,22 @@ func (s *Server) handleRawConn(rawConn net.Conn) {
|
|||
if s.opts.useHandlerImpl {
|
||||
s.serveUsingHandler(conn)
|
||||
} 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
|
||||
// serves streams on it.
|
||||
// This is run in its own goroutine (it does network I/O in
|
||||
// transport.NewServerTransport).
|
||||
func (s *Server) serveNewHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) {
|
||||
st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo)
|
||||
func (s *Server) serveHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) {
|
||||
config := &transport.ServerConfig{
|
||||
MaxStreams: s.opts.maxConcurrentStreams,
|
||||
AuthInfo: authInfo,
|
||||
InTapHandle: s.opts.inTapHandle,
|
||||
}
|
||||
st, err := transport.NewServerTransport("http2", c, config)
|
||||
if err != nil {
|
||||
s.mu.Lock()
|
||||
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
|
||||
|
@ -448,6 +467,12 @@ func (s *Server) serveStreams(st transport.ServerTransport) {
|
|||
defer wg.Done()
|
||||
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()
|
||||
}
|
||||
|
@ -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.
|
||||
// If tracing is not enabled, it returns nil.
|
||||
func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
|
||||
if !EnableTracing {
|
||||
tr, ok := trace.FromContext(stream.Context())
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
trInfo = &traceInfo{
|
||||
tr: trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()),
|
||||
tr: tr,
|
||||
}
|
||||
trInfo.firstLine.client = false
|
||||
trInfo.firstLine.remoteAddr = st.RemoteAddr()
|
||||
stream.TraceContext(trInfo.tr)
|
||||
|
||||
if dl, ok := stream.Context().Deadline(); ok {
|
||||
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 {
|
||||
var cbuf *bytes.Buffer
|
||||
var (
|
||||
cbuf *bytes.Buffer
|
||||
outPayload *stats.OutPayload
|
||||
)
|
||||
if cp != nil {
|
||||
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 {
|
||||
// This typically indicates a fatal issue (e.g., memory
|
||||
// corruption or hardware faults) the application program
|
||||
|
@ -547,10 +580,32 @@ func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Str
|
|||
// the optimal option.
|
||||
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) {
|
||||
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 {
|
||||
defer trInfo.tr.Finish()
|
||||
trInfo.firstLine.client = false
|
||||
|
@ -579,14 +634,14 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
|||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *rpcError:
|
||||
if err := t.WriteStatus(stream, err.code, err.desc); err != nil {
|
||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
||||
if e := t.WriteStatus(stream, err.code, err.desc); e != nil {
|
||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||
}
|
||||
case transport.ConnectionError:
|
||||
// Nothing to do here.
|
||||
case transport.StreamError:
|
||||
if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
|
||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
||||
if e := t.WriteStatus(stream, err.Code, err.Desc); e != nil {
|
||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||
}
|
||||
default:
|
||||
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 {
|
||||
switch err := err.(type) {
|
||||
case *rpcError:
|
||||
if err := t.WriteStatus(stream, err.code, err.desc); err != nil {
|
||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
||||
if e := t.WriteStatus(stream, err.code, err.desc); e != nil {
|
||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||
}
|
||||
return err
|
||||
default:
|
||||
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
|
||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
||||
if e := t.WriteStatus(stream, codes.Internal, err.Error()); e != nil {
|
||||
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
|
||||
statusDesc := ""
|
||||
df := func(v interface{}) error {
|
||||
if inPayload != nil {
|
||||
inPayload.WireLength = len(req)
|
||||
}
|
||||
if pf == compressionMade {
|
||||
var err error
|
||||
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 {
|
||||
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 {
|
||||
|
@ -630,6 +694,12 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
|||
if err := s.opts.codec.Unmarshal(req, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if inPayload != nil {
|
||||
inPayload.Payload = v
|
||||
inPayload.Data = req
|
||||
inPayload.Length = len(req)
|
||||
stats.Handle(stream.Context(), inPayload)
|
||||
}
|
||||
if trInfo != nil {
|
||||
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 {
|
||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return Errorf(statusCode, statusDesc)
|
||||
}
|
||||
if trInfo != nil {
|
||||
trInfo.tr.LazyLog(stringer("OK"), false)
|
||||
|
@ -677,11 +746,32 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
|||
if trInfo != nil {
|
||||
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) {
|
||||
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 {
|
||||
stream.SetSendCompress(s.opts.cp.Type())
|
||||
}
|
||||
|
@ -744,7 +834,11 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
|
|||
}
|
||||
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.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 {
|
||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||
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.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 {
|
||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||
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.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 {
|
||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||
trInfo.tr.SetError()
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -45,6 +45,7 @@ import (
|
|||
"golang.org/x/net/trace"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/stats"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
|
@ -97,7 +98,7 @@ type ClientStream interface {
|
|||
|
||||
// NewClientStream creates a new Stream for the client side. This is called
|
||||
// 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 {
|
||||
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{
|
||||
BlockingWait: !c.failFast,
|
||||
}
|
||||
|
@ -194,6 +213,8 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
|||
|
||||
tracing: EnableTracing,
|
||||
trInfo: trInfo,
|
||||
|
||||
statsCtx: ctx,
|
||||
}
|
||||
if cc.dopts.cp != nil {
|
||||
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),
|
||||
// and is set to nil when the clientStream's finish method is called.
|
||||
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 {
|
||||
|
@ -274,6 +300,8 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
|||
}
|
||||
cs.mu.Unlock()
|
||||
}
|
||||
// TODO Investigate how to signal the stats handling party.
|
||||
// generate error stats if err != nil && err != io.EOF?
|
||||
defer func() {
|
||||
if err != nil {
|
||||
cs.finish(err)
|
||||
|
@ -296,7 +324,13 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
|||
}
|
||||
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() {
|
||||
if cs.cbuf != nil {
|
||||
cs.cbuf.Reset()
|
||||
|
@ -305,11 +339,37 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
|||
if err != nil {
|
||||
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) {
|
||||
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() {
|
||||
// err != nil indicates the termination of the stream.
|
||||
if err != nil {
|
||||
|
@ -324,11 +384,15 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
|||
}
|
||||
cs.mu.Unlock()
|
||||
}
|
||||
if inPayload != nil {
|
||||
stats.Handle(cs.statsCtx, inPayload)
|
||||
}
|
||||
if !cs.desc.ClientStreams || cs.desc.ServerStreams {
|
||||
return
|
||||
}
|
||||
// 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)
|
||||
if err == nil {
|
||||
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()
|
||||
}
|
||||
}()
|
||||
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() {
|
||||
if ss.cbuf != nil {
|
||||
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 {
|
||||
return toRPCErr(err)
|
||||
}
|
||||
if outPayload != nil {
|
||||
outPayload.SentTime = time.Now()
|
||||
stats.Handle(ss.s.Context(), outPayload)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -513,7 +585,11 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
|
|||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -522,5 +598,8 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
|
|||
}
|
||||
return toRPCErr(err)
|
||||
}
|
||||
if inPayload != nil {
|
||||
stats.Handle(ss.s.Context(), inPayload)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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)
|
|
@ -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.
|
||||
|
||||
var ctx context.Context
|
||||
|
|
|
@ -51,16 +51,19 @@ import (
|
|||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/grpc/stats"
|
||||
)
|
||||
|
||||
// http2Client implements the ClientTransport interface with HTTP2.
|
||||
type http2Client struct {
|
||||
target string // server name/addr
|
||||
userAgent string
|
||||
md interface{}
|
||||
conn net.Conn // underlying communication channel
|
||||
authInfo credentials.AuthInfo // auth info about the connection
|
||||
nextID uint32 // the next stream ID to be used
|
||||
target string // server name/addr
|
||||
userAgent string
|
||||
md interface{}
|
||||
conn net.Conn // underlying communication channel
|
||||
remoteAddr net.Addr
|
||||
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.
|
||||
// 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"
|
||||
conn, err := dial(ctx, opts.Dialer, addr.Addr)
|
||||
if err != nil {
|
||||
if opts.FailOnNonTempDialError {
|
||||
return nil, connectionErrorf(isTemporary(err), err, "transport: %v", err)
|
||||
}
|
||||
return nil, connectionErrorf(true, err, "transport: %v", err)
|
||||
}
|
||||
// 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
|
||||
t := &http2Client{
|
||||
target: addr.Addr,
|
||||
userAgent: ua,
|
||||
md: addr.Metadata,
|
||||
conn: conn,
|
||||
authInfo: authInfo,
|
||||
target: addr.Addr,
|
||||
userAgent: ua,
|
||||
md: addr.Metadata,
|
||||
conn: conn,
|
||||
remoteAddr: conn.RemoteAddr(),
|
||||
localAddr: conn.LocalAddr(),
|
||||
authInfo: authInfo,
|
||||
// The client initiated stream id is odd starting from 1.
|
||||
nextID: 1,
|
||||
writableChan: make(chan int, 1),
|
||||
|
@ -270,12 +278,13 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
|
|||
// streams.
|
||||
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
|
||||
pr := &peer.Peer{
|
||||
Addr: t.conn.RemoteAddr(),
|
||||
Addr: t.remoteAddr,
|
||||
}
|
||||
// Attach Auth info if there is any.
|
||||
if t.authInfo != nil {
|
||||
pr.AuthInfo = t.authInfo
|
||||
}
|
||||
userCtx := ctx
|
||||
ctx = peer.NewContext(ctx, pr)
|
||||
authData := make(map[string]string)
|
||||
for _, c := range t.creds {
|
||||
|
@ -347,6 +356,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
|||
return nil, ErrConnClosing
|
||||
}
|
||||
s := t.newStream(ctx, callHdr)
|
||||
s.clientStatsCtx = userCtx
|
||||
t.activeStreams[s.id] = s
|
||||
|
||||
// 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
|
||||
bufLen := t.hBuf.Len()
|
||||
// Sends the headers in a single batch even when they span multiple frames.
|
||||
for !endHeaders {
|
||||
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)
|
||||
}
|
||||
}
|
||||
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
|
||||
return s, nil
|
||||
}
|
||||
|
@ -874,6 +896,24 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
|||
}
|
||||
|
||||
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()
|
||||
if !endStream {
|
||||
|
@ -885,6 +925,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
|||
}
|
||||
close(s.headerChan)
|
||||
s.headerDone = true
|
||||
isHeader = true
|
||||
}
|
||||
if !endStream || s.state == streamDone {
|
||||
s.mu.Unlock()
|
||||
|
|
|
@ -50,6 +50,8 @@ import (
|
|||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/grpc/stats"
|
||||
"google.golang.org/grpc/tap"
|
||||
)
|
||||
|
||||
// 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.
|
||||
type http2Server struct {
|
||||
conn net.Conn
|
||||
remoteAddr net.Addr
|
||||
localAddr net.Addr
|
||||
maxStreamID uint32 // max stream ID ever seen
|
||||
authInfo credentials.AuthInfo // auth info about the connection
|
||||
inTapHandle tap.ServerInHandle
|
||||
// writableChan synchronizes write access to the transport.
|
||||
// A writer acquires the write lock by receiving a value 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
|
||||
// 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)
|
||||
// Send initial settings as connection preface to client.
|
||||
var settings []http2.Setting
|
||||
// TODO(zhaoq): Have a better way to signal "no limit" because 0 is
|
||||
// permitted in the HTTP2 spec.
|
||||
maxStreams := config.MaxStreams
|
||||
if maxStreams == 0 {
|
||||
maxStreams = math.MaxUint32
|
||||
} else {
|
||||
|
@ -122,11 +128,14 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI
|
|||
var buf bytes.Buffer
|
||||
t := &http2Server{
|
||||
conn: conn,
|
||||
authInfo: authInfo,
|
||||
remoteAddr: conn.RemoteAddr(),
|
||||
localAddr: conn.LocalAddr(),
|
||||
authInfo: config.AuthInfo,
|
||||
framer: framer,
|
||||
hBuf: &buf,
|
||||
hEnc: hpack.NewEncoder(&buf),
|
||||
maxStreams: maxStreams,
|
||||
inTapHandle: config.InTapHandle,
|
||||
controlBuf: newRecvBuffer(),
|
||||
fc: &inFlow{limit: initialConnWindowSize},
|
||||
sendQuotaPool: newQuotaPool(defaultWindowSize),
|
||||
|
@ -142,7 +151,7 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI
|
|||
}
|
||||
|
||||
// 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()
|
||||
s := &Stream{
|
||||
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())
|
||||
}
|
||||
pr := &peer.Peer{
|
||||
Addr: t.conn.RemoteAddr(),
|
||||
Addr: t.remoteAddr,
|
||||
}
|
||||
// Attach Auth info if there is any.
|
||||
if t.authInfo != nil {
|
||||
|
@ -195,6 +204,18 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
|||
}
|
||||
s.recvCompress = state.encoding
|
||||
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()
|
||||
if t.state != reachable {
|
||||
t.mu.Unlock()
|
||||
|
@ -218,13 +239,25 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
|||
s.windowHandler = func(n int) {
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
// HandleStreams receives incoming streams using the given handler. This is
|
||||
// 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.
|
||||
preface := make([]byte, len(clientPreface))
|
||||
if _, err := io.ReadFull(t.conn, preface); err != nil {
|
||||
|
@ -279,7 +312,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
|
|||
}
|
||||
switch frame := frame.(type) {
|
||||
case *http2.MetaHeadersFrame:
|
||||
if t.operateHeaders(frame, handle) {
|
||||
if t.operateHeaders(frame, handle, traceCtx) {
|
||||
t.Close()
|
||||
break
|
||||
}
|
||||
|
@ -492,9 +525,16 @@ func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
|
|||
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
|
||||
}
|
||||
}
|
||||
bufLen := t.hBuf.Len()
|
||||
if err := t.writeHeaders(s, t.hBuf, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if stats.On() {
|
||||
outHeader := &stats.OutHeader{
|
||||
WireLength: bufLen,
|
||||
}
|
||||
stats.Handle(s.Context(), outHeader)
|
||||
}
|
||||
t.writableChan <- 0
|
||||
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})
|
||||
}
|
||||
}
|
||||
bufLen := t.hBuf.Len()
|
||||
if err := t.writeHeaders(s, t.hBuf, true); err != nil {
|
||||
t.Close()
|
||||
return err
|
||||
}
|
||||
if stats.On() {
|
||||
outTrailer := &stats.OutTrailer{
|
||||
WireLength: bufLen,
|
||||
}
|
||||
stats.Handle(s.Context(), outTrailer)
|
||||
}
|
||||
t.closeStream(s)
|
||||
t.writableChan <- 0
|
||||
return nil
|
||||
|
@ -767,7 +814,7 @@ func (t *http2Server) closeStream(s *Stream) {
|
|||
}
|
||||
|
||||
func (t *http2Server) RemoteAddr() net.Addr {
|
||||
return t.conn.RemoteAddr()
|
||||
return t.remoteAddr
|
||||
}
|
||||
|
||||
func (t *http2Server) Drain() {
|
||||
|
|
|
@ -45,10 +45,10 @@ import (
|
|||
"sync"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/trace"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/tap"
|
||||
)
|
||||
|
||||
// recvMsg represents the received msg from the transport. All transport
|
||||
|
@ -167,6 +167,11 @@ type Stream struct {
|
|||
id uint32
|
||||
// nil for client side Stream.
|
||||
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 context.Context
|
||||
// cancel is always nil for client side Stream.
|
||||
|
@ -266,11 +271,6 @@ func (s *Stream) Context() context.Context {
|
|||
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.
|
||||
func (s *Stream) Method() string {
|
||||
return s.method
|
||||
|
@ -355,10 +355,17 @@ const (
|
|||
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
|
||||
// if it fails.
|
||||
func NewServerTransport(protocol string, conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo) (ServerTransport, error) {
|
||||
return newHTTP2Server(conn, maxStreams, authInfo)
|
||||
func NewServerTransport(protocol string, conn net.Conn, config *ServerConfig) (ServerTransport, error) {
|
||||
return newHTTP2Server(conn, config)
|
||||
}
|
||||
|
||||
// ConnectOptions covers all relevant options for communicating with the server.
|
||||
|
@ -367,6 +374,8 @@ type ConnectOptions struct {
|
|||
UserAgent string
|
||||
// Dialer specifies how to dial a network address.
|
||||
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 []credentials.PerRPCCredentials
|
||||
// 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.
|
||||
type ServerTransport interface {
|
||||
// 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 may not be called on all streams.
|
||||
|
|
|
@ -19,16 +19,16 @@
|
|||
"revision": ""
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "M30X+Wqn7AnUr1numUOkQRI7ET0=",
|
||||
"checksumSHA1": "Xt8qcx6rrE1w8HkRSoxJDlNxswA=",
|
||||
"path": "github.com/Azure/azure-sdk-for-go/storage",
|
||||
"revision": "bd73d950fa4440dae889bd9917bff7cef539f86e",
|
||||
"revisionTime": "2016-10-28T18:31:11Z"
|
||||
"revision": "27ae5c8b5bc5d90ab0540b4c5d0f2632c8db8b57",
|
||||
"revisionTime": "2016-11-10T23:35:32Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "eWlbQbXextN77vRiVuIBV6kSwuE=",
|
||||
"checksumSHA1": "9BfDgevpBgdiP4B6DT9SvcuVyLs=",
|
||||
"path": "github.com/Jeffail/gabs",
|
||||
"revision": "855034b6b7a3b7144977efcaefe72d2c64b0d039",
|
||||
"revisionTime": "2016-08-09T16:55:30Z"
|
||||
"revision": "2a3aa15961d5fee6047b8151b67ac2f08ba2c48c",
|
||||
"revisionTime": "2016-11-16T21:39:02Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "LLVyR2dAgkihu0+HdZF+JK0gMMs=",
|
||||
|
@ -43,16 +43,16 @@
|
|||
"revisionTime": "2015-08-30T18:26:16Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "d6798KSc0jDg2MHNxKdgyNfMK7A=",
|
||||
"checksumSHA1": "6HjBxlrYOUnFZo6jPok9ZfQZ/dM=",
|
||||
"path": "github.com/armon/go-metrics",
|
||||
"revision": "3df31a1ada83e310c2e24b267c8e8b68836547b4",
|
||||
"revisionTime": "2016-07-17T04:34:58Z"
|
||||
"revision": "97c69685293dce4c0a2d0b19535179bbc976e4d2",
|
||||
"revisionTime": "2016-11-05T01:02:38Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "OmqT9Y1mAHvlAKeJh0jBHC9SH78=",
|
||||
"path": "github.com/armon/go-metrics/circonus",
|
||||
"revision": "3df31a1ada83e310c2e24b267c8e8b68836547b4",
|
||||
"revisionTime": "2016-07-17T04:34:58Z"
|
||||
"revision": "97c69685293dce4c0a2d0b19535179bbc976e4d2",
|
||||
"revisionTime": "2016-11-05T01:02:38Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "gNO0JNpLzYOdInGeq7HqMZUzx9M=",
|
||||
|
@ -67,196 +67,196 @@
|
|||
"revisionTime": "2016-10-01T16:31:30Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "QEah7lNQcDSLQinY80RVj5CrF3k=",
|
||||
"checksumSHA1": "zCEK2WbnAElGz7x27gMGEyNY19A=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Y9W+4GimK4Fuxq+vyIskVYFRnX4=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/awserr",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "+q4vdl3l1Wom8K1wfIpJ4jlFsbY=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/awsutil",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "/232RBWA3KnT7U+wciPS2+wmvR0=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/client",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ieAJ+Cvp/PKv1LpUEnUXpc3OI6E=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/client/metadata",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "c1N3Loy3AS9zD+m5CzpPNAED39U=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/corehandlers",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "zu5C95rmCZff6NYZb62lEaT5ibE=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/credentials",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "KQiUK/zr3mqnAXD7x/X55/iNme0=",
|
||||
"checksumSHA1": "u3GOAJLmdvbuNUeUEcZSEAOeL/0=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "NUJUTWlc1sV8b7WjfiYc4JZbXl0=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "4Ipx+5xN0gso+cENC2MHMWmQlR4=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/credentials/stscreds",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "DwhFsNluCFEwqzyp3hbJR3q2Wqs=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/defaults",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "8E0fEBUJY/1lJOyVxzTxMGQGInk=",
|
||||
"checksumSHA1": "/EXbk/z2TWjWc1Hvb4QYs3Wmhb8=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/ec2metadata",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "5Ac22YMTBmrX/CXaEIXzWljr8UY=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/request",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "eOo6evLMAxQfo7Qkc5/h5euN1Sw=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/session",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "diXvBs1LRC0RJ9WK6sllWKdzC04=",
|
||||
"checksumSHA1": "9K5Bpjvynki7Req1reyD1W3/uWA=",
|
||||
"path": "github.com/aws/aws-sdk-go/aws/signer/v4",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Esab5F8KswqkTdB4TtjSvZgs56k=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/endpoints",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "wk7EyvDaHwb5qqoOP/4d3cV0708=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/protocol",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "1QmQ3FqV37w0Zi44qv8pA1GeR0A=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "pNeF0Ey7TfBArH5LBQhKOQXQbLY=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "R00RL5jJXRYq1iiK1+PGvMfvXyM=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ZqY5RWavBLWTo6j9xqdyBEaNFRk=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/protocol/query",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "5xzix1R8prUyWxgLnzUQoxTsfik=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "TW/7U+/8ormL7acf6z2rv2hDD+s=",
|
||||
"checksumSHA1": "mLxtfPJvWIHdYPRY0f19kFuJ3u4=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/protocol/rest",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ODo+ko8D6unAxZuN1jGzMcN4QCc=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/protocol/restxml",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "eUEkjyMPAuekKBE4ou+nM9tXEas=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Eo9yODN5U99BK0pMzoqnBm7PCrY=",
|
||||
"path": "github.com/aws/aws-sdk-go/private/waiter",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "E5qjR1pDa/V2LEhXP36kZH2w91o=",
|
||||
"path": "github.com/aws/aws-sdk-go/service/dynamodb",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "nNF0ucfWGzZz7PQVe9da0J7Di7w=",
|
||||
"path": "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "6h4tJ9wVtbYb9wG4srtUxyPoAYM=",
|
||||
"path": "github.com/aws/aws-sdk-go/service/ec2",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "hQquEMm59E2CwVwfBvKRTVzBj/8=",
|
||||
"path": "github.com/aws/aws-sdk-go/service/iam",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "HtKiIAPKsBg2s1c5ytRkdZ/lqO8=",
|
||||
"path": "github.com/aws/aws-sdk-go/service/s3",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ouwhxcAsIYQ6oJbMRdLW/Ys/iyg=",
|
||||
"path": "github.com/aws/aws-sdk-go/service/sts",
|
||||
"revision": "967a4b67114c61521c4b514d7c9a2feb869918e9",
|
||||
"revisionTime": "2016-11-01T23:30:21Z"
|
||||
"revision": "12ce9fecda7689cae03a715e16c57519a97bddcd",
|
||||
"revisionTime": "2016-11-16T23:00:31Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Isa9x3nvIJ12hvgdvUUBty+yplU=",
|
||||
|
@ -291,44 +291,44 @@
|
|||
{
|
||||
"checksumSHA1": "vcncrPAdKKpLjl5O5lAdrFU6NOc=",
|
||||
"path": "github.com/coreos/etcd/client",
|
||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
||||
"revisionTime": "2016-11-02T17:34:52Z"
|
||||
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||
"revisionTime": "2016-11-16T00:44:50Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ga3jt9r+dQBMSXG0gnpNcXp2xYA=",
|
||||
"path": "github.com/coreos/etcd/pkg/fileutil",
|
||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
||||
"revisionTime": "2016-11-02T17:34:52Z"
|
||||
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||
"revisionTime": "2016-11-16T00:44:50Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "mKIXx1kDwmVmdIpZ3pJtRBuUKso=",
|
||||
"path": "github.com/coreos/etcd/pkg/pathutil",
|
||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
||||
"revisionTime": "2016-11-02T17:34:52Z"
|
||||
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||
"revisionTime": "2016-11-16T00:44:50Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "rMyIh9PsSvPs6Yd+YgKITQzQJx8=",
|
||||
"path": "github.com/coreos/etcd/pkg/tlsutil",
|
||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
||||
"revisionTime": "2016-11-02T17:34:52Z"
|
||||
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||
"revisionTime": "2016-11-16T00:44:50Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "985rsigkh9SZlDXm6qK6cBloQg0=",
|
||||
"path": "github.com/coreos/etcd/pkg/transport",
|
||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
||||
"revisionTime": "2016-11-02T17:34:52Z"
|
||||
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||
"revisionTime": "2016-11-16T00:44:50Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "gx1gJIMU6T0UNQ0bPZ/drQ8cpCI=",
|
||||
"path": "github.com/coreos/etcd/pkg/types",
|
||||
"revision": "7d777a4a6422d87bd9a19f56b9b1ae812e84ed73",
|
||||
"revisionTime": "2016-11-02T17:34:52Z"
|
||||
"revision": "377f19b0031f9c0aafe2aec28b6f9019311f52f9",
|
||||
"revisionTime": "2016-11-16T00:44:50Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "d50/+u/LFlXvEV10HiEoXB9OsGg=",
|
||||
"path": "github.com/coreos/go-systemd/journal",
|
||||
"revision": "64d5cd7cb947834ef93874e82745c42ad6de4d0e",
|
||||
"revisionTime": "2016-11-02T17:07:22Z"
|
||||
"revision": "48702e0da86bd25e76cfef347e2adeb434a0d0a6",
|
||||
"revisionTime": "2016-11-14T12:22:54Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "wqEybhAfMhCq2uOu4bT737mqu6U=",
|
||||
|
@ -391,40 +391,40 @@
|
|||
"revisionTime": "2016-09-26T17:55:29Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "7Di9l3MwdZliLqFKCU9ql2s+Rnw=",
|
||||
"checksumSHA1": "W7OFi8RC+99JY3lVFkqL8dVhUT0=",
|
||||
"path": "github.com/go-sql-driver/mysql",
|
||||
"revision": "ce924a41eea897745442daaa1739089b0f3f561d",
|
||||
"revisionTime": "2016-11-01T11:13:14Z"
|
||||
"revision": "665b83488b90b902ce0a305ef6652e599771cdf9",
|
||||
"revisionTime": "2016-11-16T07:07:42Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Ea5tZxe8OjKyrXAaw5FTrpxiBWw=",
|
||||
"checksumSHA1": "9dGSivFlYCZvic4tBgE0xtWF0x0=",
|
||||
"path": "github.com/gocql/gocql",
|
||||
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55",
|
||||
"revisionTime": "2016-11-01T09:30:54Z"
|
||||
"revision": "47f897f054183a4c891f27b235f11221683982dc",
|
||||
"revisionTime": "2016-11-07T21:46:42Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Z3N6HDGWcvcNu0FloZRq54uO3h4=",
|
||||
"path": "github.com/gocql/gocql/internal/lru",
|
||||
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55",
|
||||
"revisionTime": "2016-11-01T09:30:54Z"
|
||||
"revision": "47f897f054183a4c891f27b235f11221683982dc",
|
||||
"revisionTime": "2016-11-07T21:46:42Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ctK9mwZKnt/8dHxx2Ef6nZTljZs=",
|
||||
"path": "github.com/gocql/gocql/internal/murmur",
|
||||
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55",
|
||||
"revisionTime": "2016-11-01T09:30:54Z"
|
||||
"revision": "47f897f054183a4c891f27b235f11221683982dc",
|
||||
"revisionTime": "2016-11-07T21:46:42Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "tZQDfMMTKrYMXqen0zjJWLtOf1A=",
|
||||
"path": "github.com/gocql/gocql/internal/streams",
|
||||
"revision": "3a52a1dae458ea8c51f156b4b0e07758cd652b55",
|
||||
"revisionTime": "2016-11-01T09:30:54Z"
|
||||
"revision": "47f897f054183a4c891f27b235f11221683982dc",
|
||||
"revisionTime": "2016-11-07T21:46:42Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "SVXOQdpDBh0ihdZ5aIflgdA+Rpw=",
|
||||
"checksumSHA1": "QlFGL6EkbK8an+9M+EupFAKe4uM=",
|
||||
"path": "github.com/golang/protobuf/proto",
|
||||
"revision": "98fa357170587e470c5f27d3c3ea0947b71eb455",
|
||||
"revisionTime": "2016-10-12T20:53:35Z"
|
||||
"revision": "224aaba33b1ac32a92a165f27489409fb8133d08",
|
||||
"revisionTime": "2016-11-16T19:48:24Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "W+E/2xXcE1GmJ0Qb784ald0Fn6I=",
|
||||
|
@ -433,10 +433,10 @@
|
|||
"revisionTime": "2016-05-29T05:00:41Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "TSS9EqiOoJcVUMNb46zVngVrAO8=",
|
||||
"checksumSHA1": "5+cRIgrrCwJ/BhOG34jUcNyvDPI=",
|
||||
"path": "github.com/google/go-github/github",
|
||||
"revision": "f7fcf6f52ff94adf1cc0ded41e7768d2ad729972",
|
||||
"revisionTime": "2016-10-28T15:10:40Z"
|
||||
"revision": "d4f1b2d029be1730fd349ca929cc9c0da4a27007",
|
||||
"revisionTime": "2016-11-14T19:06:42Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "yyAzHoiVLu+xywYI2BDyRq6sOqE=",
|
||||
|
@ -451,16 +451,16 @@
|
|||
"revisionTime": "2016-01-25T11:53:50Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "LclVLJYrBi03PBjsVPpgoMbUDQ8=",
|
||||
"checksumSHA1": "DHgZ2o3MR8CjlrzkgrjlbyvK/cw=",
|
||||
"path": "github.com/hashicorp/consul/api",
|
||||
"revision": "fb7c03cbb1b3fa71b0746e2f19c442c63e686382",
|
||||
"revisionTime": "2016-11-01T13:11:20Z"
|
||||
"revision": "74cfcd301485b5dd658715f0282c4e5dff232fca",
|
||||
"revisionTime": "2016-11-16T15:58:24Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "0DPAA2cTBjrCGgXaxXil0vILcFs=",
|
||||
"path": "github.com/hashicorp/consul/lib",
|
||||
"revision": "fb7c03cbb1b3fa71b0746e2f19c442c63e686382",
|
||||
"revisionTime": "2016-11-01T13:11:20Z"
|
||||
"revision": "74cfcd301485b5dd658715f0282c4e5dff232fca",
|
||||
"revisionTime": "2016-11-16T15:58:24Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=",
|
||||
|
@ -481,10 +481,10 @@
|
|||
"revisionTime": "2015-05-18T23:42:57Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "KS9lmJV8Z7KHdtSIhbafQLU1hC4=",
|
||||
"checksumSHA1": "jplwF0kQyDhu2Wqq7cmZu0SHjOI=",
|
||||
"path": "github.com/hashicorp/go-multierror",
|
||||
"revision": "8c5f0ad9360406a3807ce7de6bc73269a91a6e51",
|
||||
"revisionTime": "2016-08-11T01:57:21Z"
|
||||
"revision": "8484912a3b9987857bac52e0c5fec2b95f419628",
|
||||
"revisionTime": "2016-11-06T17:22:40Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ErJHGU6AVPZM9yoY/xV11TwSjQs=",
|
||||
|
@ -525,56 +525,56 @@
|
|||
{
|
||||
"checksumSHA1": "8OPDk+bKyRGJoKcS4QNw9F7dpE8=",
|
||||
"path": "github.com/hashicorp/hcl",
|
||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
||||
"revisionTime": "2016-11-01T18:00:25Z"
|
||||
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||
"revisionTime": "2016-11-12T23:52:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "XQmjDva9JCGGkIecOgwtBEMCJhU=",
|
||||
"path": "github.com/hashicorp/hcl/hcl/ast",
|
||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
||||
"revisionTime": "2016-11-01T18:00:25Z"
|
||||
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||
"revisionTime": "2016-11-12T23:52:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "croNloscHsjX87X+4/cKOURf1EY=",
|
||||
"path": "github.com/hashicorp/hcl/hcl/parser",
|
||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
||||
"revisionTime": "2016-11-01T18:00:25Z"
|
||||
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||
"revisionTime": "2016-11-12T23:52:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "lgR7PSAZ0RtvAc9OCtCnNsF/x8g=",
|
||||
"path": "github.com/hashicorp/hcl/hcl/scanner",
|
||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
||||
"revisionTime": "2016-11-01T18:00:25Z"
|
||||
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||
"revisionTime": "2016-11-12T23:52:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "JlZmnzqdmFFyb1+2afLyR3BOE/8=",
|
||||
"checksumSHA1": "/e0ULfQnGeUKiM1+iMnQhImo62k=",
|
||||
"path": "github.com/hashicorp/hcl/hcl/strconv",
|
||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
||||
"revisionTime": "2016-11-01T18:00:25Z"
|
||||
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||
"revisionTime": "2016-11-12T23:52:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "c6yprzj06ASwCo18TtbbNNBHljA=",
|
||||
"path": "github.com/hashicorp/hcl/hcl/token",
|
||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
||||
"revisionTime": "2016-11-01T18:00:25Z"
|
||||
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||
"revisionTime": "2016-11-12T23:52:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "138aCV5n8n7tkGYMsMVQQnnLq+0=",
|
||||
"path": "github.com/hashicorp/hcl/json/parser",
|
||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
||||
"revisionTime": "2016-11-01T18:00:25Z"
|
||||
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||
"revisionTime": "2016-11-12T23:52:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "YdvFsNOMSWMLnY6fcliWQa0O5Fw=",
|
||||
"path": "github.com/hashicorp/hcl/json/scanner",
|
||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
||||
"revisionTime": "2016-11-01T18:00:25Z"
|
||||
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||
"revisionTime": "2016-11-12T23:52:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "fNlXQCQEnb+B3k5UDL/r15xtSJY=",
|
||||
"path": "github.com/hashicorp/hcl/json/token",
|
||||
"revision": "6e968a3fcdcbab092f5307fd0d85479d5af1e4dc",
|
||||
"revisionTime": "2016-11-01T18:00:25Z"
|
||||
"revision": "c3e054bfd4dcf77b9965ed2b79b22afa2f41d4eb",
|
||||
"revisionTime": "2016-11-12T23:52:43Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "qnlqWJYV81ENr61SZk9c65R1mDo=",
|
||||
|
@ -597,8 +597,8 @@
|
|||
{
|
||||
"checksumSHA1": "E3Xcanc9ouQwL+CZGOUyA/+giLg=",
|
||||
"path": "github.com/hashicorp/serf/coordinate",
|
||||
"revision": "e0680ca729b018b791af1add413faebe3e1c147e",
|
||||
"revisionTime": "2016-10-31T21:31:47Z"
|
||||
"revision": "f679d7594a349263f6118db40d87122d3a474e7d",
|
||||
"revisionTime": "2016-11-09T18:57:27Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ZhK6IO2XN81Y+3RAjTcVm1Ic7oU=",
|
||||
|
@ -673,22 +673,22 @@
|
|||
"revisionTime": "2016-10-04T15:35:44Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "T6yD9DYjn6yO+Kz70bO+sDj20oE=",
|
||||
"checksumSHA1": "avqi4lkviHdrNJ92cXCwrw9x870=",
|
||||
"path": "github.com/lib/pq",
|
||||
"revision": "a37edb86214894fa6c6c3401a4c4976b02176dd3",
|
||||
"revisionTime": "2016-11-02T07:48:14Z"
|
||||
"revision": "d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b",
|
||||
"revisionTime": "2016-11-03T02:43:54Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "xppHi82MLqVx1eyQmbhTesAEjx8=",
|
||||
"path": "github.com/lib/pq/oid",
|
||||
"revision": "a37edb86214894fa6c6c3401a4c4976b02176dd3",
|
||||
"revisionTime": "2016-11-02T07:48:14Z"
|
||||
"revision": "d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b",
|
||||
"revisionTime": "2016-11-03T02:43:54Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "WFbWfoslftbEoKx6ZNVTMcU0EzA=",
|
||||
"checksumSHA1": "I4njd26dG5hxFT2nawuByM4pxzY=",
|
||||
"path": "github.com/mattn/go-colorable",
|
||||
"revision": "6e26b354bd2b0fc420cb632b0d878abccdc6544c",
|
||||
"revisionTime": "2016-11-02T08:09:25Z"
|
||||
"revision": "d228849504861217f796da67fae4f6e347643f15",
|
||||
"revisionTime": "2016-11-03T16:00:40Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "xZuhljnmBysJPta/lMyYmJdujCg=",
|
||||
|
@ -853,160 +853,172 @@
|
|||
"revisionTime": "2016-10-31T15:37:30Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "4hQNaJUg67lF/QcO0NKzUeqlaew=",
|
||||
"checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=",
|
||||
"path": "golang.org/x/net/context",
|
||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
||||
"revisionTime": "2016-10-31T16:36:42Z"
|
||||
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||
"revisionTime": "2016-11-15T21:05:04Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "r+7Ol7uTCa/i5W8pNej9M8xZxWg=",
|
||||
"checksumSHA1": "pLsZUQhI8jm3W9R/4JO9D/L1cUA=",
|
||||
"path": "golang.org/x/net/http2",
|
||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
||||
"revisionTime": "2016-10-31T16:36:42Z"
|
||||
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||
"revisionTime": "2016-11-15T21:05:04Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "HzuGD7AwgC0p1az1WAQnEFnEk98=",
|
||||
"path": "golang.org/x/net/http2/hpack",
|
||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
||||
"revisionTime": "2016-10-31T16:36:42Z"
|
||||
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||
"revisionTime": "2016-11-15T21:05:04Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "GIGmSrYACByf5JDIP9ByBZksY80=",
|
||||
"path": "golang.org/x/net/idna",
|
||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
||||
"revisionTime": "2016-10-31T16:36:42Z"
|
||||
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||
"revisionTime": "2016-11-15T21:05:04Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "/k7k6eJDkxXx6K9Zpo/OwNm58XM=",
|
||||
"path": "golang.org/x/net/internal/timeseries",
|
||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
||||
"revisionTime": "2016-10-31T16:36:42Z"
|
||||
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||
"revisionTime": "2016-11-15T21:05:04Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "3xyuaSNmClqG4YWC7g0isQIbUTc=",
|
||||
"path": "golang.org/x/net/lex/httplex",
|
||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
||||
"revisionTime": "2016-10-31T16:36:42Z"
|
||||
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||
"revisionTime": "2016-11-15T21:05:04Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "4MMbG0LI3ghvWooRn36RmDrFIB0=",
|
||||
"checksumSHA1": "P9qTIn8a6L6Q9wd1IJBCuhno1Q8=",
|
||||
"path": "golang.org/x/net/trace",
|
||||
"revision": "4bb47a1098b37d69980d96237e2ae4ff56bb5a95",
|
||||
"revisionTime": "2016-10-31T16:36:42Z"
|
||||
"revision": "4971afdc2f162e82d185353533d3cf16188a9f4e",
|
||||
"revisionTime": "2016-11-15T21:05:04Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "XH7CgbL5Z8COUc+MKrYqS3FFosY=",
|
||||
"path": "golang.org/x/oauth2",
|
||||
"revision": "25b4fb1468cb89700c7c060cb99f30581a61f5e3",
|
||||
"revisionTime": "2016-10-25T17:59:40Z"
|
||||
"revision": "d5040cddfc0da40b408c9a1da4728662435176a9",
|
||||
"revisionTime": "2016-11-03T22:50:36Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "D3v/aqfB9swlaZcSksCoF+lbOqo=",
|
||||
"path": "golang.org/x/oauth2/internal",
|
||||
"revision": "25b4fb1468cb89700c7c060cb99f30581a61f5e3",
|
||||
"revisionTime": "2016-10-25T17:59:40Z"
|
||||
"revision": "d5040cddfc0da40b408c9a1da4728662435176a9",
|
||||
"revisionTime": "2016-11-03T22:50:36Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "aVgPDgwY3/t4J/JOw9H3FVMHqh0=",
|
||||
"checksumSHA1": "KqecwXo3OO+p4N+E9RhlHvl9I+w=",
|
||||
"path": "golang.org/x/sys/unix",
|
||||
"revision": "c200b10b5d5e122be351b67af224adc6128af5bf",
|
||||
"revisionTime": "2016-10-22T18:22:21Z"
|
||||
"revision": "b699b7032584f0953262cb2788a0ca19bb494703",
|
||||
"revisionTime": "2016-11-10T11:58:56Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "vIZ71Qe81RHec1vNHpKG+CSx/es=",
|
||||
"checksumSHA1": "gYHoPrPncGO926bN0jr1rzDxBQU=",
|
||||
"path": "google.golang.org/appengine/internal",
|
||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
||||
"revisionTime": "2016-10-25T16:43:32Z"
|
||||
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||
"revisionTime": "2016-11-15T22:01:06Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "TsNO8P0xUlLNyh3Ic/tzSp/fDWM=",
|
||||
"path": "google.golang.org/appengine/internal/base",
|
||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
||||
"revisionTime": "2016-10-25T16:43:32Z"
|
||||
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||
"revisionTime": "2016-11-15T22:01:06Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "5QsV5oLGSfKZqTCVXP6NRz5T4Tw=",
|
||||
"path": "google.golang.org/appengine/internal/datastore",
|
||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
||||
"revisionTime": "2016-10-25T16:43:32Z"
|
||||
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||
"revisionTime": "2016-11-15T22:01:06Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Gep2T9zmVYV8qZfK2gu3zrmG6QE=",
|
||||
"path": "google.golang.org/appengine/internal/log",
|
||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
||||
"revisionTime": "2016-10-25T16:43:32Z"
|
||||
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||
"revisionTime": "2016-11-15T22:01:06Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "a1XY7rz3BieOVqVI2Et6rKiwQCk=",
|
||||
"path": "google.golang.org/appengine/internal/remote_api",
|
||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
||||
"revisionTime": "2016-10-25T16:43:32Z"
|
||||
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||
"revisionTime": "2016-11-15T22:01:06Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "QtAbHtHmDzcf6vOV9eqlCpKgjiw=",
|
||||
"path": "google.golang.org/appengine/internal/urlfetch",
|
||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
||||
"revisionTime": "2016-10-25T16:43:32Z"
|
||||
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||
"revisionTime": "2016-11-15T22:01:06Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "akOV9pYnCbcPA8wJUutSQVibdyg=",
|
||||
"path": "google.golang.org/appengine/urlfetch",
|
||||
"revision": "46239ca616842c00f41b8cbc6bbf2bd6ffbfcdad",
|
||||
"revisionTime": "2016-10-25T16:43:32Z"
|
||||
"revision": "ca59ef35f409df61fa4a5f8290ff289b37eccfb8",
|
||||
"revisionTime": "2016-11-15T22:01:06Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "r2jmzn/o6RN6PT9FRRtBeAfgNEk=",
|
||||
"checksumSHA1": "xyB2Py2ViSKX8Td+oe2hxG6f0Ak=",
|
||||
"path": "google.golang.org/grpc",
|
||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
||||
"revisionTime": "2016-11-02T18:01:57Z"
|
||||
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||
"revisionTime": "2016-11-15T20:54:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "08icuA15HRkdYCt6H+Cs90RPQsY=",
|
||||
"path": "google.golang.org/grpc/codes",
|
||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
||||
"revisionTime": "2016-11-02T18:01:57Z"
|
||||
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||
"revisionTime": "2016-11-15T20:54:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Vd1MU+Ojs7GeS6jE52vlxtXvIrI=",
|
||||
"path": "google.golang.org/grpc/credentials",
|
||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
||||
"revisionTime": "2016-11-02T18:01:57Z"
|
||||
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||
"revisionTime": "2016-11-15T20:54:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "3Lt5hNAG8qJAYSsNghR5uA1zQns=",
|
||||
"path": "google.golang.org/grpc/grpclog",
|
||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
||||
"revisionTime": "2016-11-02T18:01:57Z"
|
||||
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||
"revisionTime": "2016-11-15T20:54:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "T3Q0p8kzvXFnRkMaK/G8mCv6mc0=",
|
||||
"path": "google.golang.org/grpc/internal",
|
||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
||||
"revisionTime": "2016-11-02T18:01:57Z"
|
||||
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||
"revisionTime": "2016-11-15T20:54:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "P64GkSdsTZ8Nxop5HYqZJ6e+iHs=",
|
||||
"path": "google.golang.org/grpc/metadata",
|
||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
||||
"revisionTime": "2016-11-02T18:01:57Z"
|
||||
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||
"revisionTime": "2016-11-15T20:54:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "4GSUFhOQ0kdFlBH4D5OTeKy78z0=",
|
||||
"path": "google.golang.org/grpc/naming",
|
||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
||||
"revisionTime": "2016-11-02T18:01:57Z"
|
||||
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||
"revisionTime": "2016-11-15T20:54:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "3RRoLeH6X2//7tVClOVzxW2bY+E=",
|
||||
"path": "google.golang.org/grpc/peer",
|
||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
||||
"revisionTime": "2016-11-02T18:01:57Z"
|
||||
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||
"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",
|
||||
"revision": "f8b3c6010888dd5f12b97390791d75ff43453836",
|
||||
"revisionTime": "2016-11-02T18:01:57Z"
|
||||
"revision": "941cc894cea3c87a12943fd12b594964541b6d28",
|
||||
"revisionTime": "2016-11-15T20:54:09Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "wSu8owMAP7GixsYoSZ4CmKUVhnU=",
|
||||
|
|
Loading…
Reference in New Issue