blob: 33c86cfc9ccb6aafd4fd831a38817634ef2bcded [file] [log] [blame]
package naming
import (
"errors"
"fmt"
"net"
"strconv"
"strings"
"veyron.io/veyron/veyron2/ipc/version"
"veyron.io/veyron/veyron2/naming"
)
const (
separator = "@"
suffix = "@@"
)
var errInvalidEndpointString = errors.New("invalid endpoint string")
// Network is the string returned by naming.Endpoint.Network implementations
// defined in this package.
const Network = "veyron"
// Endpoint is a naming.Endpoint implementation used to convey RPC information.
type Endpoint struct {
Protocol string
Address string
RID naming.RoutingID
MinIPCVersion version.IPCVersion
MaxIPCVersion version.IPCVersion
IsMountTable bool
}
// NewEndpoint creates a new endpoint from a string as per naming.NewEndpoint
func NewEndpoint(input string) (*Endpoint, error) {
ep := new(Endpoint)
// We have to guess this is a mount table if we don't know.
ep.IsMountTable = true
// The prefix and suffix are optional.
input = strings.TrimPrefix(strings.TrimSuffix(input, suffix), separator)
parts := strings.Split(input, separator)
if len(parts) == 1 {
err := ep.parseHostPort(parts[0])
return ep, err
}
version, err := strconv.ParseUint(parts[0], 10, 16)
if err != nil {
return nil, fmt.Errorf("invalid version: %v", err)
}
switch version {
case 1:
err = ep.parseV1(parts)
case 2:
err = ep.parseV2(parts)
case 3:
err = ep.parseV3(parts)
default:
err = errInvalidEndpointString
}
return ep, err
}
func (ep *Endpoint) parseHostPort(input string) error {
// Could be in host:port format.
if _, _, err := net.SplitHostPort(input); err != nil {
return errInvalidEndpointString
}
ep.Protocol = "tcp"
ep.Address = input
ep.RID = naming.NullRoutingID
return nil
}
func (ep *Endpoint) parseV1(parts []string) error {
if len(parts) != 4 {
return errInvalidEndpointString
}
ep.Protocol = parts[1]
if len(ep.Protocol) == 0 {
ep.Protocol = "tcp"
}
ep.Address = parts[2]
if len(ep.Address) == 0 {
ep.Address = net.JoinHostPort("", "0")
}
if err := ep.RID.FromString(parts[3]); err != nil {
return fmt.Errorf("invalid routing id: %v", err)
}
return nil
}
func parseIPCVersion(input string) (version.IPCVersion, error) {
if input == "" {
return version.UnknownIPCVersion, nil
}
v, err := strconv.ParseUint(input, 10, 32)
if err != nil {
err = fmt.Errorf("invalid IPC version: %s, %v", err)
}
return version.IPCVersion(v), err
}
func printIPCVersion(v version.IPCVersion) string {
if v == version.UnknownIPCVersion {
return ""
}
return strconv.FormatUint(uint64(v), 10)
}
func parseMountTableFlag(input string) (bool, error) {
switch len(input) {
case 0:
return true, nil
case 1:
switch f := input[0]; f {
case 'm':
return true, nil
case 's':
return false, nil
default:
return false, fmt.Errorf("%c is not one of 'm' or 's'", f)
}
}
return false, fmt.Errorf("flag is either missing or too long")
}
func (ep *Endpoint) parseV2(parts []string) error {
var err error
if len(parts) != 6 {
return errInvalidEndpointString
}
if err = ep.parseV1(parts[:4]); err != nil {
return err
}
if ep.MinIPCVersion, err = parseIPCVersion(parts[4]); err != nil {
return fmt.Errorf("invalid IPC version: %v", err)
}
if ep.MaxIPCVersion, err = parseIPCVersion(parts[5]); err != nil {
return fmt.Errorf("invalid IPC version: %v", err)
}
return nil
}
func (ep *Endpoint) parseV3(parts []string) error {
var err error
if len(parts) != 7 {
return errInvalidEndpointString
}
if err = ep.parseV2(parts[:6]); err != nil {
return err
}
if ep.IsMountTable, err = parseMountTableFlag(parts[6]); err != nil {
return fmt.Errorf("invalid mount table flag: %v", err)
}
return nil
}
func (ep *Endpoint) RoutingID() naming.RoutingID {
//nologcall
return ep.RID
}
func (ep *Endpoint) Network() string {
//nologcall
return Network
}
var defaultVersion = 3
func (ep *Endpoint) VersionedString(version int) string {
switch version {
default:
return ep.VersionedString(defaultVersion)
case 1:
return fmt.Sprintf("@1@%s@%s@@", ep.Protocol, ep.Address)
case 2:
return fmt.Sprintf("@2@%s@%s@%s@%s@%s@@",
ep.Protocol, ep.Address, ep.RID,
printIPCVersion(ep.MinIPCVersion), printIPCVersion(ep.MaxIPCVersion))
case 3:
mt := "s"
if ep.IsMountTable {
mt = "m"
}
return fmt.Sprintf("@3@%s@%s@%s@%s@%s@%s@@",
ep.Protocol, ep.Address, ep.RID,
printIPCVersion(ep.MinIPCVersion), printIPCVersion(ep.MaxIPCVersion),
mt)
}
}
func (ep *Endpoint) String() string {
//nologcall
return ep.VersionedString(defaultVersion)
}
func (ep *Endpoint) Addr() net.Addr {
//nologcall
return &addr{network: ep.Protocol, address: ep.Address}
}
func (ep *Endpoint) ServesMountTable() bool {
//nologcall
return ep.IsMountTable
}
type addr struct {
network, address string
}
func (a *addr) Network() string {
return a.network
}
func (a *addr) String() string {
return a.address
}