mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-14 23:17:15 +00:00
961c878e0d
Switch to using Go modules. This migrates our vendor.json to use Go 1.11's modules system, and replaces the vendor folder with the output of go mod vendor. The vendored code should remain basically the same; I believe some tree shaking of packages and support scripts/licenses/READMEs/etc. happened. This also fixes Travis and our Makefile to no longer use govendor.
291 lines
7.6 KiB
Go
291 lines
7.6 KiB
Go
// Copyright 2012 Google Inc. All rights reserved.
|
|
// Use of this source code is governed by the Apache 2.0
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build appengine
|
|
|
|
package socket
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"golang.org/x/net/context"
|
|
"google.golang.org/appengine/internal"
|
|
|
|
pb "google.golang.org/appengine/internal/socket"
|
|
)
|
|
|
|
// Dial connects to the address addr on the network protocol.
|
|
// The address format is host:port, where host may be a hostname or an IP address.
|
|
// Known protocols are "tcp" and "udp".
|
|
// The returned connection satisfies net.Conn, and is valid while ctx is valid;
|
|
// if the connection is to be used after ctx becomes invalid, invoke SetContext
|
|
// with the new context.
|
|
func Dial(ctx context.Context, protocol, addr string) (*Conn, error) {
|
|
return DialTimeout(ctx, protocol, addr, 0)
|
|
}
|
|
|
|
var ipFamilies = []pb.CreateSocketRequest_SocketFamily{
|
|
pb.CreateSocketRequest_IPv4,
|
|
pb.CreateSocketRequest_IPv6,
|
|
}
|
|
|
|
// DialTimeout is like Dial but takes a timeout.
|
|
// The timeout includes name resolution, if required.
|
|
func DialTimeout(ctx context.Context, protocol, addr string, timeout time.Duration) (*Conn, error) {
|
|
dialCtx := ctx // Used for dialing and name resolution, but not stored in the *Conn.
|
|
if timeout > 0 {
|
|
var cancel context.CancelFunc
|
|
dialCtx, cancel = context.WithTimeout(ctx, timeout)
|
|
defer cancel()
|
|
}
|
|
|
|
host, portStr, err := net.SplitHostPort(addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
port, err := strconv.Atoi(portStr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("socket: bad port %q: %v", portStr, err)
|
|
}
|
|
|
|
var prot pb.CreateSocketRequest_SocketProtocol
|
|
switch protocol {
|
|
case "tcp":
|
|
prot = pb.CreateSocketRequest_TCP
|
|
case "udp":
|
|
prot = pb.CreateSocketRequest_UDP
|
|
default:
|
|
return nil, fmt.Errorf("socket: unknown protocol %q", protocol)
|
|
}
|
|
|
|
packedAddrs, resolved, err := resolve(dialCtx, ipFamilies, host)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("socket: failed resolving %q: %v", host, err)
|
|
}
|
|
if len(packedAddrs) == 0 {
|
|
return nil, fmt.Errorf("no addresses for %q", host)
|
|
}
|
|
|
|
packedAddr := packedAddrs[0] // use first address
|
|
fam := pb.CreateSocketRequest_IPv4
|
|
if len(packedAddr) == net.IPv6len {
|
|
fam = pb.CreateSocketRequest_IPv6
|
|
}
|
|
|
|
req := &pb.CreateSocketRequest{
|
|
Family: fam.Enum(),
|
|
Protocol: prot.Enum(),
|
|
RemoteIp: &pb.AddressPort{
|
|
Port: proto.Int32(int32(port)),
|
|
PackedAddress: packedAddr,
|
|
},
|
|
}
|
|
if resolved {
|
|
req.RemoteIp.HostnameHint = &host
|
|
}
|
|
res := &pb.CreateSocketReply{}
|
|
if err := internal.Call(dialCtx, "remote_socket", "CreateSocket", req, res); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Conn{
|
|
ctx: ctx,
|
|
desc: res.GetSocketDescriptor(),
|
|
prot: prot,
|
|
local: res.ProxyExternalIp,
|
|
remote: req.RemoteIp,
|
|
}, nil
|
|
}
|
|
|
|
// LookupIP returns the given host's IP addresses.
|
|
func LookupIP(ctx context.Context, host string) (addrs []net.IP, err error) {
|
|
packedAddrs, _, err := resolve(ctx, ipFamilies, host)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("socket: failed resolving %q: %v", host, err)
|
|
}
|
|
addrs = make([]net.IP, len(packedAddrs))
|
|
for i, pa := range packedAddrs {
|
|
addrs[i] = net.IP(pa)
|
|
}
|
|
return addrs, nil
|
|
}
|
|
|
|
func resolve(ctx context.Context, fams []pb.CreateSocketRequest_SocketFamily, host string) ([][]byte, bool, error) {
|
|
// Check if it's an IP address.
|
|
if ip := net.ParseIP(host); ip != nil {
|
|
if ip := ip.To4(); ip != nil {
|
|
return [][]byte{ip}, false, nil
|
|
}
|
|
return [][]byte{ip}, false, nil
|
|
}
|
|
|
|
req := &pb.ResolveRequest{
|
|
Name: &host,
|
|
AddressFamilies: fams,
|
|
}
|
|
res := &pb.ResolveReply{}
|
|
if err := internal.Call(ctx, "remote_socket", "Resolve", req, res); err != nil {
|
|
// XXX: need to map to pb.ResolveReply_ErrorCode?
|
|
return nil, false, err
|
|
}
|
|
return res.PackedAddress, true, nil
|
|
}
|
|
|
|
// withDeadline is like context.WithDeadline, except it ignores the zero deadline.
|
|
func withDeadline(parent context.Context, deadline time.Time) (context.Context, context.CancelFunc) {
|
|
if deadline.IsZero() {
|
|
return parent, func() {}
|
|
}
|
|
return context.WithDeadline(parent, deadline)
|
|
}
|
|
|
|
// Conn represents a socket connection.
|
|
// It implements net.Conn.
|
|
type Conn struct {
|
|
ctx context.Context
|
|
desc string
|
|
offset int64
|
|
|
|
prot pb.CreateSocketRequest_SocketProtocol
|
|
local, remote *pb.AddressPort
|
|
|
|
readDeadline, writeDeadline time.Time // optional
|
|
}
|
|
|
|
// SetContext sets the context that is used by this Conn.
|
|
// It is usually used only when using a Conn that was created in a different context,
|
|
// such as when a connection is created during a warmup request but used while
|
|
// servicing a user request.
|
|
func (cn *Conn) SetContext(ctx context.Context) {
|
|
cn.ctx = ctx
|
|
}
|
|
|
|
func (cn *Conn) Read(b []byte) (n int, err error) {
|
|
const maxRead = 1 << 20
|
|
if len(b) > maxRead {
|
|
b = b[:maxRead]
|
|
}
|
|
|
|
req := &pb.ReceiveRequest{
|
|
SocketDescriptor: &cn.desc,
|
|
DataSize: proto.Int32(int32(len(b))),
|
|
}
|
|
res := &pb.ReceiveReply{}
|
|
if !cn.readDeadline.IsZero() {
|
|
req.TimeoutSeconds = proto.Float64(cn.readDeadline.Sub(time.Now()).Seconds())
|
|
}
|
|
ctx, cancel := withDeadline(cn.ctx, cn.readDeadline)
|
|
defer cancel()
|
|
if err := internal.Call(ctx, "remote_socket", "Receive", req, res); err != nil {
|
|
return 0, err
|
|
}
|
|
if len(res.Data) == 0 {
|
|
return 0, io.EOF
|
|
}
|
|
if len(res.Data) > len(b) {
|
|
return 0, fmt.Errorf("socket: internal error: read too much data: %d > %d", len(res.Data), len(b))
|
|
}
|
|
return copy(b, res.Data), nil
|
|
}
|
|
|
|
func (cn *Conn) Write(b []byte) (n int, err error) {
|
|
const lim = 1 << 20 // max per chunk
|
|
|
|
for n < len(b) {
|
|
chunk := b[n:]
|
|
if len(chunk) > lim {
|
|
chunk = chunk[:lim]
|
|
}
|
|
|
|
req := &pb.SendRequest{
|
|
SocketDescriptor: &cn.desc,
|
|
Data: chunk,
|
|
StreamOffset: &cn.offset,
|
|
}
|
|
res := &pb.SendReply{}
|
|
if !cn.writeDeadline.IsZero() {
|
|
req.TimeoutSeconds = proto.Float64(cn.writeDeadline.Sub(time.Now()).Seconds())
|
|
}
|
|
ctx, cancel := withDeadline(cn.ctx, cn.writeDeadline)
|
|
defer cancel()
|
|
if err = internal.Call(ctx, "remote_socket", "Send", req, res); err != nil {
|
|
// assume zero bytes were sent in this RPC
|
|
break
|
|
}
|
|
n += int(res.GetDataSent())
|
|
cn.offset += int64(res.GetDataSent())
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (cn *Conn) Close() error {
|
|
req := &pb.CloseRequest{
|
|
SocketDescriptor: &cn.desc,
|
|
}
|
|
res := &pb.CloseReply{}
|
|
if err := internal.Call(cn.ctx, "remote_socket", "Close", req, res); err != nil {
|
|
return err
|
|
}
|
|
cn.desc = "CLOSED"
|
|
return nil
|
|
}
|
|
|
|
func addr(prot pb.CreateSocketRequest_SocketProtocol, ap *pb.AddressPort) net.Addr {
|
|
if ap == nil {
|
|
return nil
|
|
}
|
|
switch prot {
|
|
case pb.CreateSocketRequest_TCP:
|
|
return &net.TCPAddr{
|
|
IP: net.IP(ap.PackedAddress),
|
|
Port: int(*ap.Port),
|
|
}
|
|
case pb.CreateSocketRequest_UDP:
|
|
return &net.UDPAddr{
|
|
IP: net.IP(ap.PackedAddress),
|
|
Port: int(*ap.Port),
|
|
}
|
|
}
|
|
panic("unknown protocol " + prot.String())
|
|
}
|
|
|
|
func (cn *Conn) LocalAddr() net.Addr { return addr(cn.prot, cn.local) }
|
|
func (cn *Conn) RemoteAddr() net.Addr { return addr(cn.prot, cn.remote) }
|
|
|
|
func (cn *Conn) SetDeadline(t time.Time) error {
|
|
cn.readDeadline = t
|
|
cn.writeDeadline = t
|
|
return nil
|
|
}
|
|
|
|
func (cn *Conn) SetReadDeadline(t time.Time) error {
|
|
cn.readDeadline = t
|
|
return nil
|
|
}
|
|
|
|
func (cn *Conn) SetWriteDeadline(t time.Time) error {
|
|
cn.writeDeadline = t
|
|
return nil
|
|
}
|
|
|
|
// KeepAlive signals that the connection is still in use.
|
|
// It may be called to prevent the socket being closed due to inactivity.
|
|
func (cn *Conn) KeepAlive() error {
|
|
req := &pb.GetSocketNameRequest{
|
|
SocketDescriptor: &cn.desc,
|
|
}
|
|
res := &pb.GetSocketNameReply{}
|
|
return internal.Call(cn.ctx, "remote_socket", "GetSocketName", req, res)
|
|
}
|
|
|
|
func init() {
|
|
internal.RegisterErrorCodeMap("remote_socket", pb.RemoteSocketServiceError_ErrorCode_name)
|
|
}
|