github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/google.golang.org/appengine/socket/socket_classic.go (about) 1 // Copyright 2012 Google Inc. All rights reserved. 2 // Use of this source code is governed by the Apache 2.0 3 // license that can be found in the LICENSE file. 4 5 // +build appengine 6 7 package socket 8 9 import ( 10 "fmt" 11 "io" 12 "net" 13 "strconv" 14 "time" 15 16 "github.com/golang/protobuf/proto" 17 "golang.org/x/net/context" 18 "google.golang.org/appengine/internal" 19 20 pb "google.golang.org/appengine/internal/socket" 21 ) 22 23 // Dial connects to the address addr on the network protocol. 24 // The address format is host:port, where host may be a hostname or an IP address. 25 // Known protocols are "tcp" and "udp". 26 // The returned connection satisfies net.Conn, and is valid while ctx is valid; 27 // if the connection is to be used after ctx becomes invalid, invoke SetContext 28 // with the new context. 29 func Dial(ctx context.Context, protocol, addr string) (*Conn, error) { 30 return DialTimeout(ctx, protocol, addr, 0) 31 } 32 33 var ipFamilies = []pb.CreateSocketRequest_SocketFamily{ 34 pb.CreateSocketRequest_IPv4, 35 pb.CreateSocketRequest_IPv6, 36 } 37 38 // DialTimeout is like Dial but takes a timeout. 39 // The timeout includes name resolution, if required. 40 func DialTimeout(ctx context.Context, protocol, addr string, timeout time.Duration) (*Conn, error) { 41 dialCtx := ctx // Used for dialing and name resolution, but not stored in the *Conn. 42 if timeout > 0 { 43 var cancel context.CancelFunc 44 dialCtx, cancel = context.WithTimeout(ctx, timeout) 45 defer cancel() 46 } 47 48 host, portStr, err := net.SplitHostPort(addr) 49 if err != nil { 50 return nil, err 51 } 52 port, err := strconv.Atoi(portStr) 53 if err != nil { 54 return nil, fmt.Errorf("socket: bad port %q: %v", portStr, err) 55 } 56 57 var prot pb.CreateSocketRequest_SocketProtocol 58 switch protocol { 59 case "tcp": 60 prot = pb.CreateSocketRequest_TCP 61 case "udp": 62 prot = pb.CreateSocketRequest_UDP 63 default: 64 return nil, fmt.Errorf("socket: unknown protocol %q", protocol) 65 } 66 67 packedAddrs, resolved, err := resolve(dialCtx, ipFamilies, host) 68 if err != nil { 69 return nil, fmt.Errorf("socket: failed resolving %q: %v", host, err) 70 } 71 if len(packedAddrs) == 0 { 72 return nil, fmt.Errorf("no addresses for %q", host) 73 } 74 75 packedAddr := packedAddrs[0] // use first address 76 fam := pb.CreateSocketRequest_IPv4 77 if len(packedAddr) == net.IPv6len { 78 fam = pb.CreateSocketRequest_IPv6 79 } 80 81 req := &pb.CreateSocketRequest{ 82 Family: fam.Enum(), 83 Protocol: prot.Enum(), 84 RemoteIp: &pb.AddressPort{ 85 Port: proto.Int32(int32(port)), 86 PackedAddress: packedAddr, 87 }, 88 } 89 if resolved { 90 req.RemoteIp.HostnameHint = &host 91 } 92 res := &pb.CreateSocketReply{} 93 if err := internal.Call(dialCtx, "remote_socket", "CreateSocket", req, res); err != nil { 94 return nil, err 95 } 96 97 return &Conn{ 98 ctx: ctx, 99 desc: res.GetSocketDescriptor(), 100 prot: prot, 101 local: res.ProxyExternalIp, 102 remote: req.RemoteIp, 103 }, nil 104 } 105 106 // LookupIP returns the given host's IP addresses. 107 func LookupIP(ctx context.Context, host string) (addrs []net.IP, err error) { 108 packedAddrs, _, err := resolve(ctx, ipFamilies, host) 109 if err != nil { 110 return nil, fmt.Errorf("socket: failed resolving %q: %v", host, err) 111 } 112 addrs = make([]net.IP, len(packedAddrs)) 113 for i, pa := range packedAddrs { 114 addrs[i] = net.IP(pa) 115 } 116 return addrs, nil 117 } 118 119 func resolve(ctx context.Context, fams []pb.CreateSocketRequest_SocketFamily, host string) ([][]byte, bool, error) { 120 // Check if it's an IP address. 121 if ip := net.ParseIP(host); ip != nil { 122 if ip := ip.To4(); ip != nil { 123 return [][]byte{ip}, false, nil 124 } 125 return [][]byte{ip}, false, nil 126 } 127 128 req := &pb.ResolveRequest{ 129 Name: &host, 130 AddressFamilies: fams, 131 } 132 res := &pb.ResolveReply{} 133 if err := internal.Call(ctx, "remote_socket", "Resolve", req, res); err != nil { 134 // XXX: need to map to pb.ResolveReply_ErrorCode? 135 return nil, false, err 136 } 137 return res.PackedAddress, true, nil 138 } 139 140 // withDeadline is like context.WithDeadline, except it ignores the zero deadline. 141 func withDeadline(parent context.Context, deadline time.Time) (context.Context, context.CancelFunc) { 142 if deadline.IsZero() { 143 return parent, func() {} 144 } 145 return context.WithDeadline(parent, deadline) 146 } 147 148 // Conn represents a socket connection. 149 // It implements net.Conn. 150 type Conn struct { 151 ctx context.Context 152 desc string 153 offset int64 154 155 prot pb.CreateSocketRequest_SocketProtocol 156 local, remote *pb.AddressPort 157 158 readDeadline, writeDeadline time.Time // optional 159 } 160 161 // SetContext sets the context that is used by this Conn. 162 // It is usually used only when using a Conn that was created in a different context, 163 // such as when a connection is created during a warmup request but used while 164 // servicing a user request. 165 func (cn *Conn) SetContext(ctx context.Context) { 166 cn.ctx = ctx 167 } 168 169 func (cn *Conn) Read(b []byte) (n int, err error) { 170 const maxRead = 1 << 20 171 if len(b) > maxRead { 172 b = b[:maxRead] 173 } 174 175 req := &pb.ReceiveRequest{ 176 SocketDescriptor: &cn.desc, 177 DataSize: proto.Int32(int32(len(b))), 178 } 179 res := &pb.ReceiveReply{} 180 if !cn.readDeadline.IsZero() { 181 req.TimeoutSeconds = proto.Float64(cn.readDeadline.Sub(time.Now()).Seconds()) 182 } 183 ctx, cancel := withDeadline(cn.ctx, cn.readDeadline) 184 defer cancel() 185 if err := internal.Call(ctx, "remote_socket", "Receive", req, res); err != nil { 186 return 0, err 187 } 188 if len(res.Data) == 0 { 189 return 0, io.EOF 190 } 191 if len(res.Data) > len(b) { 192 return 0, fmt.Errorf("socket: internal error: read too much data: %d > %d", len(res.Data), len(b)) 193 } 194 return copy(b, res.Data), nil 195 } 196 197 func (cn *Conn) Write(b []byte) (n int, err error) { 198 const lim = 1 << 20 // max per chunk 199 200 for n < len(b) { 201 chunk := b[n:] 202 if len(chunk) > lim { 203 chunk = chunk[:lim] 204 } 205 206 req := &pb.SendRequest{ 207 SocketDescriptor: &cn.desc, 208 Data: chunk, 209 StreamOffset: &cn.offset, 210 } 211 res := &pb.SendReply{} 212 if !cn.writeDeadline.IsZero() { 213 req.TimeoutSeconds = proto.Float64(cn.writeDeadline.Sub(time.Now()).Seconds()) 214 } 215 ctx, cancel := withDeadline(cn.ctx, cn.writeDeadline) 216 defer cancel() 217 if err = internal.Call(ctx, "remote_socket", "Send", req, res); err != nil { 218 // assume zero bytes were sent in this RPC 219 break 220 } 221 n += int(res.GetDataSent()) 222 cn.offset += int64(res.GetDataSent()) 223 } 224 225 return 226 } 227 228 func (cn *Conn) Close() error { 229 req := &pb.CloseRequest{ 230 SocketDescriptor: &cn.desc, 231 } 232 res := &pb.CloseReply{} 233 if err := internal.Call(cn.ctx, "remote_socket", "Close", req, res); err != nil { 234 return err 235 } 236 cn.desc = "CLOSED" 237 return nil 238 } 239 240 func addr(prot pb.CreateSocketRequest_SocketProtocol, ap *pb.AddressPort) net.Addr { 241 if ap == nil { 242 return nil 243 } 244 switch prot { 245 case pb.CreateSocketRequest_TCP: 246 return &net.TCPAddr{ 247 IP: net.IP(ap.PackedAddress), 248 Port: int(*ap.Port), 249 } 250 case pb.CreateSocketRequest_UDP: 251 return &net.UDPAddr{ 252 IP: net.IP(ap.PackedAddress), 253 Port: int(*ap.Port), 254 } 255 } 256 panic("unknown protocol " + prot.String()) 257 } 258 259 func (cn *Conn) LocalAddr() net.Addr { return addr(cn.prot, cn.local) } 260 func (cn *Conn) RemoteAddr() net.Addr { return addr(cn.prot, cn.remote) } 261 262 func (cn *Conn) SetDeadline(t time.Time) error { 263 cn.readDeadline = t 264 cn.writeDeadline = t 265 return nil 266 } 267 268 func (cn *Conn) SetReadDeadline(t time.Time) error { 269 cn.readDeadline = t 270 return nil 271 } 272 273 func (cn *Conn) SetWriteDeadline(t time.Time) error { 274 cn.writeDeadline = t 275 return nil 276 } 277 278 // KeepAlive signals that the connection is still in use. 279 // It may be called to prevent the socket being closed due to inactivity. 280 func (cn *Conn) KeepAlive() error { 281 req := &pb.GetSocketNameRequest{ 282 SocketDescriptor: &cn.desc, 283 } 284 res := &pb.GetSocketNameReply{} 285 return internal.Call(cn.ctx, "remote_socket", "GetSocketName", req, res) 286 } 287 288 func init() { 289 internal.RegisterErrorCodeMap("remote_socket", pb.RemoteSocketServiceError_ErrorCode_name) 290 }