// DialTimeout is like Dial but takes a timeout. // The timeout includes name resolution, if required. func DialTimeout(c appengine.Context, protocol, addr string, timeout time.Duration) (*Conn, error) { var deadline time.Time if timeout > 0 { deadline = time.Now().Add(timeout) } 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(c, ipFamilies, host, deadline) 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 := c.Call("remote_socket", "CreateSocket", req, res, opts(deadline)); err != nil { return nil, err } return &Conn{ c: c, desc: res.GetSocketDescriptor(), prot: prot, local: res.ProxyExternalIp, remote: req.RemoteIp, }, nil }