<commit_before>package buildah

import (
	"context"
	"fmt"
	"io"
	"strings"

	"github.com/containers/buildah/pkg/blobcache"
	"github.com/containers/buildah/util"
	cp "github.com/containers/image/copy"
	"github.com/containers/image/directory"
	"github.com/containers/image/docker"
	dockerarchive "github.com/containers/image/docker/archive"
	"github.com/containers/image/docker/reference"
	tarfile "github.com/containers/image/docker/tarfile"
	ociarchive "github.com/containers/image/oci/archive"
	oci "github.com/containers/image/oci/layout"
	"github.com/containers/image/signature"
	is "github.com/containers/image/storage"
	"github.com/containers/image/transports"
	"github.com/containers/image/transports/alltransports"
	"github.com/containers/image/types"
	"github.com/containers/storage"
	multierror "github.com/hashicorp/go-multierror"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// PullOptions can be used to alter how an image is copied in from somewhere.
type PullOptions struct {
	// SignaturePolicyPath specifies an override location for the signature
	// policy which should be used for verifying the new image as it is
	// being written.  Except in specific circumstances, no value should be
	// specified, indicating that the shared, system-wide default policy
	// should be used.
	SignaturePolicyPath string
	// ReportWriter is an io.Writer which will be used to log the writing
	// of the new image.
	ReportWriter io.Writer
	// Store is the local storage store which holds the source image.
	Store storage.Store
	// github.com/containers/image/types SystemContext to hold credentials
	// and other authentication/authorization information.
	SystemContext *types.SystemContext
	// BlobDirectory is the name of a directory in which we'll attempt to
	// store copies of layer blobs that we pull down, if any.  It should
	// already exist.
	BlobDirectory string
	// AllTags is a boolean value that determines if all tagged images
	// will be downloaded from the repository. The default is false.
	AllTags bool
}

func localImageNameForReference(ctx context.Context, store storage.Store, srcRef types.ImageReference) (string, error) {
	if srcRef == nil {
		return "", errors.Errorf("reference to image is empty")
	}
	var name string
	switch srcRef.Transport().Name() {
	case dockerarchive.Transport.Name():
		file := srcRef.StringWithinTransport()
		tarSource, err := tarfile.NewSourceFromFile(file)
		if err != nil {
			return "", errors.Wrapf(err, "error opening tarfile %q as a source image", file)
		}
		manifest, err := tarSource.LoadTarManifest()
		if err != nil {
			return "", errors.Errorf("error retrieving manifest.json from tarfile %q: %v", file, err)
		}
		// to pull the first image stored in the tar file
		if len(manifest) == 0 {
			// use the hex of the digest if no manifest is found
			name, err = getImageDigest(ctx, srcRef, nil)
			if err != nil {
				return "", err
			}
		} else {
			if len(manifest[0].RepoTags) > 0 {
				name = manifest[0].RepoTags[0]
			} else {
				// If the input image has no repotags, we need to feed it a dest anyways
				name, err = getImageDigest(ctx, srcRef, nil)
				if err != nil {
					return "", err
				}
			}
		}
	case ociarchive.Transport.Name():
		// retrieve the manifest from index.json to access the image name
		manifest, err := ociarchive.LoadManifestDescriptor(srcRef)
		if err != nil {
			return "", errors.Wrapf(err, "error loading manifest for %q", transports.ImageName(srcRef))
		}
		// if index.json has no reference name, compute the image digest instead
		if manifest.Annotations == nil || manifest.Annotations["org.opencontainers.image.ref.name"] == "" {
			name, err = getImageDigest(ctx, srcRef, nil)
			if err != nil {
				return "", err
			}
		} else {
			name = manifest.Annotations["org.opencontainers.image.ref.name"]
		}
	case directory.Transport.Name():
		// supports pull from a directory
		name = srcRef.StringWithinTransport()
		// remove leading "/"
		if name[:1] == "/" {
			name = name[1:]
		}
	case oci.Transport.Name():
		// supports pull from a directory
		split := strings.SplitN(srcRef.StringWithinTransport(), ":", 2)
		name = split[0]
		// remove leading "/"
		if name[:1] == "/" {
			name = name[1:]
		}
	default:
		ref := srcRef.DockerReference()
		if ref == nil {
			name = srcRef.StringWithinTransport()
			_, err := is.Transport.ParseStoreReference(store, name)
			if err == nil {
				return name, nil
			}
			logrus.Debugf("error parsing local storage reference %q: %v", name, err)
			if strings.LastIndex(name, "/") != -1 {
				name = name[strings.LastIndex(name, "/")+1:]
				_, err = is.Transport.ParseStoreReference(store, name)
				if err == nil {
					return name, errors.Wrapf(err, "error parsing local storage reference %q", name)
				}
			}
			return "", errors.Errorf("reference to image %q is not a named reference", transports.ImageName(srcRef))
		}

		if named, ok := ref.(reference.Named); ok {
			name = named.Name()
			if namedTagged, ok := ref.(reference.NamedTagged); ok {
				name = name + ":" + namedTagged.Tag()
			}
			if canonical, ok := ref.(reference.Canonical); ok {
				name = name + "@" + canonical.Digest().String()
			}
		}
	}

	if _, err := is.Transport.ParseStoreReference(store, name); err != nil {
		return "", errors.Wrapf(err, "error parsing computed local image name %q", name)
	}
	return name, nil
}

// Pull copies the contents of the image from somewhere else to local storage.
func Pull(ctx context.Context, imageName string, options PullOptions) error {
	systemContext := getSystemContext(options.SystemContext, options.SignaturePolicyPath)

	boptions := BuilderOptions{
		FromImage:           imageName,
		SignaturePolicyPath: options.SignaturePolicyPath,
		SystemContext:       systemContext,
		PullBlobDirectory:   options.BlobDirectory,
		ReportWriter:        options.ReportWriter,
	}

	storageRef, transport, img, err := resolveImage(ctx, systemContext, options.Store, boptions)
	if err != nil {
		return err
	}

	var errs *multierror.Error
	if options.AllTags {
		if transport != util.DefaultTransport {
			return errors.New("Non-docker transport is not supported, for --all-tags pulling")
		}

		repo := reference.TrimNamed(storageRef.DockerReference())
		dockerRef, err := alltransports.ParseImageName(transport + storageRef.DockerReference().String())
		if err != nil {
			return errors.Wrapf(err, "error getting repository tags")
		}
		tags, err := docker.GetRepositoryTags(ctx, systemContext, dockerRef)
		if err != nil {
			return errors.Wrapf(err, "error getting repository tags")
		}
		for _, tag := range tags {
			tagged, err := reference.WithTag(repo, tag)
			if err != nil {
				errs = multierror.Append(errs, err)
				continue
			}
			taggedRef, err := docker.NewReference(tagged)
			if err != nil {
				return errors.Wrapf(err, "internal error creating docker.Transport reference for %s", tagged.String())
			}
			if options.ReportWriter != nil {
				options.ReportWriter.Write([]byte("Pulling " + tagged.String() + "\n"))
			}
			ref, err := pullImage(ctx, options.Store, taggedRef, options, systemContext)
			if err != nil {
				errs = multierror.Append(errs, err)
				continue
			}
			taggedImg, err := is.Transport.GetStoreImage(options.Store, ref)
			if err != nil {
				errs = multierror.Append(errs, err)
				continue
			}
			fmt.Printf("%s\n", taggedImg.ID)
		}
	} else {
		fmt.Printf("%s\n", img.ID)
	}

	return errs.ErrorOrNil()
}

func pullImage(ctx context.Context, store storage.Store, srcRef types.ImageReference, options PullOptions, sc *types.SystemContext) (types.ImageReference, error) {
	blocked, err := isReferenceBlocked(srcRef, sc)
	if err != nil {
		return nil, errors.Wrapf(err, "error checking if pulling from registry for %q is blocked", transports.ImageName(srcRef))
	}
	if blocked {
		return nil, errors.Errorf("pull access to registry for %q is blocked by configuration", transports.ImageName(srcRef))
	}

	destName, err := localImageNameForReference(ctx, store, srcRef)
	if err != nil {
		return nil, errors.Wrapf(err, "error computing local image name for %q", transports.ImageName(srcRef))
	}
	if destName == "" {
		return nil, errors.Errorf("error computing local image name for %q", transports.ImageName(srcRef))
	}

	destRef, err := is.Transport.ParseStoreReference(store, destName)
	if err != nil {
		return nil, errors.Wrapf(err, "error parsing image name %q", destName)
	}
	var maybeCachedDestRef types.ImageReference = destRef
	if options.BlobDirectory != "" {
		cachedRef, err := blobcache.NewBlobCache(destRef, options.BlobDirectory, types.PreserveOriginal)
		if err != nil {
			return nil, errors.Wrapf(err, "error wrapping image reference %q in blob cache at %q", transports.ImageName(destRef), options.BlobDirectory)
		}
		maybeCachedDestRef = cachedRef
	}

	policy, err := signature.DefaultPolicy(sc)
	if err != nil {
		return nil, errors.Wrapf(err, "error obtaining default signature policy")
	}

	policyContext, err := signature.NewPolicyContext(policy)
	if err != nil {
		return nil, errors.Wrapf(err, "error creating new signature policy context")
	}

	defer func() {
		if err2 := policyContext.Destroy(); err2 != nil {
			logrus.Debugf("error destroying signature policy context: %v", err2)
		}
	}()

	logrus.Debugf("copying %q to %q", transports.ImageName(srcRef), destName)
	if _, err := cp.Image(ctx, policyContext, maybeCachedDestRef, srcRef, getCopyOptions(options.ReportWriter, srcRef, sc, maybeCachedDestRef, nil, "")); err != nil {
		logrus.Debugf("error copying src image [%q] to dest image [%q] err: %v", transports.ImageName(srcRef), destName, err)
		return nil, err
	}
	return destRef, nil
}

// getImageDigest creates an image object and uses the hex value of the digest as the image ID
// for parsing the store reference
func getImageDigest(ctx context.Context, src types.ImageReference, sc *types.SystemContext) (string, error) {
	newImg, err := src.NewImage(ctx, sc)
	if err != nil {
		return "", errors.Wrapf(err, "error opening image %q for reading", transports.ImageName(src))
	}
	defer newImg.Close()

	digest := newImg.ConfigInfo().Digest
	if err = digest.Validate(); err != nil {
		return "", errors.Wrapf(err, "error getting config info from image %q", transports.ImageName(src))
	}
	return "@" + digest.Hex(), nil
}
<commit_msg>Hard-code docker.Transport use in pull --all-tags<commit_after>package buildah

import (
	"context"
	"fmt"
	"io"
	"strings"

	"github.com/containers/buildah/pkg/blobcache"
	"github.com/containers/buildah/util"
	cp "github.com/containers/image/copy"
	"github.com/containers/image/directory"
	"github.com/containers/image/docker"
	dockerarchive "github.com/containers/image/docker/archive"
	"github.com/containers/image/docker/reference"
	tarfile "github.com/containers/image/docker/tarfile"
	ociarchive "github.com/containers/image/oci/archive"
	oci "github.com/containers/image/oci/layout"
	"github.com/containers/image/signature"
	is "github.com/containers/image/storage"
	"github.com/containers/image/transports"
	"github.com/containers/image/types"
	"github.com/containers/storage"
	multierror "github.com/hashicorp/go-multierror"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// PullOptions can be used to alter how an image is copied in from somewhere.
type PullOptions struct {
	// SignaturePolicyPath specifies an override location for the signature
	// policy which should be used for verifying the new image as it is
	// being written.  Except in specific circumstances, no value should be
	// specified, indicating that the shared, system-wide default policy
	// should be used.
	SignaturePolicyPath string
	// ReportWriter is an io.Writer which will be used to log the writing
	// of the new image.
	ReportWriter io.Writer
	// Store is the local storage store which holds the source image.
	Store storage.Store
	// github.com/containers/image/types SystemContext to hold credentials
	// and other authentication/authorization information.
	SystemContext *types.SystemContext
	// BlobDirectory is the name of a directory in which we'll attempt to
	// store copies of layer blobs that we pull down, if any.  It should
	// already exist.
	BlobDirectory string
	// AllTags is a boolean value that determines if all tagged images
	// will be downloaded from the repository. The default is false.
	AllTags bool
}

func localImageNameForReference(ctx context.Context, store storage.Store, srcRef types.ImageReference) (string, error) {
	if srcRef == nil {
		return "", errors.Errorf("reference to image is empty")
	}
	var name string
	switch srcRef.Transport().Name() {
	case dockerarchive.Transport.Name():
		file := srcRef.StringWithinTransport()
		tarSource, err := tarfile.NewSourceFromFile(file)
		if err != nil {
			return "", errors.Wrapf(err, "error opening tarfile %q as a source image", file)
		}
		manifest, err := tarSource.LoadTarManifest()
		if err != nil {
			return "", errors.Errorf("error retrieving manifest.json from tarfile %q: %v", file, err)
		}
		// to pull the first image stored in the tar file
		if len(manifest) == 0 {
			// use the hex of the digest if no manifest is found
			name, err = getImageDigest(ctx, srcRef, nil)
			if err != nil {
				return "", err
			}
		} else {
			if len(manifest[0].RepoTags) > 0 {
				name = manifest[0].RepoTags[0]
			} else {
				// If the input image has no repotags, we need to feed it a dest anyways
				name, err = getImageDigest(ctx, srcRef, nil)
				if err != nil {
					return "", err
				}
			}
		}
	case ociarchive.Transport.Name():
		// retrieve the manifest from index.json to access the image name
		manifest, err := ociarchive.LoadManifestDescriptor(srcRef)
		if err != nil {
			return "", errors.Wrapf(err, "error loading manifest for %q", transports.ImageName(srcRef))
		}
		// if index.json has no reference name, compute the image digest instead
		if manifest.Annotations == nil || manifest.Annotations["org.opencontainers.image.ref.name"] == "" {
			name, err = getImageDigest(ctx, srcRef, nil)
			if err != nil {
				return "", err
			}
		} else {
			name = manifest.Annotations["org.opencontainers.image.ref.name"]
		}
	case directory.Transport.Name():
		// supports pull from a directory
		name = srcRef.StringWithinTransport()
		// remove leading "/"
		if name[:1] == "/" {
			name = name[1:]
		}
	case oci.Transport.Name():
		// supports pull from a directory
		split := strings.SplitN(srcRef.StringWithinTransport(), ":", 2)
		name = split[0]
		// remove leading "/"
		if name[:1] == "/" {
			name = name[1:]
		}
	default:
		ref := srcRef.DockerReference()
		if ref == nil {
			name = srcRef.StringWithinTransport()
			_, err := is.Transport.ParseStoreReference(store, name)
			if err == nil {
				return name, nil
			}
			logrus.Debugf("error parsing local storage reference %q: %v", name, err)
			if strings.LastIndex(name, "/") != -1 {
				name = name[strings.LastIndex(name, "/")+1:]
				_, err = is.Transport.ParseStoreReference(store, name)
				if err == nil {
					return name, errors.Wrapf(err, "error parsing local storage reference %q", name)
				}
			}
			return "", errors.Errorf("reference to image %q is not a named reference", transports.ImageName(srcRef))
		}

		if named, ok := ref.(reference.Named); ok {
			name = named.Name()
			if namedTagged, ok := ref.(reference.NamedTagged); ok {
				name = name + ":" + namedTagged.Tag()
			}
			if canonical, ok := ref.(reference.Canonical); ok {
				name = name + "@" + canonical.Digest().String()
			}
		}
	}

	if _, err := is.Transport.ParseStoreReference(store, name); err != nil {
		return "", errors.Wrapf(err, "error parsing computed local image name %q", name)
	}
	return name, nil
}

// Pull copies the contents of the image from somewhere else to local storage.
func Pull(ctx context.Context, imageName string, options PullOptions) error {
	systemContext := getSystemContext(options.SystemContext, options.SignaturePolicyPath)

	boptions := BuilderOptions{
		FromImage:           imageName,
		SignaturePolicyPath: options.SignaturePolicyPath,
		SystemContext:       systemContext,
		PullBlobDirectory:   options.BlobDirectory,
		ReportWriter:        options.ReportWriter,
	}

	storageRef, transport, img, err := resolveImage(ctx, systemContext, options.Store, boptions)
	if err != nil {
		return err
	}

	var errs *multierror.Error
	if options.AllTags {
		if transport != util.DefaultTransport {
			return errors.New("Non-docker transport is not supported, for --all-tags pulling")
		}

		repo := reference.TrimNamed(storageRef.DockerReference())
		dockerRef, err := docker.NewReference(reference.TagNameOnly(storageRef.DockerReference()))
		if err != nil {
			return errors.Wrapf(err, "internal error creating docker.Transport reference for %s", storageRef.DockerReference().String())
		}
		tags, err := docker.GetRepositoryTags(ctx, systemContext, dockerRef)
		if err != nil {
			return errors.Wrapf(err, "error getting repository tags")
		}
		for _, tag := range tags {
			tagged, err := reference.WithTag(repo, tag)
			if err != nil {
				errs = multierror.Append(errs, err)
				continue
			}
			taggedRef, err := docker.NewReference(tagged)
			if err != nil {
				return errors.Wrapf(err, "internal error creating docker.Transport reference for %s", tagged.String())
			}
			if options.ReportWriter != nil {
				options.ReportWriter.Write([]byte("Pulling " + tagged.String() + "\n"))
			}
			ref, err := pullImage(ctx, options.Store, taggedRef, options, systemContext)
			if err != nil {
				errs = multierror.Append(errs, err)
				continue
			}
			taggedImg, err := is.Transport.GetStoreImage(options.Store, ref)
			if err != nil {
				errs = multierror.Append(errs, err)
				continue
			}
			fmt.Printf("%s\n", taggedImg.ID)
		}
	} else {
		fmt.Printf("%s\n", img.ID)
	}

	return errs.ErrorOrNil()
}

func pullImage(ctx context.Context, store storage.Store, srcRef types.ImageReference, options PullOptions, sc *types.SystemContext) (types.ImageReference, error) {
	blocked, err := isReferenceBlocked(srcRef, sc)
	if err != nil {
		return nil, errors.Wrapf(err, "error checking if pulling from registry for %q is blocked", transports.ImageName(srcRef))
	}
	if blocked {
		return nil, errors.Errorf("pull access to registry for %q is blocked by configuration", transports.ImageName(srcRef))
	}

	destName, err := localImageNameForReference(ctx, store, srcRef)
	if err != nil {
		return nil, errors.Wrapf(err, "error computing local image name for %q", transports.ImageName(srcRef))
	}
	if destName == "" {
		return nil, errors.Errorf("error computing local image name for %q", transports.ImageName(srcRef))
	}

	destRef, err := is.Transport.ParseStoreReference(store, destName)
	if err != nil {
		return nil, errors.Wrapf(err, "error parsing image name %q", destName)
	}
	var maybeCachedDestRef types.ImageReference = destRef
	if options.BlobDirectory != "" {
		cachedRef, err := blobcache.NewBlobCache(destRef, options.BlobDirectory, types.PreserveOriginal)
		if err != nil {
			return nil, errors.Wrapf(err, "error wrapping image reference %q in blob cache at %q", transports.ImageName(destRef), options.BlobDirectory)
		}
		maybeCachedDestRef = cachedRef
	}

	policy, err := signature.DefaultPolicy(sc)
	if err != nil {
		return nil, errors.Wrapf(err, "error obtaining default signature policy")
	}

	policyContext, err := signature.NewPolicyContext(policy)
	if err != nil {
		return nil, errors.Wrapf(err, "error creating new signature policy context")
	}

	defer func() {
		if err2 := policyContext.Destroy(); err2 != nil {
			logrus.Debugf("error destroying signature policy context: %v", err2)
		}
	}()

	logrus.Debugf("copying %q to %q", transports.ImageName(srcRef), destName)
	if _, err := cp.Image(ctx, policyContext, maybeCachedDestRef, srcRef, getCopyOptions(options.ReportWriter, srcRef, sc, maybeCachedDestRef, nil, "")); err != nil {
		logrus.Debugf("error copying src image [%q] to dest image [%q] err: %v", transports.ImageName(srcRef), destName, err)
		return nil, err
	}
	return destRef, nil
}

// getImageDigest creates an image object and uses the hex value of the digest as the image ID
// for parsing the store reference
func getImageDigest(ctx context.Context, src types.ImageReference, sc *types.SystemContext) (string, error) {
	newImg, err := src.NewImage(ctx, sc)
	if err != nil {
		return "", errors.Wrapf(err, "error opening image %q for reading", transports.ImageName(src))
	}
	defer newImg.Close()

	digest := newImg.ConfigInfo().Digest
	if err = digest.Validate(); err != nil {
		return "", errors.Wrapf(err, "error getting config info from image %q", transports.ImageName(src))
	}
	return "@" + digest.Hex(), nil
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// This package partially implements the TLS 1.1 protocol, as specified in RFC 4346.
package tls

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"net"
	"os"
	"strings"
)

// Server returns a new TLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must have
// at least one certificate.
func Server(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config}
}

// Client returns a new TLS client side connection
// using conn as the underlying transport.
// Client interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Client(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config, isClient: true}
}

