github.com/rbisecke/kafka-go@v0.4.27/protocol/protocol.go (about) 1 package protocol 2 3 import ( 4 "fmt" 5 "io" 6 "net" 7 "reflect" 8 "strconv" 9 "strings" 10 ) 11 12 // Message is an interface implemented by all request and response types of the 13 // kafka protocol. 14 // 15 // This interface is used mostly as a safe-guard to provide a compile-time check 16 // for values passed to functions dealing kafka message types. 17 type Message interface { 18 ApiKey() ApiKey 19 } 20 21 type ApiKey int16 22 23 func (k ApiKey) String() string { 24 if i := int(k); i >= 0 && i < len(apiNames) { 25 return apiNames[i] 26 } 27 return strconv.Itoa(int(k)) 28 } 29 30 func (k ApiKey) MinVersion() int16 { return k.apiType().minVersion() } 31 32 func (k ApiKey) MaxVersion() int16 { return k.apiType().maxVersion() } 33 34 func (k ApiKey) SelectVersion(minVersion, maxVersion int16) int16 { 35 min := k.MinVersion() 36 max := k.MaxVersion() 37 switch { 38 case min > maxVersion: 39 return min 40 case max < maxVersion: 41 return max 42 default: 43 return maxVersion 44 } 45 } 46 47 func (k ApiKey) apiType() apiType { 48 if i := int(k); i >= 0 && i < len(apiTypes) { 49 return apiTypes[i] 50 } 51 return apiType{} 52 } 53 54 const ( 55 Produce ApiKey = 0 56 Fetch ApiKey = 1 57 ListOffsets ApiKey = 2 58 Metadata ApiKey = 3 59 LeaderAndIsr ApiKey = 4 60 StopReplica ApiKey = 5 61 UpdateMetadata ApiKey = 6 62 ControlledShutdown ApiKey = 7 63 OffsetCommit ApiKey = 8 64 OffsetFetch ApiKey = 9 65 FindCoordinator ApiKey = 10 66 JoinGroup ApiKey = 11 67 Heartbeat ApiKey = 12 68 LeaveGroup ApiKey = 13 69 SyncGroup ApiKey = 14 70 DescribeGroups ApiKey = 15 71 ListGroups ApiKey = 16 72 SaslHandshake ApiKey = 17 73 ApiVersions ApiKey = 18 74 CreateTopics ApiKey = 19 75 DeleteTopics ApiKey = 20 76 DeleteRecords ApiKey = 21 77 InitProducerId ApiKey = 22 78 OffsetForLeaderEpoch ApiKey = 23 79 AddPartitionsToTxn ApiKey = 24 80 AddOffsetsToTxn ApiKey = 25 81 EndTxn ApiKey = 26 82 WriteTxnMarkers ApiKey = 27 83 TxnOffsetCommit ApiKey = 28 84 DescribeAcls ApiKey = 29 85 CreateAcls ApiKey = 30 86 DeleteAcls ApiKey = 31 87 DescribeConfigs ApiKey = 32 88 AlterConfigs ApiKey = 33 89 AlterReplicaLogDirs ApiKey = 34 90 DescribeLogDirs ApiKey = 35 91 SaslAuthenticate ApiKey = 36 92 CreatePartitions ApiKey = 37 93 CreateDelegationToken ApiKey = 38 94 RenewDelegationToken ApiKey = 39 95 ExpireDelegationToken ApiKey = 40 96 DescribeDelegationToken ApiKey = 41 97 DeleteGroups ApiKey = 42 98 ElectLeaders ApiKey = 43 99 IncrementalAlterConfigs ApiKey = 44 100 AlterPartitionReassignments ApiKey = 45 101 ListPartitionReassignments ApiKey = 46 102 OffsetDelete ApiKey = 47 103 DescribeClientQuotas ApiKey = 48 104 AlterClientQuotas ApiKey = 49 105 106 numApis = 50 107 ) 108 109 var apiNames = [numApis]string{ 110 Produce: "Produce", 111 Fetch: "Fetch", 112 ListOffsets: "ListOffsets", 113 Metadata: "Metadata", 114 LeaderAndIsr: "LeaderAndIsr", 115 StopReplica: "StopReplica", 116 UpdateMetadata: "UpdateMetadata", 117 ControlledShutdown: "ControlledShutdown", 118 OffsetCommit: "OffsetCommit", 119 OffsetFetch: "OffsetFetch", 120 FindCoordinator: "FindCoordinator", 121 JoinGroup: "JoinGroup", 122 Heartbeat: "Heartbeat", 123 LeaveGroup: "LeaveGroup", 124 SyncGroup: "SyncGroup", 125 DescribeGroups: "DescribeGroups", 126 ListGroups: "ListGroups", 127 SaslHandshake: "SaslHandshake", 128 ApiVersions: "ApiVersions", 129 CreateTopics: "CreateTopics", 130 DeleteTopics: "DeleteTopics", 131 DeleteRecords: "DeleteRecords", 132 InitProducerId: "InitProducerId", 133 OffsetForLeaderEpoch: "OffsetForLeaderEpoch", 134 AddPartitionsToTxn: "AddPartitionsToTxn", 135 AddOffsetsToTxn: "AddOffsetsToTxn", 136 EndTxn: "EndTxn", 137 WriteTxnMarkers: "WriteTxnMarkers", 138 TxnOffsetCommit: "TxnOffsetCommit", 139 DescribeAcls: "DescribeAcls", 140 CreateAcls: "CreateAcls", 141 DeleteAcls: "DeleteAcls", 142 DescribeConfigs: "DescribeConfigs", 143 AlterConfigs: "AlterConfigs", 144 AlterReplicaLogDirs: "AlterReplicaLogDirs", 145 DescribeLogDirs: "DescribeLogDirs", 146 SaslAuthenticate: "SaslAuthenticate", 147 CreatePartitions: "CreatePartitions", 148 CreateDelegationToken: "CreateDelegationToken", 149 RenewDelegationToken: "RenewDelegationToken", 150 ExpireDelegationToken: "ExpireDelegationToken", 151 DescribeDelegationToken: "DescribeDelegationToken", 152 DeleteGroups: "DeleteGroups", 153 ElectLeaders: "ElectLeaders", 154 IncrementalAlterConfigs: "IncrementalAlterConfigs", 155 AlterPartitionReassignments: "AlterPartitionReassignments", 156 ListPartitionReassignments: "ListPartitionReassignments", 157 OffsetDelete: "OffsetDelete", 158 DescribeClientQuotas: "DescribeClientQuotas", 159 AlterClientQuotas: "AlterClientQuotas", 160 } 161 162 type messageType struct { 163 version int16 164 flexible bool 165 gotype reflect.Type 166 decode decodeFunc 167 encode encodeFunc 168 } 169 170 func (t *messageType) new() Message { 171 return reflect.New(t.gotype).Interface().(Message) 172 } 173 174 type apiType struct { 175 requests []messageType 176 responses []messageType 177 } 178 179 func (t apiType) minVersion() int16 { 180 if len(t.requests) == 0 { 181 return 0 182 } 183 return t.requests[0].version 184 } 185 186 func (t apiType) maxVersion() int16 { 187 if len(t.requests) == 0 { 188 return 0 189 } 190 return t.requests[len(t.requests)-1].version 191 } 192 193 var apiTypes [numApis]apiType 194 195 // Register is automatically called by sub-packages are imported to install a 196 // new pair of request/response message types. 197 func Register(req, res Message) { 198 k1 := req.ApiKey() 199 k2 := res.ApiKey() 200 201 if k1 != k2 { 202 panic(fmt.Sprintf("[%T/%T]: request and response API keys mismatch: %d != %d", req, res, k1, k2)) 203 } 204 205 apiTypes[k1] = apiType{ 206 requests: typesOf(req), 207 responses: typesOf(res), 208 } 209 } 210 211 func typesOf(v interface{}) []messageType { 212 return makeTypes(reflect.TypeOf(v).Elem()) 213 } 214 215 func makeTypes(t reflect.Type) []messageType { 216 minVersion := int16(-1) 217 maxVersion := int16(-1) 218 219 // All future versions will be flexible (according to spec), so don't need to 220 // worry about maxes here. 221 minFlexibleVersion := int16(-1) 222 223 forEachStructField(t, func(_ reflect.Type, _ index, tag string) { 224 forEachStructTag(tag, func(tag structTag) bool { 225 if minVersion < 0 || tag.MinVersion < minVersion { 226 minVersion = tag.MinVersion 227 } 228 if maxVersion < 0 || tag.MaxVersion > maxVersion { 229 maxVersion = tag.MaxVersion 230 } 231 if tag.TagID > -2 && (minFlexibleVersion < 0 || tag.MinVersion < minFlexibleVersion) { 232 minFlexibleVersion = tag.MinVersion 233 } 234 return true 235 }) 236 }) 237 238 types := make([]messageType, 0, (maxVersion-minVersion)+1) 239 240 for v := minVersion; v <= maxVersion; v++ { 241 flexible := minFlexibleVersion >= 0 && v >= minFlexibleVersion 242 243 types = append(types, messageType{ 244 version: v, 245 gotype: t, 246 flexible: flexible, 247 decode: decodeFuncOf(t, v, flexible, structTag{}), 248 encode: encodeFuncOf(t, v, flexible, structTag{}), 249 }) 250 } 251 252 return types 253 } 254 255 type structTag struct { 256 MinVersion int16 257 MaxVersion int16 258 Compact bool 259 Nullable bool 260 TagID int 261 } 262 263 func forEachStructTag(tag string, do func(structTag) bool) { 264 if tag == "-" { 265 return // special case to ignore the field 266 } 267 268 forEach(tag, '|', func(s string) bool { 269 tag := structTag{ 270 MinVersion: -1, 271 MaxVersion: -1, 272 273 // Legitimate tag IDs can start at 0. We use -1 as a placeholder to indicate 274 // that the message type is flexible, so that leaves -2 as the default for 275 // indicating that there is no tag ID and the message is not flexible. 276 TagID: -2, 277 } 278 279 var err error 280 forEach(s, ',', func(s string) bool { 281 switch { 282 case strings.HasPrefix(s, "min="): 283 tag.MinVersion, err = parseVersion(s[4:]) 284 case strings.HasPrefix(s, "max="): 285 tag.MaxVersion, err = parseVersion(s[4:]) 286 case s == "tag": 287 tag.TagID = -1 288 case strings.HasPrefix(s, "tag="): 289 tag.TagID, err = strconv.Atoi(s[4:]) 290 case s == "compact": 291 tag.Compact = true 292 case s == "nullable": 293 tag.Nullable = true 294 default: 295 err = fmt.Errorf("unrecognized option: %q", s) 296 } 297 return err == nil 298 }) 299 300 if err != nil { 301 panic(fmt.Errorf("malformed struct tag: %w", err)) 302 } 303 304 if tag.MinVersion < 0 && tag.MaxVersion >= 0 { 305 panic(fmt.Errorf("missing minimum version in struct tag: %q", s)) 306 } 307 308 if tag.MaxVersion < 0 && tag.MinVersion >= 0 { 309 panic(fmt.Errorf("missing maximum version in struct tag: %q", s)) 310 } 311 312 if tag.MinVersion > tag.MaxVersion { 313 panic(fmt.Errorf("invalid version range in struct tag: %q", s)) 314 } 315 316 return do(tag) 317 }) 318 } 319 320 func forEach(s string, sep byte, do func(string) bool) bool { 321 for len(s) != 0 { 322 p := "" 323 i := strings.IndexByte(s, sep) 324 if i < 0 { 325 p, s = s, "" 326 } else { 327 p, s = s[:i], s[i+1:] 328 } 329 if !do(p) { 330 return false 331 } 332 } 333 return true 334 } 335 336 func forEachStructField(t reflect.Type, do func(reflect.Type, index, string)) { 337 for i, n := 0, t.NumField(); i < n; i++ { 338 f := t.Field(i) 339 340 if f.PkgPath != "" && f.Name != "_" { 341 continue 342 } 343 344 kafkaTag, ok := f.Tag.Lookup("kafka") 345 if !ok { 346 kafkaTag = "|" 347 } 348 349 do(f.Type, indexOf(f), kafkaTag) 350 } 351 } 352 353 func parseVersion(s string) (int16, error) { 354 if !strings.HasPrefix(s, "v") { 355 return 0, fmt.Errorf("invalid version number: %q", s) 356 } 357 i, err := strconv.ParseInt(s[1:], 10, 16) 358 if err != nil { 359 return 0, fmt.Errorf("invalid version number: %q: %w", s, err) 360 } 361 if i < 0 { 362 return 0, fmt.Errorf("invalid negative version number: %q", s) 363 } 364 return int16(i), nil 365 } 366 367 func dontExpectEOF(err error) error { 368 switch err { 369 case nil: 370 return nil 371 case io.EOF: 372 return io.ErrUnexpectedEOF 373 default: 374 return err 375 } 376 } 377 378 type Broker struct { 379 Rack string 380 Host string 381 Port int32 382 ID int32 383 } 384 385 func (b Broker) String() string { 386 return net.JoinHostPort(b.Host, itoa(b.Port)) 387 } 388 389 func (b Broker) Format(w fmt.State, v rune) { 390 switch v { 391 case 'd': 392 io.WriteString(w, itoa(b.ID)) 393 case 's': 394 io.WriteString(w, b.String()) 395 case 'v': 396 io.WriteString(w, itoa(b.ID)) 397 io.WriteString(w, " ") 398 io.WriteString(w, b.String()) 399 if b.Rack != "" { 400 io.WriteString(w, " ") 401 io.WriteString(w, b.Rack) 402 } 403 } 404 } 405 406 func itoa(i int32) string { 407 return strconv.Itoa(int(i)) 408 } 409 410 type Topic struct { 411 Name string 412 Error int16 413 Partitions map[int32]Partition 414 } 415 416 type Partition struct { 417 ID int32 418 Error int16 419 Leader int32 420 Replicas []int32 421 ISR []int32 422 Offline []int32 423 } 424 425 // BrokerMessage is an extension of the Message interface implemented by some 426 // request types to customize the broker assignment logic. 427 type BrokerMessage interface { 428 // Given a representation of the kafka cluster state as argument, returns 429 // the broker that the message should be routed to. 430 Broker(Cluster) (Broker, error) 431 } 432 433 // GroupMessage is an extension of the Message interface implemented by some 434 // request types to inform the program that they should be routed to a group 435 // coordinator. 436 type GroupMessage interface { 437 // Returns the group configured on the message. 438 Group() string 439 } 440 441 // TransactionalMessage is an extension of the Message interface implemented by some 442 // request types to inform the program that they should be routed to a transaction 443 // coordinator. 444 type TransactionalMessage interface { 445 // Returns the transactional id configured on the message. 446 Transaction() string 447 } 448 449 // PreparedMessage is an extension of the Message interface implemented by some 450 // request types which may need to run some pre-processing on their state before 451 // being sent. 452 type PreparedMessage interface { 453 // Prepares the message before being sent to a kafka broker using the API 454 // version passed as argument. 455 Prepare(apiVersion int16) 456 } 457 458 // Splitter is an interface implemented by messages that can be split into 459 // multiple requests and have their results merged back by a Merger. 460 type Splitter interface { 461 // For a given cluster layout, returns the list of messages constructed 462 // from the receiver for each requests that should be sent to the cluster. 463 // The second return value is a Merger which can be used to merge back the 464 // results of each request into a single message (or an error). 465 Split(Cluster) ([]Message, Merger, error) 466 } 467 468 // Merger is an interface implemented by messages which can merge multiple 469 // results into one response. 470 type Merger interface { 471 // Given a list of message and associated results, merge them back into a 472 // response (or an error). The results must be either Message or error 473 // values, other types should trigger a panic. 474 Merge(messages []Message, results []interface{}) (Message, error) 475 } 476 477 // Result converts r to a Message or and error, or panics if r could be be 478 // converted to these types. 479 func Result(r interface{}) (Message, error) { 480 switch v := r.(type) { 481 case Message: 482 return v, nil 483 case error: 484 return nil, v 485 default: 486 panic(fmt.Errorf("BUG: result must be a message or an error but not %T", v)) 487 } 488 }