github.com/hack0072008/kafka-go@v1.0.1/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  }