// A Listener implements a network listener (net.Listener) for TLS connections.
type Listener struct {
	listener net.Listener
	config   *Config
}

// Accept waits for and returns the next incoming TLS connection.
// The returned connection c is a *tls.Conn.
func (l *Listener) Accept() (c net.Conn, err os.Error) {
	c, err = l.listener.Accept()
	if err != nil {
		return
	}
	c = Server(c, l.config)
	return
}

// Close closes the listener.
func (l *Listener) Close() os.Error { return l.listener.Close() }

// Addr returns the listener's network address.
func (l *Listener) Addr() net.Addr { return l.listener.Addr() }

// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
// The configuration config must be non-nil and must have
// at least one certificate.
func NewListener(listener net.Listener, config *Config) (l *Listener) {
	l = new(Listener)
	l.listener = listener
	l.config = config
	return
}

// Listen creates a TLS listener accepting connections on the
// given network address using net.Listen.
// The configuration config must be non-nil and must have
// at least one certificate.
func Listen(network, laddr string, config *Config) (*Listener, os.Error) {
	if config == nil || len(config.Certificates) == 0 {
		return nil, os.NewError("tls.Listen: no certificates in configuration")
	}
	l, err := net.Listen(network, laddr)
	if err != nil {
		return nil, err
	}
	return NewListener(l, config), nil
}

// Dial connects to the given network address using net.Dial
// and then initiates a TLS handshake, returning the resulting
// TLS connection.
// Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Dial(network, laddr, raddr string, config *Config) (*Conn, os.Error) {
	c, err := net.Dial(network, laddr, raddr)
	if err != nil {
		return nil, err
	}

	colonPos := strings.LastIndex(raddr, ":")
	if colonPos == -1 {
		colonPos = len(raddr)
	}
	hostname := raddr[:colonPos]

	if config == nil {
		config = defaultConfig()
	}
	if config.ServerName != "" {
		// Make a copy to avoid polluting argument or default.
		c := *config
		c.ServerName = hostname
		config = &c
	}
	conn := Client(c, config)
	if err = conn.Handshake(); err != nil {
		c.Close()
		return nil, err
	}
	return conn, nil
}

