github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/aeron/channeluri.go (about)

     1  // Licensed under the Apache License, Version 2.0 (the "License");
     2  // you may not use this file except in compliance with the License.
     3  // You may obtain a copy of the License at
     4  //
     5  // http://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS,
     9  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  // See the License for the specific language governing permissions and
    11  // limitations under the License.
    12  
    13  package aeron
    14  
    15  import (
    16  	"errors"
    17  	"fmt"
    18  	"sort"
    19  	"strconv"
    20  	"strings"
    21  )
    22  
    23  // ChannelUri is a parser for Aeron channel URIs. The format is:
    24  // aeron-uri = "aeron:" media [ "?" param *( "|" param ) ]
    25  // media     = *( "[^?:]" )
    26  // param     = key "=" value
    27  // key       = *( "[^=]" )
    28  // value     = *( "[^|]" )
    29  //
    30  // Multiple params with the same key are allowed, the last value specified takes precedence.
    31  type ChannelUri struct {
    32  	prefix string
    33  	media  string
    34  	params map[string]string
    35  }
    36  
    37  const SpyQualifier = "aeron-spy"
    38  const AeronScheme = "aeron"
    39  const AeronPrefix = "aeron:"
    40  
    41  const IpcMedia = "ipc"
    42  const UdpMedia = "udp"
    43  const IpcChannel = "aeron:ipc"
    44  const SpyPrefix = "aeron-spy:"
    45  const EndpointParamName = "endpoint"
    46  const InterfaceParamName = "interface"
    47  const InitialTermIdParamName = "init-term-id"
    48  const TermIdParamName = "term-id"
    49  const TermOffsetParamName = "term-offset"
    50  const TermLengthParamName = "term-length"
    51  const MtuLengthParamName = "mtu"
    52  const TtlParamName = "ttl"
    53  const MdcControlParamName = "control"
    54  const MdcControlModeParamName = "control-mode"
    55  const MdcControlModeManual = "manual"
    56  const MdcControlModeDynamic = "dynamic"
    57  const SessionIdParamName = "session-id"
    58  const LingerParamName = "linger"
    59  const ReliableStreamParamName = "reliable"
    60  const TagsParamName = "tags"
    61  const TagPrefix = "tag:"
    62  const SparseParamName = "sparse"
    63  const AliasParamName = "alias"
    64  const EosParamName = "eos"
    65  const TetherParamName = "tether"
    66  const GroupParamName = "group"
    67  const RejoinParamName = "rejoin"
    68  const CongestionControlParamName = "cc"
    69  const FlowControlParamName = "fc"
    70  const GroupTagParamName = "gtag"
    71  const SpiesSimulateConnectionParamName = "ssc"
    72  const SocketSndbufParamName = "so-sndbuf"
    73  const SocketRcvbufParamName = "so-rcvbuf"
    74  const ReceiverWindowLengthParamName = "rcv-wnd"
    75  const MediaRcvTimestampOffsetParamName = "media-rcv-ts-offset"
    76  const ChannelRcvTimestampOffsetParamName = "channel-rcv-ts-offset"
    77  const ChannelSndTimestampOffsetParamName = "channel-snd-ts-offset"
    78  
    79  const spyPrefix = SpyQualifier + ":"
    80  const aeronPrefix = AeronScheme + ":"
    81  
    82  type parseState int8
    83  
    84  const (
    85  	parseStateMedia parseState = iota + 1
    86  	parseStateParamsKey
    87  	parseStateParamsValue
    88  )
    89  
    90  // ParseChannelUri parses a string which contains an Aeron URI.
    91  func ParseChannelUri(uriStr string) (uri ChannelUri, err error) {
    92  	uri.params = make(map[string]string)
    93  	if strings.HasPrefix(uriStr, spyPrefix) {
    94  		uri.prefix = SpyQualifier
    95  		uriStr = uriStr[len(spyPrefix):]
    96  	}
    97  	if !strings.HasPrefix(uriStr, aeronPrefix) {
    98  		err = fmt.Errorf("aeron URIs must start with 'aeron:', found '%s'", uriStr)
    99  		return
   100  	}
   101  	uriStr = uriStr[len(aeronPrefix):]
   102  
   103  	state := parseStateMedia
   104  	value := ""
   105  	key := ""
   106  	for len(uriStr) > 0 {
   107  		c := uriStr[0]
   108  		switch state {
   109  		case parseStateMedia:
   110  			switch c {
   111  			case '?':
   112  				uri.media = value
   113  				value = ""
   114  				state = parseStateParamsKey
   115  			case ':':
   116  				err = fmt.Errorf("encountered ':' within media definition")
   117  				return
   118  			default:
   119  				value += string(c)
   120  			}
   121  		case parseStateParamsKey:
   122  			if c == '=' {
   123  				key = value
   124  				value = ""
   125  				state = parseStateParamsValue
   126  			} else {
   127  				value += string(c)
   128  			}
   129  		case parseStateParamsValue:
   130  			if c == '|' {
   131  				uri.params[key] = value
   132  				value = ""
   133  				state = parseStateParamsKey
   134  			} else {
   135  				value += string(c)
   136  			}
   137  		default:
   138  			return uri, errors.New("unexpected state")
   139  		}
   140  		uriStr = uriStr[1:]
   141  	}
   142  	switch state {
   143  	case parseStateMedia:
   144  		uri.media = value
   145  	case parseStateParamsValue:
   146  		uri.params[key] = value
   147  	default:
   148  		err = fmt.Errorf("no more input found")
   149  	}
   150  	return
   151  }
   152  
   153  // Clone returns a deep copy of a ChannelUri.
   154  func (uri ChannelUri) Clone() (res ChannelUri) {
   155  	res.prefix = uri.prefix
   156  	res.media = uri.media
   157  	res.params = make(map[string]string)
   158  	for k, v := range uri.params {
   159  		res.params[k] = v
   160  	}
   161  	return
   162  }
   163  
   164  func (uri ChannelUri) Scheme() string {
   165  	return AeronScheme
   166  }
   167  
   168  func (uri ChannelUri) Prefix() string {
   169  	return uri.prefix
   170  }
   171  
   172  func (uri ChannelUri) Media() string {
   173  	return uri.media
   174  }
   175  
   176  func (uri ChannelUri) IsIpc() bool {
   177  	return uri.media == IpcMedia
   178  }
   179  
   180  func (uri ChannelUri) IsUdp() bool {
   181  	return uri.media == UdpMedia
   182  }
   183  
   184  func (uri *ChannelUri) SetPrefix(prefix string) {
   185  	uri.prefix = prefix
   186  }
   187  
   188  func (uri *ChannelUri) SetMedia(media string) {
   189  	uri.media = media
   190  }
   191  
   192  func (uri *ChannelUri) SetControlMode(controlMode string) {
   193  	uri.Set(MdcControlModeParamName, controlMode)
   194  }
   195  
   196  func (uri *ChannelUri) SetSessionID(sessionID int32) {
   197  	uri.Set(SessionIdParamName, strconv.Itoa(int(sessionID)))
   198  }
   199  
   200  func (uri ChannelUri) Get(key string) string {
   201  	return uri.params[key]
   202  }
   203  
   204  func (uri ChannelUri) Set(key string, value string) {
   205  	uri.params[key] = value
   206  }
   207  
   208  func (uri ChannelUri) Remove(key string) {
   209  	delete(uri.params, key)
   210  }
   211  
   212  func (uri ChannelUri) String() (result string) {
   213  	if uri.prefix != "" {
   214  		result += uri.prefix
   215  		if !strings.HasSuffix(uri.prefix, ":") {
   216  			result += ":"
   217  		}
   218  	}
   219  	result += aeronPrefix
   220  	result += uri.media
   221  	if len(uri.params) > 0 {
   222  		result += "?"
   223  		sortedKeys := make([]string, 0, len(uri.params))
   224  		for k := range uri.params {
   225  			sortedKeys = append(sortedKeys, k)
   226  		}
   227  		sort.Strings(sortedKeys)
   228  		for _, k := range sortedKeys {
   229  			result += k + "=" + uri.params[k] + "|"
   230  		}
   231  		result = result[:len(result)-1]
   232  	}
   233  	return
   234  }