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 }