// LoadX509KeyPair reads and parses a public/private key pair from a pair of
// files. The files must contain PEM encoded data.
func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os.Error) {
	certPEMBlock, err := ioutil.ReadFile(certFile)
	if err != nil {
		return
	}

	certDERBlock, _ := pem.Decode(certPEMBlock)
	if certDERBlock == nil {
		err = os.ErrorString("crypto/tls: failed to parse certificate PEM data")
		return
	}

	cert.Certificate = [][]byte{certDERBlock.Bytes}

	keyPEMBlock, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return
	}

	keyDERBlock, _ := pem.Decode(keyPEMBlock)
	if keyDERBlock == nil {
		err = os.ErrorString("crypto/tls: failed to parse key PEM data")
		return
	}

	key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
	if err != nil {
		err = os.ErrorString("crypto/tls: failed to parse key")
		return
	}

	cert.PrivateKey = key

	// We don't need to parse the public key for TLS, but we so do anyway
	// to check that it looks sane and matches the private key.
	x509Cert, err := x509.ParseCertificate(certDERBlock.Bytes)
	if err != nil {
		return
	}

	if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 {
		err = os.ErrorString("crypto/tls: private key does not match public key")
		return
	}

	return
}
<commit_msg>crypto/tls: load a chain of certificates from a file.<commit_after>// Copyright 2009 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.

// This package partially implements the TLS 1.1 protocol, as specified in RFC 4346.
package tls

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"net"
	"os"
	"strings"
)

// Server returns a new TLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must have
// at least one certificate.
func Server(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config}
}

// Client returns a new TLS client side connection
// using conn as the underlying transport.
// Client interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Client(conn net.Conn, config *Config) *Conn {
	return &Conn{conn: conn, config: config, isClient: true}
}

// A Listener implements a network listener (net.Listener) for TLS connections.
type Listener struct {
	listener net.Listener
	config   *Config
}

// Accept waits for and returns the next incoming TLS connection.
// The returned connection c is a *tls.Conn.
func (l *Listener) Accept() (c net.Conn, err os.Error) {
	c, err = l.listener.Accept()
	if err != nil {
		return
	}
	c = Server(c, l.config)
	return
}

// Close closes the listener.
func (l *Listener) Close() os.Error { return l.listener.Close() }

// Addr returns the listener's network address.
func (l *Listener) Addr() net.Addr { return l.listener.Addr() }

// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
// The configuration config must be non-nil and must have
// at least one certificate.
func NewListener(listener net.Listener, config *Config) (l *Listener) {
	l = new(Listener)
	l.listener = listener
	l.config = config
	return
}

// Listen creates a TLS listener accepting connections on the
// given network address using net.Listen.
// The configuration config must be non-nil and must have
// at least one certificate.
func Listen(network, laddr string, config *Config) (*Listener, os.Error) {
	if config == nil || len(config.Certificates) == 0 {
		return nil, os.NewError("tls.Listen: no certificates in configuration")
	}
	l, err := net.Listen(network, laddr)
	if err != nil {
		return nil, err
	}
	return NewListener(l, config), nil
}

// Dial connects to the given network address using net.Dial
// and then initiates a TLS handshake, returning the resulting
// TLS connection.
// Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Dial(network, laddr, raddr string, config *Config) (*Conn, os.Error) {
	c, err := net.Dial(network, laddr, raddr)
	if err != nil {
		return nil, err
	}

	colonPos := strings.LastIndex(raddr, ":")
	if colonPos == -1 {
		colonPos = len(raddr)
	}
	hostname := raddr[:colonPos]

	if config == nil {
		config = defaultConfig()
	}
	if config.ServerName != "" {
		// Make a copy to avoid polluting argument or default.
		c := *config
		c.ServerName = hostname
		config = &c
	}
	conn := Client(c, config)
	if err = conn.Handshake(); err != nil {
		c.Close()
		return nil, err
	}
	return conn, nil
}

// LoadX509KeyPair reads and parses a public/private key pair from a pair of
// files. The files must contain PEM encoded data.
func LoadX509KeyPair(certFile string, keyFile string) (cert Certificate, err os.Error) {
	certPEMBlock, err := ioutil.ReadFile(certFile)
	if err != nil {
		return
	}

	var certDERBlock *pem.Block
	for {
		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
		if certDERBlock == nil {
			break
		}
		if certDERBlock.Type == "CERTIFICATE" {
			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
		}
	}

	if len(cert.Certificate) == 0 {
		err = os.ErrorString("crypto/tls: failed to parse certificate PEM data")
		return
	}

	keyPEMBlock, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return
	}

	keyDERBlock, _ := pem.Decode(keyPEMBlock)
	if keyDERBlock == nil {
		err = os.ErrorString("crypto/tls: failed to parse key PEM data")
		return
	}

	key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
	if err != nil {
		err = os.ErrorString("crypto/tls: failed to parse key")
		return
	}

	cert.PrivateKey = key

	// We don't need to parse the public key for TLS, but we so do anyway
	// to check that it looks sane and matches the private key.
	x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
	if err != nil {
		return
	}

	if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 {
		err = os.ErrorString("crypto/tls: private key does not match public key")
		return
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/mdigger/log"
)

// SendPush отправляет уведомление на все зарегистрированные токены устройств
// пользователя.
func (p *Proxy) SendPush(userID string, payload interface{}) error {
	// преобразуем данные для пуша в формат JSON
	var dataPayload []byte
	switch data := payload.(type) {
	case []byte:
		dataPayload = data
	case string:
		dataPayload = []byte(data)
	case json.RawMessage:
		dataPayload = []byte(data)
	default:
		var err error
		dataPayload, err = json.Marshal(payload)
		if err != nil {
			log.WithError(err).Error("push payload to json error")
			return err
		}
	}

	ctxlog := log.WithField("user", userID)
	// получаем список зарегистрированных устройств пользователя
	userDeviceTokens, err := p.store.Tokens(userID)
	if err != nil {
		ctxlog.WithError(err).Error("get users tokens from store error")
		return err
	}
	// разбираем токены устройств по их типу и topic id
	for topicID, tokens := range userDeviceTokens {
		// на всякий случай проверяем длину ключа, чтобы не было ошибок
		if len(tokens) == 0 {
			continue
		}
		ctxlog := ctxlog.WithField("topicID", topicID)
		// находим разделитель типа
		devider := strings.IndexByte(topicID, ':')
		if devider < 0 || devider == len(topicID)-1 {
			ctxlog.Warning("bad topicID type")
			continue
		}
		// в зависимости от типов пушей различается их обработка
		switch topicID[:devider] {
		case "apn": // Apple Push
			p.sendPushAPN(userID, topicID[devider+1:], tokens, dataPayload)
		case "fcm": // Google Cloud Message
			p.sendPushFCM(userID, topicID[devider+1:], tokens, dataPayload)
		}
	}
	return nil
}

var (
	// PushTimeout задает максимальное время ожидание отправки уведомления
	PushTimeout = time.Second * 5
	userAgent   = fmt.Sprintf("%s/%s", appName, version) // имя агента для пушей
)

// sendPushAPN отправляет уведомления через Apple Push Notification.
func (p *Proxy) sendPushAPN(userID, topicID string, tokens map[string]time.Time,
	payload []byte) {
	ctxlog := log.WithFields(log.Fields{
		"topicID": topicID,
		"user":    userID,
	})
	var transport = p.apns.Get(topicID)
	if transport == nil {
		ctxlog.Warning("apns push topicID ignored")
		return // тема не поддерживается
	}
	var client = &http.Client{
		Transport: transport,
		Timeout:   PushTimeout,
	}
	// задаем хост в зависимости от sandbox
	var host string
	if topicID[len(topicID)-1] != '~' {
		host = "https://api.push.apple.com"
	} else {
		host = "https://api.development.push.apple.com"
	}
	// для каждого токена устройства формируем отдельный запрос
	var success, failure int // счетчики
	for token := range tokens {
		req, _ := http.NewRequest("POST", host+"/3/device/"+token,
			bytes.NewReader(payload))
		req.Header.Set("user-agent", userAgent)
		req.Header.Set("Content-Type", "application/json")
		resp, err := client.Do(req)
		if err != nil {
			// в случае ошибки отправки пуша прекращаем обработку
			// данного bundle
			ctxlog.WithError(err).Error("apple push error")
			break
		}
		if resp.StatusCode == http.StatusOK {
			resp.Body.Close()
			success++
			continue
		}
		failure++
		// разбираем ответ сервера с описанием ошибки
		var apnsError = new(struct {
			Reason string `json:"reason"`
		})
		err = json.NewDecoder(resp.Body).Decode(apnsError)
		resp.Body.Close()
		if err != nil {
			ctxlog.WithError(err).Error("apple push response decode error")
			continue
		}
		ctxlog = ctxlog.WithField("error", apnsError.Reason)
		// в случае ошибки связанной с токеном устройства, удаляем его
		switch apnsError.Reason {
		case "MissingDeviceToken",
			"BadDeviceToken",
			"DeviceTokenNotForTopic",
			"Unregistered":
			p.store.TokenRemove(topicID, token)
			ctxlog.Debug("remove apple bad token")
		default:
			ctxlog.Error("apple push error")
		}
	}
	log.WithFields(log.Fields{
		"success": success,
		"failure": failure,
	}).Info("apple push sended")
}

// sendPushFCM отправляет уведомления через Firebase Cloud Messages.
func (p *Proxy) sendPushFCM(userID, topicID string, tokens map[string]time.Time,
	payload []byte) {
	ctxlog := log.WithFields(log.Fields{
		"topicID": topicID,
		"user":    userID,
	})
	// получаем ключ для авторизации пуша для приложения
	// игнорируем все незарегистрированные типы приложений
	key, ok := p.fcm[topicID]
	if !ok {
		ctxlog.Warning("push bundle ignored")
		return
	}
	// формируем список токенов устройств
	var bundleTokens = make([]string, 0, len(tokens))
	for token := range tokens {
		bundleTokens = append(bundleTokens, token)
	}
	// формируем данные для отправки (без визуальной составляющей пуша:
	// только данные)
	var gfcmMsg = &struct {
		RegistrationIDs []string        `json:"registration_ids,omitempty"`
		Data            json.RawMessage `json:"data,omitempty"`
		TTL             uint16          `json:"time_to_live"`
	}{
		// т.к. тут только устройства ОДНОГО пользователя, то
		// ограничением на количество токенов можно пренебречь
		RegistrationIDs: bundleTokens,
		// BUG(d3): данный хак позволяет не формировать второй раз
		// представление данных в формате JSON, но накладывает ограничения
		// на использование ИСКЛЮЧИТЕЛЬНО не визуальных данных
		Data: payload, // добавляем уже сформированные ранее данные
		// время жизни сообщения TTL = 0, поэтому оно не кешируется
		// на сервере, а сразу отправляется пользователю: для пушей
		// оо звонках мне показалось это наиболее актуальным.
	}
	// приводим к формату JSON
	data, err := json.Marshal(gfcmMsg)
	if err != nil {
		ctxlog.WithError(err).Error("google push data create error")
		return
	}
	req, _ := http.NewRequest("POST",
		"https://fcm.googleapis.com/fcm/send", bytes.NewReader(data))
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "key="+key)
	var client = &http.Client{Timeout: PushTimeout}
	resp, err := client.Do(req)
	if err != nil {
		ctxlog.WithError(err).Error("google push request error")
		return
	}
	// проверяем статус ответа
	if resp.StatusCode != http.StatusOK {
		ctxlog.WithField("status", resp.StatusCode).
			Error("google push error")
		resp.Body.Close()
		return
	}
	// разбираем ответ сервера
	var result = new(struct {
		Success int `json:"success"`
		Failure int `json:"failure"`
		Results []struct {
			RegistrationID string `json:"registration_id"`
			Error          string `json:"error"`
		} `json:"results"`
	})
	err = json.NewDecoder(resp.Body).Decode(result)
	resp.Body.Close()
	if err != nil {
		ctxlog.WithError(err).Error("google push response decode error")
		return
	}
	// проходим по массиву результатов в ответе для каждого токена
	for indx, result := range result.Results {
		switch result.Error {
		case "":
			// нет ошибки - доставлено
			// проверяем, что, возможно, токен устарел и его нужно
			// заменить на более новый, который указан в ответе
			if result.RegistrationID != "" {
				token := gfcmMsg.RegistrationIDs[indx]
				ctxlog.WithField("token", token).
					Debug("update google push token")
				p.store.TokenRemove(topicID, token)
				p.store.TokenAdd(userID, topicID, result.RegistrationID)
			}
		case "Unavailable":
			// устройство в данный момент не доступно
		default:
			// все остальное представляет из себя, так или иначе,
			// ошибки, связанные с неверным токеном устройства
			token := gfcmMsg.RegistrationIDs[indx]
			ctxlog.WithFields(log.Fields{
				"token": token,
				"error": result.Error,
			}).Debug("remove google bad token")
			p.store.TokenRemove(topicID, token)
		}
	}
	ctxlog.WithFields(log.Fields{
		"success": result.Success,
		"failure": result.Failure,
	}).Info("google push sended")
}
<commit_msg>push error log<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/mdigger/log"
)

// SendPush отправляет уведомление на все зарегистрированные токены устройств
// пользователя.
func (p *Proxy) SendPush(userID string, payload interface{}) error {
	// преобразуем данные для пуша в формат JSON
	var dataPayload []byte
	switch data := payload.(type) {
	case []byte:
		dataPayload = data
	case string:
		dataPayload = []byte(data)
	case json.RawMessage:
		dataPayload = []byte(data)
	default:
		var err error
		dataPayload, err = json.Marshal(payload)
		if err != nil {
			log.WithError(err).Error("push payload to json error")
			return err
		}
	}

	ctxlog := log.WithField("user", userID)
	// получаем список зарегистрированных устройств пользователя
	userDeviceTokens, err := p.store.Tokens(userID)
	if err != nil {
		ctxlog.WithError(err).Error("get users tokens from store error")
		return err
	}
	// разбираем токены устройств по их типу и topic id
	for topicID, tokens := range userDeviceTokens {
		// на всякий случай проверяем длину ключа, чтобы не было ошибок
		if len(tokens) == 0 {
			continue
		}
		ctxlog := ctxlog.WithField("topicID", topicID)
		// находим разделитель типа
		devider := strings.IndexByte(topicID, ':')
		if devider < 0 || devider == len(topicID)-1 {
			ctxlog.Warning("bad topicID type")
			continue
		}
		// в зависимости от типов пушей различается их обработка
		switch topicID[:devider] {
		case "apn": // Apple Push
			p.sendPushAPN(userID, topicID[devider+1:], tokens, dataPayload)
		case "fcm": // Google Cloud Message
			p.sendPushFCM(userID, topicID[devider+1:], tokens, dataPayload)
		}
	}
	return nil
}

var (
	// PushTimeout задает максимальное время ожидание отправки уведомления
	PushTimeout = time.Second * 5
	userAgent   = fmt.Sprintf("%s/%s", appName, version) // имя агента для пушей
)

// sendPushAPN отправляет уведомления через Apple Push Notification.
func (p *Proxy) sendPushAPN(userID, topicID string, tokens map[string]time.Time,
	payload []byte) {
	ctxlog := log.WithFields(log.Fields{
		"topicID": topicID,
		"user":    userID,
	})
	var transport = p.apns.Get(topicID)
	if transport == nil {
		ctxlog.Warning("apns push topicID ignored")
		return // тема не поддерживается
	}
	var client = &http.Client{
		Transport: transport,
		Timeout:   PushTimeout,
	}
	// задаем хост в зависимости от sandbox
	var host string
	if topicID[len(topicID)-1] != '~' {
		host = "https://api.push.apple.com"
	} else {
		host = "https://api.development.push.apple.com"
	}
	// для каждого токена устройства формируем отдельный запрос
	var success, failure int // счетчики
	for token := range tokens {
		req, _ := http.NewRequest("POST", host+"/3/device/"+token,
			bytes.NewReader(payload))
		req.Header.Set("user-agent", userAgent)
		req.Header.Set("Content-Type", "application/json")
		resp, err := client.Do(req)
		if err != nil {
			// в случае ошибки отправки пуша прекращаем обработку
			// данного bundle
			ctxlog.WithError(err).Error("apple push error")
			break
		}
		if resp.StatusCode == http.StatusOK {
			resp.Body.Close()
			success++
			continue
		}
		failure++
		// разбираем ответ сервера с описанием ошибки
		var apnsError = new(struct {
			Reason string `json:"reason"`
		})
		err = json.NewDecoder(resp.Body).Decode(apnsError)
		resp.Body.Close()
		if err != nil {
			ctxlog.WithError(err).Error("apple push response decode error")
			continue
		}
		// в случае ошибки связанной с токеном устройства, удаляем его
		var ctxerr = ctxlog.WithField("error", apnsError.Reason)
		switch apnsError.Reason {
		case "MissingDeviceToken",
			"BadDeviceToken",
			"DeviceTokenNotForTopic",
			"Unregistered":
			p.store.TokenRemove(topicID, token)
			ctxerr.Debug("remove apple bad token")
		default:
			ctxerr.Error("apple push error")
		}
	}
	log.WithFields(log.Fields{
		"success": success,
		"failure": failure,
	}).Info("apple push sended")
}

// sendPushFCM отправляет уведомления через Firebase Cloud Messages.
func (p *Proxy) sendPushFCM(userID, topicID string, tokens map[string]time.Time,
	payload []byte) {
	ctxlog := log.WithFields(log.Fields{
		"topicID": topicID,
		"user":    userID,
	})
	// получаем ключ для авторизации пуша для приложения
	// игнорируем все незарегистрированные типы приложений
	key, ok := p.fcm[topicID]
	if !ok {
		ctxlog.Warning("push bundle ignored")
		return
	}
	// формируем список токенов устройств
	var bundleTokens = make([]string, 0, len(tokens))
	for token := range tokens {
		bundleTokens = append(bundleTokens, token)
	}
	// формируем данные для отправки (без визуальной составляющей пуша:
	// только данные)
	var gfcmMsg = &struct {
		RegistrationIDs []string        `json:"registration_ids,omitempty"`
		Data            json.RawMessage `json:"data,omitempty"`
		TTL             uint16          `json:"time_to_live"`
	}{
		// т.к. тут только устройства ОДНОГО пользователя, то
		// ограничением на количество токенов можно пренебречь
		RegistrationIDs: bundleTokens,
		// BUG(d3): данный хак позволяет не формировать второй раз
		// представление данных в формате JSON, но накладывает ограничения
		// на использование ИСКЛЮЧИТЕЛЬНО не визуальных данных
		Data: payload, // добавляем уже сформированные ранее данные
		// время жизни сообщения TTL = 0, поэтому оно не кешируется
		// на сервере, а сразу отправляется пользователю: для пушей
		// оо звонках мне показалось это наиболее актуальным.
	}
	// приводим к формату JSON
	data, err := json.Marshal(gfcmMsg)
	if err != nil {
		ctxlog.WithError(err).Error("google push data create error")
		return
	}
	req, _ := http.NewRequest("POST",
		"https://fcm.googleapis.com/fcm/send", bytes.NewReader(data))
	req.Header.Set("User-Agent", userAgent)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "key="+key)
	var client = &http.Client{Timeout: PushTimeout}
	resp, err := client.Do(req)
	if err != nil {
		ctxlog.WithError(err).Error("google push request error")
		return
	}
	// проверяем статус ответа
	if resp.StatusCode != http.StatusOK {
		ctxlog.WithField("status", resp.StatusCode).
			Error("google push error")
		resp.Body.Close()
		return
	}
	// разбираем ответ сервера
	var result = new(struct {
		Success int `json:"success"`
		Failure int `json:"failure"`
		Results []struct {
			RegistrationID string `json:"registration_id"`
			Error          string `json:"error"`
		} `json:"results"`
	})
	err = json.NewDecoder(resp.Body).Decode(result)
	resp.Body.Close()
	if err != nil {
		ctxlog.WithError(err).Error("google push response decode error")
		return
	}
	// проходим по массиву результатов в ответе для каждого токена
	for indx, result := range result.Results {
		switch result.Error {
		case "":
			// нет ошибки - доставлено
			// проверяем, что, возможно, токен устарел и его нужно
			// заменить на более новый, который указан в ответе
			if result.RegistrationID != "" {
				token := gfcmMsg.RegistrationIDs[indx]
				ctxlog.WithField("token", token).
					Debug("update google push token")
				p.store.TokenRemove(topicID, token)
				p.store.TokenAdd(userID, topicID, result.RegistrationID)
			}
		case "Unavailable":
			// устройство в данный момент не доступно
		default:
			// все остальное представляет из себя, так или иначе,
			// ошибки, связанные с неверным токеном устройства
			token := gfcmMsg.RegistrationIDs[indx]
			ctxlog.WithFields(log.Fields{
				"token": token,
				"error": result.Error,
			}).Debug("remove google bad token")
			p.store.TokenRemove(topicID, token)
		}
	}
	ctxlog.WithFields(log.Fields{
		"success": result.Success,
		"failure": result.Failure,
	}).Info("google push sended")
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

// Godoc comment extraction and comment -> HTML formatting.

package doc

import (
	"go/ast"
	"io"
	"regexp"
	"strings"
	"template" // for htmlEscape
)

func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' }

func stripTrailingWhitespace(s string) string {
	i := len(s)
	for i > 0 && isWhitespace(s[i-1]) {
		i--
	}
	return s[0:i]
}

// CommentText returns the text of comment,
// with the comment markers - //, /*, and */ - removed.
func CommentText(comment *ast.CommentGroup) string {
	if comment == nil {
		return ""
	}
	comments := make([]string, len(comment.List))
	for i, c := range comment.List {
		comments[i] = string(c.Text)
	}

	lines := make([]string, 0, 10) // most comments are less than 10 lines
	for _, c := range comments {
		// Remove comment markers.
		// The parser has given us exactly the comment text.
		switch c[1] {
		case '/':
			//-style comment
			c = c[2:]
			// Remove leading space after //, if there is one.
			// TODO(gri) This appears to be necessary in isolated
			//           cases (bignum.RatFromString) - why?
			if len(c) > 0 && c[0] == ' ' {
				c = c[1:]
			}
		case '*':
			/*-style comment */
			c = c[2 : len(c)-2]
		}

		// Split on newlines.
		cl := strings.Split(c, "\n", -1)

		// Walk lines, stripping trailing white space and adding to list.
		for _, l := range cl {
			lines = append(lines, stripTrailingWhitespace(l))
		}
	}

	// Remove leading blank lines; convert runs of
	// interior blank lines to a single blank line.
	n := 0
	for _, line := range lines {
		if line != "" || n > 0 && lines[n-1] != "" {
			lines[n] = line
			n++
		}
	}
	lines = lines[0:n]

	// Add final "" entry to get trailing newline from Join.
	// The original loop always leaves room for one more.
	if n > 0 && lines[n-1] != "" {
		lines = lines[0 : n+1]
		lines[n] = ""
	}

	return strings.Join(lines, "\n")
}

// Split bytes into lines.
func split(text []byte) [][]byte {
	// count lines
	n := 0
	last := 0
	for i, c := range text {
		if c == '\n' {
			last = i + 1
			n++
		}
	}
	if last < len(text) {
		n++
	}

	// split
	out := make([][]byte, n)
	last = 0
	n = 0
	for i, c := range text {
		if c == '\n' {
			out[n] = text[last : i+1]
			last = i + 1
			n++
		}
	}
	if last < len(text) {
		out[n] = text[last:]
	}

	return out
}

var (
	ldquo = []byte("“")
	rdquo = []byte("”")
)

// Escape comment text for HTML. If nice is set,
// also turn `` into “ and '' into ”.
func commentEscape(w io.Writer, s []byte, nice bool) {
	last := 0
	if nice {
		for i := 0; i < len(s)-1; i++ {
			ch := s[i]
			if ch == s[i+1] && (ch == '`' || ch == '\'') {
				template.HTMLEscape(w, s[last:i])
				last = i + 2
				switch ch {
				case '`':
					w.Write(ldquo)
				case '\'':
					w.Write(rdquo)
				}
				i++ // loop will add one more
			}
		}
	}
	template.HTMLEscape(w, s[last:])
}

const (
	// Regexp for Go identifiers
	identRx = `[a-zA-Z_][a-zA-Z_0-9]*` // TODO(gri) ASCII only for now - fix this

	// Regexp for URLs
	protocol = `(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero):`
	hostPart = `[a-zA-Z0-9_@\-]+`
	filePart = `[a-zA-Z0-9_?%#~&/\-+=]+`
	urlRx    = protocol + `//` + // http://
		hostPart + `([.:]` + hostPart + `)*/?` + // //www.google.com:8080/
		filePart + `([:.,]` + filePart + `)*`
)

var matchRx = regexp.MustCompile(`(` + identRx + `)|(` + urlRx + `)`)

var (
	html_a      = []byte(`<a href="`)
	html_aq     = []byte(`">`)
	html_enda   = []byte("")
	html_i      = []byte("")
	html_endi   = []byte("")
	html_p      = []byte("
\n")
	html_endp   = []byte("
\n")
	html_pre    = []byte("
")
	html_endpre = []byte("
\n")
)

// Emphasize and escape a line of text for HTML. URLs are converted into links;
// if the URL also appears in the words map, the link is taken from the map (if
// the corresponding map value is the empty string, the URL is not converted
// into a link). Go identifiers that appear in the words map are italicized; if
// the corresponding map value is not the empty string, it is considered a URL
// and the word is converted into a link. If nice is set, the remaining text's
// appearance is improved where it makes sense (e.g., `` is turned into “
// and '' into ”).
func emphasize(w io.Writer, line []byte, words map[string]string, nice bool) {
	for {
		m := matchRx.FindSubmatchIndex(line)
		if m == nil {
			break
		}
		// m >= 6 (two parenthesized sub-regexps in matchRx, 1st one is identRx)

		// write text before match
		commentEscape(w, line[0:m[0]], nice)

		// analyze match
		match := line[m[0]:m[1]]
		url := ""
		italics := false
		if words != nil {
			url, italics = words[string(match)]
		}
		if m[2] < 0 {
			// didn't match against first parenthesized sub-regexp; must be match against urlRx
			if !italics {
				// no alternative URL in words list, use match instead
				url = string(match)
			}
			italics = false // don't italicize URLs
		}

		// write match
		if len(url) > 0 {
			w.Write(html_a)
			template.HTMLEscape(w, []byte(url))
			w.Write(html_aq)
		}
		if italics {
			w.Write(html_i)
		}
		commentEscape(w, match, nice)
		if italics {
			w.Write(html_endi)
		}
		if len(url) > 0 {
			w.Write(html_enda)
		}

		// advance
		line = line[m[1]:]
	}
	commentEscape(w, line, nice)
}

func indentLen(s []byte) int {
	i := 0
	for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
		i++
	}
	return i
}

func isBlank(s []byte) bool { return len(s) == 0 || (len(s) == 1 && s[0] == '\n') }

func commonPrefix(a, b []byte) []byte {
	i := 0
	for i < len(a) && i < len(b) && a[i] == b[i] {
		i++
	}
	return a[0:i]
}

func unindent(block [][]byte) {
	if len(block) == 0 {
		return
	}

	// compute maximum common white prefix
	prefix := block[0][0:indentLen(block[0])]
	for _, line := range block {
		if !isBlank(line) {
			prefix = commonPrefix(prefix, line[0:indentLen(line)])
		}
	}
	n := len(prefix)

	// remove
	for i, line := range block {
		if !isBlank(line) {
			block[i] = line[n:]
		}
	}
}

// Convert comment text to formatted HTML.
// The comment was prepared by DocReader,
// so it is known not to have leading, trailing blank lines
// nor to have trailing spaces at the end of lines.
// The comment markers have already been removed.
//
// Turn each run of multiple \n into

// Turn each run of indented lines into a
 block without indent.
//
// URLs in the comment text are converted into links; if the URL also appears
// in the words map, the link is taken from the map (if the corresponding map
// value is the empty string, the URL is not converted into a link).
//
// Go identifiers that appear in the words map are italicized; if the corresponding
// map value is not the empty string, it is considered a URL and the word is converted
// into a link.
func ToHTML(w io.Writer, s []byte, words map[string]string) {
	inpara := false

	close := func() {
		if inpara {
			w.Write(html_endp)
			inpara = false
		}
	}
	open := func() {
		if !inpara {
			w.Write(html_p)
			inpara = true
		}
	}

	lines := split(s)
	unindent(lines)
	for i := 0; i < len(lines); {
		line := lines[i]
		if isBlank(line) {
			// close paragraph
			close()
			i++
			continue
		}
		if indentLen(line) > 0 {
			// close paragraph
			close()

			// count indented or blank lines
			j := i + 1
			for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
				j++
			}
			// but not trailing blank lines
			for j > i && isBlank(lines[j-1]) {
				j--
			}
			block := lines[i:j]
			i = j

			unindent(block)

			// put those lines in a pre block
			w.Write(html_pre)
			for _, line := range block {
				emphasize(w, line, nil, false) // no nice text formatting
			}
			w.Write(html_endpre)
			continue
		}
		// open paragraph
		open()
		emphasize(w, lines[i], words, true) // nice text formatting
		i++
	}
	close()
}
<commit_msg>godoc: bug fix (bug introduced with revision 3ee58453e961) (thanks to Chris Dollin for identifying the cause)<commit_after>// Copyright 2009 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.

// Godoc comment extraction and comment -> HTML formatting.

package doc

import (
	"go/ast"
	"io"
	"regexp"
	"strings"
	"template" // for htmlEscape
)

func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' }

func stripTrailingWhitespace(s string) string {
	i := len(s)
	for i > 0 && isWhitespace(s[i-1]) {
		i--
	}
	return s[0:i]
}

// CommentText returns the text of comment,
// with the comment markers - //, /*, and */ - removed.
func CommentText(comment *ast.CommentGroup) string {
	if comment == nil {
		return ""
	}
	comments := make([]string, len(comment.List))
	for i, c := range comment.List {
		comments[i] = string(c.Text)
	}

	lines := make([]string, 0, 10) // most comments are less than 10 lines
	for _, c := range comments {
		// Remove comment markers.
		// The parser has given us exactly the comment text.
		switch c[1] {
		case '/':
			//-style comment
			c = c[2:]
			// Remove leading space after //, if there is one.
			// TODO(gri) This appears to be necessary in isolated
			//           cases (bignum.RatFromString) - why?
			if len(c) > 0 && c[0] == ' ' {
				c = c[1:]
			}
		case '*':
			/*-style comment */
			c = c[2 : len(c)-2]
		}

		// Split on newlines.
		cl := strings.Split(c, "\n", -1)

		// Walk lines, stripping trailing white space and adding to list.
		for _, l := range cl {
			lines = append(lines, stripTrailingWhitespace(l))
		}
	}

	// Remove leading blank lines; convert runs of
	// interior blank lines to a single blank line.
	n := 0
	for _, line := range lines {
		if line != "" || n > 0 && lines[n-1] != "" {
			lines[n] = line
			n++
		}
	}
	lines = lines[0:n]

	// Add final "" entry to get trailing newline from Join.
	if n > 0 && lines[n-1] != "" {
		lines = append(lines, "")
	}

	return strings.Join(lines, "\n")
}

// Split bytes into lines.
func split(text []byte) [][]byte {
	// count lines
	n := 0
	last := 0
	for i, c := range text {
		if c == '\n' {
			last = i + 1
			n++
		}
	}
	if last < len(text) {
		n++
	}

	// split
	out := make([][]byte, n)
	last = 0
	n = 0
	for i, c := range text {
		if c == '\n' {
			out[n] = text[last : i+1]
			last = i + 1
			n++
		}
	}
	if last < len(text) {
		out[n] = text[last:]
	}

	return out
}

var (
	ldquo = []byte("“")
	rdquo = []byte("”")
)

// Escape comment text for HTML. If nice is set,
// also turn `` into “ and '' into ”.
func commentEscape(w io.Writer, s []byte, nice bool) {
	last := 0
	if nice {
		for i := 0; i < len(s)-1; i++ {
			ch := s[i]
			if ch == s[i+1] && (ch == '`' || ch == '\'') {
				template.HTMLEscape(w, s[last:i])
				last = i + 2
				switch ch {
				case '`':
					w.Write(ldquo)
				case '\'':
					w.Write(rdquo)
				}
				i++ // loop will add one more
			}
		}
	}
	template.HTMLEscape(w, s[last:])
}

const (
	// Regexp for Go identifiers
	identRx = `[a-zA-Z_][a-zA-Z_0-9]*` // TODO(gri) ASCII only for now - fix this

	// Regexp for URLs
	protocol = `(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero):`
	hostPart = `[a-zA-Z0-9_@\-]+`
	filePart = `[a-zA-Z0-9_?%#~&/\-+=]+`
	urlRx    = protocol + `//` + // http://
		hostPart + `([.:]` + hostPart + `)*/?` + // //www.google.com:8080/
		filePart + `([:.,]` + filePart + `)*`
)

var matchRx = regexp.MustCompile(`(` + identRx + `)|(` + urlRx + `)`)

var (
	html_a      = []byte(`<a href="`)
	html_aq     = []byte(`">`)
	html_enda   = []byte("")
	html_i      = []byte("")
	html_endi   = []byte("")
	html_p      = []byte("
\n")
	html_endp   = []byte("
\n")
	html_pre    = []byte("
")
	html_endpre = []byte("
\n")
)

// Emphasize and escape a line of text for HTML. URLs are converted into links;
// if the URL also appears in the words map, the link is taken from the map (if
// the corresponding map value is the empty string, the URL is not converted
// into a link). Go identifiers that appear in the words map are italicized; if
// the corresponding map value is not the empty string, it is considered a URL
// and the word is converted into a link. If nice is set, the remaining text's
// appearance is improved where it makes sense (e.g., `` is turned into “
// and '' into ”).
func emphasize(w io.Writer, line []byte, words map[string]string, nice bool) {
	for {
		m := matchRx.FindSubmatchIndex(line)
		if m == nil {
			break
		}
		// m >= 6 (two parenthesized sub-regexps in matchRx, 1st one is identRx)

		// write text before match
		commentEscape(w, line[0:m[0]], nice)

		// analyze match
		match := line[m[0]:m[1]]
		url := ""
		italics := false
		if words != nil {
			url, italics = words[string(match)]
		}
		if m[2] < 0 {
			// didn't match against first parenthesized sub-regexp; must be match against urlRx
			if !italics {
				// no alternative URL in words list, use match instead
				url = string(match)
			}
			italics = false // don't italicize URLs
		}

		// write match
		if len(url) > 0 {
			w.Write(html_a)
			template.HTMLEscape(w, []byte(url))
			w.Write(html_aq)
		}
		if italics {
			w.Write(html_i)
		}
		commentEscape(w, match, nice)
		if italics {
			w.Write(html_endi)
		}
		if len(url) > 0 {
			w.Write(html_enda)
		}

		// advance
		line = line[m[1]:]
	}
	commentEscape(w, line, nice)
}

func indentLen(s []byte) int {
	i := 0
	for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
		i++
	}
	return i
}

func isBlank(s []byte) bool { return len(s) == 0 || (len(s) == 1 && s[0] == '\n') }

func commonPrefix(a, b []byte) []byte {
	i := 0
	for i < len(a) && i < len(b) && a[i] == b[i] {
		i++
	}
	return a[0:i]
}

func unindent(block [][]byte) {
	if len(block) == 0 {
		return
	}

	// compute maximum common white prefix
	prefix := block[0][0:indentLen(block[0])]
	for _, line := range block {
		if !isBlank(line) {
			prefix = commonPrefix(prefix, line[0:indentLen(line)])
		}
	}
	n := len(prefix)

	// remove
	for i, line := range block {
		if !isBlank(line) {
			block[i] = line[n:]
		}
	}
}

// Convert comment text to formatted HTML.
// The comment was prepared by DocReader,
// so it is known not to have leading, trailing blank lines
// nor to have trailing spaces at the end of lines.
// The comment markers have already been removed.
//
// Turn each run of multiple \n into

// Turn each run of indented lines into a
 block without indent.
//
// URLs in the comment text are converted into links; if the URL also appears
// in the words map, the link is taken from the map (if the corresponding map
// value is the empty string, the URL is not converted into a link).
//
// Go identifiers that appear in the words map are italicized; if the corresponding
// map value is not the empty string, it is considered a URL and the word is converted
// into a link.
func ToHTML(w io.Writer, s []byte, words map[string]string) {
	inpara := false

	close := func() {
		if inpara {
			w.Write(html_endp)
			inpara = false
		}
	}
	open := func() {
		if !inpara {
			w.Write(html_p)
			inpara = true
		}
	}

	lines := split(s)
	unindent(lines)
	for i := 0; i < len(lines); {
		line := lines[i]
		if isBlank(line) {
			// close paragraph
			close()
			i++
			continue
		}
		if indentLen(line) > 0 {
			// close paragraph
			close()

			// count indented or blank lines
			j := i + 1
			for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
				j++
			}
			// but not trailing blank lines
			for j > i && isBlank(lines[j-1]) {
				j--
			}
			block := lines[i:j]
			i = j

			unindent(block)

			// put those lines in a pre block
			w.Write(html_pre)
			for _, line := range block {
				emphasize(w, line, nil, false) // no nice text formatting
			}
			w.Write(html_endpre)
			continue
		}
		// open paragraph
		open()
		emphasize(w, lines[i], words, true) // nice text formatting
		i++
	}
	close()
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

/*
	The runtime package contains operations that interact with Go's runtime system,
	such as functions to control goroutines. It also includes the low-level type information
	used by the reflect package; see reflect's documentation for the programmable
	interface to the run-time type system.
*/
package runtime

// Gosched yields the processor, allowing other goroutines to run.  It does not
// suspend the current goroutine, so execution resumes automatically.
func Gosched()

// Goexit terminates the goroutine that calls it.  No other goroutine is affected.
// Goexit runs all deferred calls before terminating the goroutine.
func Goexit()

// Caller reports file and line number information about function invocations on
// the calling goroutine's stack.  The argument skip is the number of stack frames to
// ascend, with 0 identifying the the caller of Caller.  The return values report the
// program counter, file name, and line number within the file of the corresponding
// call.  The boolean ok is false if it was not possible to recover the information.
func Caller(skip int) (pc uintptr, file string, line int, ok bool)

// Callers fills the slice pc with the program counters of function invocations
// on the calling goroutine's stack.  The argument skip is the number of stack frames
// to skip before recording in pc, with 0 starting at the caller of Caller.
// It returns the number of entries written to pc.
func Callers(skip int, pc []uintptr) int

// FuncForPC returns a *Func describing the function that contains the
// given program counter address, or else nil.
func FuncForPC(pc uintptr) *Func

// NOTE(rsc): Func must match struct Func in runtime.h

// Func records information about a function in the program,
// in particular  the mapping from program counters to source
// line numbers within that function.
type Func struct {
	name   string
	typ    string
	src    string
	pcln   []byte
	entry  uintptr
	pc0    uintptr
	ln0    int32
	frame  int32
	args   int32
	locals int32
}

// Name returns the name of the function.
func (f *Func) Name() string { return f.name }

// Entry returns the entry address of the function.
func (f *Func) Entry() uintptr { return f.entry }

// FileLine returns the file name and line number of the
// source code corresponding to the program counter pc.
// The result will not be accurate if pc is not a program
// counter within f.
func (f *Func) FileLine(pc uintptr) (file string, line int) {
	// NOTE(rsc): If you edit this function, also edit
	// symtab.c:/^funcline.
	var pcQuant uintptr = 1
	if GOARCH == "arm" {
		pcQuant = 4
	}

	targetpc := pc
	p := f.pcln
	pc = f.pc0
	line = int(f.ln0)
	file = f.src
	for i := 0; i < len(p) && pc <= targetpc; i++ {
		switch {
		case p[i] == 0:
			line += int(p[i+1]<<24) | int(p[i+2]<<16) | int(p[i+3]<<8) | int(p[i+4])
			i += 4
		case p[i] <= 64:
			line += int(p[i])
		case p[i] <= 128:
			line -= int(p[i] - 64)
		default:
			pc += pcQuant * uintptr(p[i]-129)
		}
		pc += pcQuant
	}
	return
}

// mid returns the current os thread (m) id.
func mid() uint32

// Semacquire waits until *s > 0 and then atomically decrements it.
// It is intended as a simple sleep primitive for use by the synchronization
// library and should not be used directly.
func Semacquire(s *uint32)

// Semrelease atomically increments *s and notifies a waiting goroutine
// if one is blocked in Semacquire.
// It is intended as a simple wakeup primitive for use by the synchronization
// library and should not be used directly.
func Semrelease(s *uint32)

// SetFinalizer sets the finalizer associated with x to f.
// When the garbage collector finds an unreachable block
// with an associated finalizer, it clears the association and runs
// f(x) in a separate goroutine.  This makes x reachable again, but
// now without an associated finalizer.  Assuming that SetFinalizer
// is not called again, the next time the garbage collector sees
// that x is unreachable, it will free x.
//
// SetFinalizer(x, nil) clears any finalizer associated with x.
//
// The argument x must be a pointer to an object allocated by
// calling new or by taking the address of a composite literal.
// The argument f must be a function that takes a single argument
// of x's type and returns no arguments.  If either of these is not
// true, SetFinalizer aborts the program.
//
// Finalizers are run in dependency order: if A points at B, both have
// finalizers, and they are otherwise unreachable, only the finalizer
// for A runs; once A is freed, the finalizer for B can run.
// If a cyclic structure includes a block with a finalizer, that
// cycle is not guaranteed to be garbage collected and the finalizer
// is not guaranteed to run, because there is no ordering that
// respects the dependencies.
//
// The finalizer for x is scheduled to run at some arbitrary time after
// x becomes unreachable.
// There is no guarantee that finalizers will run before a program exits,
// so typically they are useful only for releasing non-memory resources
// associated with an object during a long-running program.
// For example, an os.File object could use a finalizer to close the
// associated operating system file descriptor when a program discards
// an os.File without calling Close, but it would be a mistake
// to depend on a finalizer to flush an in-memory I/O buffer such as a
// bufio.Writer, because the buffer would not be flushed at program exit.
//
// A single goroutine runs all finalizers for a program, sequentially.
// If a finalizer must run for a long time, it should do so by starting
// a new goroutine.
//
// TODO(rsc): make os.File use SetFinalizer
// TODO(rsc): allow f to have (ignored) return values
//
func SetFinalizer(x, f interface{})

func getgoroot() string

// GOROOT returns the root of the Go tree.
// It uses the GOROOT environment variable, if set,
// or else the root used during the Go build.
func GOROOT() string {
	s := getgoroot()
	if s != "" {
		return s
	}
	return defaultGoroot
}

// Version returns the Go tree's version string.
// It is either a sequence number or, when possible,
// a release tag like "release.2010-03-04".
// A trailing + indicates that the tree had local modifications
// at the time of the build.
func Version() string {
	return theVersion
}

// GOOS is the Go tree's operating system target:
// one of darwin, freebsd, linux, and so on.
const GOOS string = theGoos

// GOARCH is the Go tree's architecture target:
// 386, amd64, or arm.
const GOARCH string = theGoarch
<commit_msg>runtime: remove done TODO from SetFinalizer<commit_after>// Copyright 2009 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.

/*
	The runtime package contains operations that interact with Go's runtime system,
	such as functions to control goroutines. It also includes the low-level type information
	used by the reflect package; see reflect's documentation for the programmable
	interface to the run-time type system.
*/
package runtime

// Gosched yields the processor, allowing other goroutines to run.  It does not
// suspend the current goroutine, so execution resumes automatically.
func Gosched()

// Goexit terminates the goroutine that calls it.  No other goroutine is affected.
// Goexit runs all deferred calls before terminating the goroutine.
func Goexit()

// Caller reports file and line number information about function invocations on
// the calling goroutine's stack.  The argument skip is the number of stack frames to
// ascend, with 0 identifying the the caller of Caller.  The return values report the
// program counter, file name, and line number within the file of the corresponding
// call.  The boolean ok is false if it was not possible to recover the information.
func Caller(skip int) (pc uintptr, file string, line int, ok bool)

// Callers fills the slice pc with the program counters of function invocations
// on the calling goroutine's stack.  The argument skip is the number of stack frames
// to skip before recording in pc, with 0 starting at the caller of Caller.
// It returns the number of entries written to pc.
func Callers(skip int, pc []uintptr) int

// FuncForPC returns a *Func describing the function that contains the
// given program counter address, or else nil.
func FuncForPC(pc uintptr) *Func

// NOTE(rsc): Func must match struct Func in runtime.h

// Func records information about a function in the program,
// in particular  the mapping from program counters to source
// line numbers within that function.
type Func struct {
	name   string
	typ    string
	src    string
	pcln   []byte
	entry  uintptr
	pc0    uintptr
	ln0    int32
	frame  int32
	args   int32
	locals int32
}

// Name returns the name of the function.
func (f *Func) Name() string { return f.name }

// Entry returns the entry address of the function.
func (f *Func) Entry() uintptr { return f.entry }

// FileLine returns the file name and line number of the
// source code corresponding to the program counter pc.
// The result will not be accurate if pc is not a program
// counter within f.
func (f *Func) FileLine(pc uintptr) (file string, line int) {
	// NOTE(rsc): If you edit this function, also edit
	// symtab.c:/^funcline.
	var pcQuant uintptr = 1
	if GOARCH == "arm" {
		pcQuant = 4
	}

	targetpc := pc
	p := f.pcln
	pc = f.pc0
	line = int(f.ln0)
	file = f.src
	for i := 0; i < len(p) && pc <= targetpc; i++ {
		switch {
		case p[i] == 0:
			line += int(p[i+1]<<24) | int(p[i+2]<<16) | int(p[i+3]<<8) | int(p[i+4])
			i += 4
		case p[i] <= 64:
			line += int(p[i])
		case p[i] <= 128:
			line -= int(p[i] - 64)
		default:
			pc += pcQuant * uintptr(p[i]-129)
		}
		pc += pcQuant
	}
	return
}

// mid returns the current os thread (m) id.
func mid() uint32

// Semacquire waits until *s > 0 and then atomically decrements it.
// It is intended as a simple sleep primitive for use by the synchronization
// library and should not be used directly.
func Semacquire(s *uint32)

// Semrelease atomically increments *s and notifies a waiting goroutine
// if one is blocked in Semacquire.
// It is intended as a simple wakeup primitive for use by the synchronization
// library and should not be used directly.
func Semrelease(s *uint32)

// SetFinalizer sets the finalizer associated with x to f.
// When the garbage collector finds an unreachable block
// with an associated finalizer, it clears the association and runs
// f(x) in a separate goroutine.  This makes x reachable again, but
// now without an associated finalizer.  Assuming that SetFinalizer
// is not called again, the next time the garbage collector sees
// that x is unreachable, it will free x.
//
// SetFinalizer(x, nil) clears any finalizer associated with x.
//
// The argument x must be a pointer to an object allocated by
// calling new or by taking the address of a composite literal.
// The argument f must be a function that takes a single argument
// of x's type and returns no arguments.  If either of these is not
// true, SetFinalizer aborts the program.
//
// Finalizers are run in dependency order: if A points at B, both have
// finalizers, and they are otherwise unreachable, only the finalizer
// for A runs; once A is freed, the finalizer for B can run.
// If a cyclic structure includes a block with a finalizer, that
// cycle is not guaranteed to be garbage collected and the finalizer
// is not guaranteed to run, because there is no ordering that
// respects the dependencies.
//
// The finalizer for x is scheduled to run at some arbitrary time after
// x becomes unreachable.
// There is no guarantee that finalizers will run before a program exits,
// so typically they are useful only for releasing non-memory resources
// associated with an object during a long-running program.
// For example, an os.File object could use a finalizer to close the
// associated operating system file descriptor when a program discards
// an os.File without calling Close, but it would be a mistake
// to depend on a finalizer to flush an in-memory I/O buffer such as a
// bufio.Writer, because the buffer would not be flushed at program exit.
//
// A single goroutine runs all finalizers for a program, sequentially.
// If a finalizer must run for a long time, it should do so by starting
// a new goroutine.
//
// TODO(rsc): allow f to have (ignored) return values
//
func SetFinalizer(x, f interface{})

func getgoroot() string

// GOROOT returns the root of the Go tree.
// It uses the GOROOT environment variable, if set,
// or else the root used during the Go build.
func GOROOT() string {
	s := getgoroot()
	if s != "" {
		return s
	}
	return defaultGoroot
}

// Version returns the Go tree's version string.
// It is either a sequence number or, when possible,
// a release tag like "release.2010-03-04".
// A trailing + indicates that the tree had local modifications
// at the time of the build.
func Version() string {
	return theVersion
}

// GOOS is the Go tree's operating system target:
// one of darwin, freebsd, linux, and so on.
const GOOS string = theGoos

// GOARCH is the Go tree's architecture target:
// 386, amd64, or arm.
const GOARCH string = theGoarch
<|endoftext|>


<commit_before>// Copyright 2009 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.

package sync_test

import (
	. "sync"
	"testing"
)

type one int

func (o *one) Increment() {
	*o++
}

func run(once *Once, o *one, c chan bool) {
	once.Do(func() { o.Increment() })
	c <- true
}

func TestOnce(t *testing.T) {
	o := new(one)
	once := new(Once)
	c := make(chan bool)
	const N = 10
	for i := 0; i < N; i++ {
		go run(once, o, c)
	}
	for i := 0; i < N; i++ {
		<-c
	}
	if *o != 1 {
		t.Errorf("once failed: %d is not 1", *o)
	}
}
<commit_msg>sync: add benchmark for Once.<commit_after>// Copyright 2009 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.

package sync_test

import (
	. "sync"
	"sync/atomic"
	"runtime"
	"testing"
)

type one int

func (o *one) Increment() {
	*o++
}

func run(once *Once, o *one, c chan bool) {
	once.Do(func() { o.Increment() })
	c <- true
}

func TestOnce(t *testing.T) {
	o := new(one)
	once := new(Once)
	c := make(chan bool)
	const N = 10
	for i := 0; i < N; i++ {
		go run(once, o, c)
	}
	for i := 0; i < N; i++ {
		<-c
	}
	if *o != 1 {
		t.Errorf("once failed: %d is not 1", *o)
	}
}

func BenchmarkOnce(b *testing.B) {
	const CallsPerSched = 1000
	procs := runtime.GOMAXPROCS(-1)
	N := int32(b.N / CallsPerSched)
	var once Once
	f := func() {}
	c := make(chan bool, procs)
	for p := 0; p < procs; p++ {
		go func() {
			for atomic.AddInt32(&N, -1) >= 0 {
				runtime.Gosched()
				for g := 0; g < CallsPerSched; g++ {
					once.Do(f)
				}
			}
			c <- true
		}()
	}
	for p := 0; p < procs; p++ {
		<-c
	}
}
<|endoftext|>


<commit_before>package service

import (
	"context"

	"github.com/giantswarm/microerror"
	"k8s.io/api/core/v1"
	apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"

	"github.com/giantswarm/aws-operator/service/awsconfig/v10/key"
)

func (r *Resource) GetDesiredState(ctx context.Context, obj interface{}) (interface{}, error) {
	customObject, err := key.ToCustomObject(obj)
	if err != nil {
		return nil, microerror.Mask(err)
	}

	service := &v1.Service{
		ObjectMeta: apismetav1.ObjectMeta{
			Name:      "master",
			Namespace: key.ClusterID(customObject),
			Labels: map[string]string{
				"app":      "master",
				"cluster":  key.ClusterID(customObject),
				"customer": key.CustomerID(customObject),
			},
			Annotations: map[string]string{
				"giantswarm.io/prometheus-cluster": key.ClusterID(customObject),
			},
		},
		Spec: v1.ServiceSpec{
			Ports: []v1.ServicePort{
				{
					Protocol:   v1.ProtocolTCP,
					Port:       httpsPort,
					TargetPort: intstr.FromInt(httpsPort),
				},
			},
		},
	}

	return service, nil
}
<commit_msg>Adds new style labels to service (#892)<commit_after>package service

import (
	"context"

	"github.com/giantswarm/microerror"
	"k8s.io/api/core/v1"
	apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"

	"github.com/giantswarm/aws-operator/service/awsconfig/v10/key"
)

func (r *Resource) GetDesiredState(ctx context.Context, obj interface{}) (interface{}, error) {
	customObject, err := key.ToCustomObject(obj)
	if err != nil {
		return nil, microerror.Mask(err)
	}

	service := &v1.Service{
		ObjectMeta: apismetav1.ObjectMeta{
			Name:      "master",
			Namespace: key.ClusterID(customObject),
			Labels: map[string]string{
				"app":                        "master",
				"cluster":                    key.ClusterID(customObject),
				"customer":                   key.CustomerID(customObject),
				"giantswarm.io/cluster":      key.ClusterID(customObject),
				"giantswarm.io/organization": key.CustomerID(customObject),
			},
			Annotations: map[string]string{
				"giantswarm.io/prometheus-cluster": key.ClusterID(customObject),
			},
		},
		Spec: v1.ServiceSpec{
			Ports: []v1.ServicePort{
				{
					Protocol:   v1.ProtocolTCP,
					Port:       httpsPort,
					TargetPort: intstr.FromInt(httpsPort),
				},
			},
		},
	}

	return service, nil
}
<|endoftext|>


<commit_before>package terraform

import (
	"reflect"
	"testing"
)

func TestVariables(t *testing.T) {
	cases := map[string]struct {
		Module   string
		Env      map[string]string
		Override map[string]interface{}
		Error    bool
		Expected map[string]interface{}
	}{
		"config only": {
			"vars-basic",
			nil,
			nil,
			false,
			map[string]interface{}{
				"a": "foo",
				"b": []interface{}{},
				"c": map[string]interface{}{},
			},
		},

		"env vars": {
			"vars-basic",
			map[string]string{
				"TF_VAR_a": "bar",
				"TF_VAR_b": `["foo", "bar"]`,
				"TF_VAR_c": `{"foo" = "bar"}`,
			},
			nil,
			false,
			map[string]interface{}{
				"a": "bar",
				"b": []interface{}{"foo", "bar"},
				"c": map[string]interface{}{
					"foo": "bar",
				},
			},
		},

		"override": {
			"vars-basic",
			nil,
			map[string]interface{}{
				"a": "bar",
				"b": []interface{}{"foo", "bar"},
				"c": map[string]interface{}{
					"foo": "bar",
				},
			},
			false,
			map[string]interface{}{
				"a": "bar",
				"b": []interface{}{"foo", "bar"},
				"c": map[string]interface{}{
					"foo": "bar",
				},
			},
		},

		"override partial map": {
			"vars-basic",
			map[string]string{
				"TF_VAR_c": `{"foo" = "a", "bar" = "baz"}`,
			},
			map[string]interface{}{
				"c": map[string]interface{}{
					"foo": "bar",
				},
			},
			false,
			map[string]interface{}{
				"a": "foo",
				"b": []interface{}{},
				"c": map[string]interface{}{
					"foo": "bar",
					"bar": "baz",
				},
			},
		},

		"bools: config only": {
			"vars-basic-bool",
			nil,
			nil,
			false,
			map[string]interface{}{
				"a": "1",
				"b": "0",
			},
		},

		"bools: override with string": {
			"vars-basic-bool",
			nil,
			map[string]interface{}{
				"a": "foo",
				"b": "bar",
			},
			false,
			map[string]interface{}{
				"a": "foo",
				"b": "bar",
			},
		},

		"bools: override with env": {
			"vars-basic-bool",
			map[string]string{
				"TF_VAR_a": "false",
				"TF_VAR_b": "true",
			},
			nil,
			false,
			map[string]interface{}{
				"a": "false",
				"b": "true",
			},
		},

		"bools: override with bool": {
			"vars-basic-bool",
			nil,
			map[string]interface{}{
				"a": false,
				"b": true,
			},
			false,
			map[string]interface{}{
				"a": "0",
				"b": "1",
			},
		},
	}

	for name, tc := range cases {
		// Wrapped in a func so we can get defers to work
		t.Run(name, func(t *testing.T) {
			// Set the env vars
			for k, v := range tc.Env {
				defer tempEnv(t, k, v)()
			}

			m := testModule(t, tc.Module)
			actual, err := Variables(m, tc.Override)
			if (err != nil) != tc.Error {
				t.Fatalf("%s: err: %s", name, err)
			}
			if err != nil {
				return
			}

			if !reflect.DeepEqual(actual, tc.Expected) {
				t.Fatalf("%s\n\nexpected: %#v\n\ngot: %#v", name, tc.Expected, actual)
			}
		})
	}
}
<commit_msg>Add failing test for GH-10155<commit_after>package terraform

import (
	"reflect"
	"testing"
)

func TestVariables(t *testing.T) {
	cases := map[string]struct {
		Module   string
		Env      map[string]string
		Override map[string]interface{}
		Error    bool
		Expected map[string]interface{}
	}{
		"config only": {
			"vars-basic",
			nil,
			nil,
			false,
			map[string]interface{}{
				"a": "foo",
				"b": []interface{}{},
				"c": map[string]interface{}{},
			},
		},

		"env vars": {
			"vars-basic",
			map[string]string{
				"TF_VAR_a": "bar",
				"TF_VAR_b": `["foo", "bar"]`,
				"TF_VAR_c": `{"foo" = "bar"}`,
			},
			nil,
			false,
			map[string]interface{}{
				"a": "bar",
				"b": []interface{}{"foo", "bar"},
				"c": map[string]interface{}{
					"foo": "bar",
				},
			},
		},

		"override": {
			"vars-basic",
			nil,
			map[string]interface{}{
				"a": "bar",
				"b": []interface{}{"foo", "bar"},
				"c": map[string]interface{}{
					"foo": "bar",
				},
			},
			false,
			map[string]interface{}{
				"a": "bar",
				"b": []interface{}{"foo", "bar"},
				"c": map[string]interface{}{
					"foo": "bar",
				},
			},
		},

		"override partial map": {
			"vars-basic",
			map[string]string{
				"TF_VAR_c": `{"foo" = "a", "bar" = "baz"}`,
			},
			map[string]interface{}{
				"c": map[string]interface{}{
					"foo": "bar",
				},
			},
			false,
			map[string]interface{}{
				"a": "foo",
				"b": []interface{}{},
				"c": map[string]interface{}{
					"foo": "bar",
					"bar": "baz",
				},
			},
		},

		"bools: config only": {
			"vars-basic-bool",
			nil,
			nil,
			false,
			map[string]interface{}{
				"a": "1",
				"b": "0",
			},
		},

		"bools: override with string": {
			"vars-basic-bool",
			nil,
			map[string]interface{}{
				"a": "foo",
				"b": "bar",
			},
			false,
			map[string]interface{}{
				"a": "foo",
				"b": "bar",
			},
		},

		"bools: override with env": {
			"vars-basic-bool",
			map[string]string{
				"TF_VAR_a": "false",
				"TF_VAR_b": "true",
			},
			nil,
			false,
			map[string]interface{}{
				"a": "false",
				"b": "true",
			},
		},

		"bools: override with bool": {
			"vars-basic-bool",
			nil,
			map[string]interface{}{
				"a": false,
				"b": true,
			},
			false,
			map[string]interface{}{
				"a": "0",
				"b": "1",
			},
		},

		"override map with string": {
			"vars-basic",
			map[string]string{
				"TF_VAR_c": `{"foo" = "a", "bar" = "baz"}`,
			},
			map[string]interface{}{
				"c": "bar",
			},
			true,
			nil,
		},
	}

	for name, tc := range cases {
		// Wrapped in a func so we can get defers to work
		t.Run(name, func(t *testing.T) {
			// Set the env vars
			for k, v := range tc.Env {
				defer tempEnv(t, k, v)()
			}

			m := testModule(t, tc.Module)
			actual, err := Variables(m, tc.Override)
			if (err != nil) != tc.Error {
				t.Fatalf("%s: err: %s", name, err)
			}
			if err != nil {
				return
			}

			if !reflect.DeepEqual(actual, tc.Expected) {
				t.Fatalf("%s\n\nexpected: %#v\n\ngot: %#v", name, tc.Expected, actual)
			}
		})
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package trace

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"testing"

	pb "cloud.google.com/go/trace/testdata/helloworld"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

func TestGRPCInterceptors(t *testing.T) {
	tc := newTestClient(&noopTransport{})

	// default sampling with global=1.
	parent := tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=1")
	testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if got, want := in.TraceID(), out.TraceID(); got != want {
			t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want)
		}
		if !in.Traced() {
			t.Errorf("incoming span is not traced; want traced")
		}
	})

	// default sampling with global=0.
	parent = tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=0")
	testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if got, want := in.TraceID(), out.TraceID(); got != want {
			t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want)
		}
		if in.Traced() {
			t.Errorf("incoming span is traced; want not traced")
		}
	})

	// sampling all with global=1.
	all, _ := NewLimitedSampler(1.0, 1<<32)
	tc.SetSamplingPolicy(all)
	parent = tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=1")
	testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if got, want := in.TraceID(), out.TraceID(); got != want {
			t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want)
		}
		if !in.Traced() {
			t.Errorf("incoming span is not traced; want traced")
		}
	})

	// sampling none with global=1.
	none, _ := NewLimitedSampler(0, 0)
	tc.SetSamplingPolicy(none)
	parent = tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=1")
	testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if got, want := in.TraceID(), out.TraceID(); got != want {
			t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want)
		}
		if in.Traced() {
			t.Errorf("incoming span is traced; want not traced")
		}
	})

	// sampling all with no parent span.
	tc.SetSamplingPolicy(all)
	testGRPCInterceptor(t, tc, nil, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if in.TraceID() == "" {
			t.Errorf("incoming call TraceID is empty")
		}
		if !in.Traced() {
			t.Errorf("incoming span is not traced; want traced")
		}
	})

	// sampling none with no parent span.
	tc.SetSamplingPolicy(none)
	testGRPCInterceptor(t, tc, nil, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if in.TraceID() == "" {
			t.Errorf("incoming call TraceID is empty")
		}
		if in.Traced() {
			t.Errorf("incoming span is traced; want not traced")
		}
	})
}

func testGRPCInterceptor(t *testing.T, tc *Client, parent *Span, assert func(t *testing.T, out, in *Span)) {
	incomingCh := make(chan *Span, 1)
	addrCh := make(chan net.Addr, 1)
	go func() {
		lis, err := net.Listen("tcp", "")
		if err != nil {
			t.Fatalf("Failed to listen: %v", err)
		}
		addrCh <- lis.Addr()

		s := grpc.NewServer(grpc.UnaryInterceptor(tc.GRPCServerInterceptor()))
		pb.RegisterGreeterServer(s, &grpcServer{
			fn: func(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
				incomingCh <- FromContext(ctx)
				return &pb.HelloReply{}, nil
			},
		})
		if err := s.Serve(lis); err != nil {
			t.Fatalf("Failed to serve: %v", err)
		}
	}()

	addr := <-addrCh
	conn, err := grpc.Dial(addr.String(), grpc.WithInsecure(), grpc.WithBlock(), grpc.WithUnaryInterceptor(tc.GRPCClientInterceptor()))
	if err != nil {
		t.Fatalf("Did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	outgoingCtx := NewContext(context.Background(), parent)
	_, err = c.SayHello(outgoingCtx, &pb.HelloRequest{})
	if err != nil {
		log.Fatalf("Could not SayHello: %v", err)
	}

	assert(t, parent, <-incomingCh)
}

type noopTransport struct{}

func (rt *noopTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	resp := &http.Response{
		Status:     "200 OK",
		StatusCode: 200,
		Body:       ioutil.NopCloser(strings.NewReader("{}")),
	}
	return resp, nil
}

type grpcServer struct {
	fn func(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error)
}

func (s *grpcServer) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return s.fn(ctx, in)
}
<commit_msg>trace: skip gRPC interceptor test<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package trace

import (
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"testing"

	pb "cloud.google.com/go/trace/testdata/helloworld"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

func TestGRPCInterceptors(t *testing.T) {
	t.Skip("hangs forever for go < 1.9")

	tc := newTestClient(&noopTransport{})

	// default sampling with global=1.
	parent := tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=1")
	testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if got, want := in.TraceID(), out.TraceID(); got != want {
			t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want)
		}
		if !in.Traced() {
			t.Errorf("incoming span is not traced; want traced")
		}
	})

	// default sampling with global=0.
	parent = tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=0")
	testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if got, want := in.TraceID(), out.TraceID(); got != want {
			t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want)
		}
		if in.Traced() {
			t.Errorf("incoming span is traced; want not traced")
		}
	})

	// sampling all with global=1.
	all, _ := NewLimitedSampler(1.0, 1<<32)
	tc.SetSamplingPolicy(all)
	parent = tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=1")
	testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if got, want := in.TraceID(), out.TraceID(); got != want {
			t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want)
		}
		if !in.Traced() {
			t.Errorf("incoming span is not traced; want traced")
		}
	})

	// sampling none with global=1.
	none, _ := NewLimitedSampler(0, 0)
	tc.SetSamplingPolicy(none)
	parent = tc.SpanFromHeader("parent", "7f27601f17b7a2873739efd18ff83872/123;o=1")
	testGRPCInterceptor(t, tc, parent, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if got, want := in.TraceID(), out.TraceID(); got != want {
			t.Errorf("incoming call is not tracing the outgoing trace; TraceID = %q; want %q", got, want)
		}
		if in.Traced() {
			t.Errorf("incoming span is traced; want not traced")
		}
	})

	// sampling all with no parent span.
	tc.SetSamplingPolicy(all)
	testGRPCInterceptor(t, tc, nil, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if in.TraceID() == "" {
			t.Errorf("incoming call TraceID is empty")
		}
		if !in.Traced() {
			t.Errorf("incoming span is not traced; want traced")
		}
	})

	// sampling none with no parent span.
	tc.SetSamplingPolicy(none)
	testGRPCInterceptor(t, tc, nil, func(t *testing.T, out, in *Span) {
		if in == nil {
			t.Fatalf("missing span in the incoming context")
		}
		if in.TraceID() == "" {
			t.Errorf("incoming call TraceID is empty")
		}
		if in.Traced() {
			t.Errorf("incoming span is traced; want not traced")
		}
	})
}

func testGRPCInterceptor(t *testing.T, tc *Client, parent *Span, assert func(t *testing.T, out, in *Span)) {
	incomingCh := make(chan *Span, 1)
	addrCh := make(chan net.Addr, 1)
	go func() {
		lis, err := net.Listen("tcp", "")
		if err != nil {
			t.Fatalf("Failed to listen: %v", err)
		}
		addrCh <- lis.Addr()

		s := grpc.NewServer(grpc.UnaryInterceptor(tc.GRPCServerInterceptor()))
		pb.RegisterGreeterServer(s, &grpcServer{
			fn: func(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
				incomingCh <- FromContext(ctx)
				return &pb.HelloReply{}, nil
			},
		})
		if err := s.Serve(lis); err != nil {
			t.Fatalf("Failed to serve: %v", err)
		}
	}()

	addr := <-addrCh
	conn, err := grpc.Dial(addr.String(), grpc.WithInsecure(), grpc.WithBlock(), grpc.WithUnaryInterceptor(tc.GRPCClientInterceptor()))
	if err != nil {
		t.Fatalf("Did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	outgoingCtx := NewContext(context.Background(), parent)
	_, err = c.SayHello(outgoingCtx, &pb.HelloRequest{})
	if err != nil {
		log.Fatalf("Could not SayHello: %v", err)
	}

	assert(t, parent, <-incomingCh)
}

type noopTransport struct{}

func (rt *noopTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	resp := &http.Response{
		Status:     "200 OK",
		StatusCode: 200,
		Body:       ioutil.NopCloser(strings.NewReader("{}")),
	}
	return resp, nil
}

type grpcServer struct {
	fn func(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error)
}

func (s *grpcServer) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return s.fn(ctx, in)
}
<|endoftext|>