Browse Source

Add support for decoding versioned responses

This will be needed to properly parse kafka's 0.10.0 messages where the version
does not just affect the request format but also the response.
Evan Huus 9 years ago
parent
commit
37654da7e1
48 changed files with 229 additions and 77 deletions
  1. 1 1
      api_versions_request.go
  2. 9 1
      api_versions_response.go
  3. 1 1
      api_versions_response_test.go
  4. 3 3
      broker.go
  5. 1 1
      consumer_metadata_request.go
  6. 9 1
      consumer_metadata_response.go
  7. 1 1
      describe_groups_request.go
  8. 9 1
      describe_groups_response.go
  9. 2 2
      describe_groups_response_test.go
  10. 22 0
      encoder_decoder.go
  11. 1 1
      fetch_request.go
  12. 9 1
      fetch_response.go
  13. 2 2
      fetch_response_test.go
  14. 1 1
      heartbeat_request.go
  15. 9 1
      heartbeat_response.go
  16. 1 1
      heartbeat_response_test.go
  17. 1 1
      join_group_request.go
  18. 9 1
      join_group_response.go
  19. 3 3
      join_group_response_test.go
  20. 1 1
      leave_group_request.go
  21. 9 1
      leave_group_response.go
  22. 2 2
      leave_group_response_test.go
  23. 1 1
      list_groups_request.go
  24. 9 1
      list_groups_response.go
  25. 3 3
      list_groups_response_test.go
  26. 1 1
      metadata_request.go
  27. 9 1
      metadata_response.go
  28. 3 3
      metadata_response_test.go
  29. 1 1
      mockbroker.go
  30. 10 10
      mockresponses.go
  31. 3 1
      offset_commit_request.go
  32. 9 1
      offset_commit_response.go
  33. 2 1
      offset_fetch_request.go
  34. 9 1
      offset_fetch_response.go
  35. 1 1
      offset_request.go
  36. 9 1
      offset_response.go
  37. 2 2
      offset_response_test.go
  38. 1 1
      produce_request.go
  39. 9 1
      produce_response.go
  40. 2 2
      produce_response_test.go
  41. 5 5
      request.go
  42. 11 4
      request_test.go
  43. 1 1
      sasl_handshake_request.go
  44. 9 1
      sasl_handshake_response.go
  45. 1 1
      sasl_handshake_response_test.go
  46. 1 1
      sync_group_request.go
  47. 9 1
      sync_group_response.go
  48. 2 2
      sync_group_response_test.go

+ 1 - 1
api_versions_request.go

@@ -7,7 +7,7 @@ func (r *ApiVersionsRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *ApiVersionsRequest) decode(pd packetDecoder) (err error) {
+func (r *ApiVersionsRequest) decode(pd packetDecoder, version int16) (err error) {
 	return nil
 	return nil
 }
 }
 
 

+ 9 - 1
api_versions_response.go

@@ -49,7 +49,7 @@ func (r *ApiVersionsResponse) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *ApiVersionsResponse) decode(pd packetDecoder) error {
+func (r *ApiVersionsResponse) decode(pd packetDecoder, version int16) error {
 	if kerr, err := pd.getInt16(); err != nil {
 	if kerr, err := pd.getInt16(); err != nil {
 		return err
 		return err
 	} else {
 	} else {
@@ -72,3 +72,11 @@ func (r *ApiVersionsResponse) decode(pd packetDecoder) error {
 
 
 	return nil
 	return nil
 }
 }
+
+func (r *ApiVersionsResponse) key() int16 {
+	return 18
+}
+
+func (r *ApiVersionsResponse) version() int16 {
+	return 0
+}

+ 1 - 1
api_versions_response_test.go

@@ -16,7 +16,7 @@ func TestApiVersionsResponse(t *testing.T) {
 	var response *ApiVersionsResponse
 	var response *ApiVersionsResponse
 
 
 	response = new(ApiVersionsResponse)
 	response = new(ApiVersionsResponse)
-	testDecodable(t, "no error", response, apiVersionResponse)
+	testVersionDecodable(t, "no error", response, apiVersionResponse, 0)
 	if response.Err != ErrNoError {
 	if response.Err != ErrNoError {
 		t.Error("Decoding error failed: no error expected but found", response.Err)
 		t.Error("Decoding error failed: no error expected but found", response.Err)
 	}
 	}

+ 3 - 3
broker.go

@@ -317,7 +317,7 @@ func (b *Broker) DescribeGroups(request *DescribeGroupsRequest) (*DescribeGroups
 	return response, nil
 	return response, nil
 }
 }
 
 
-func (b *Broker) send(rb requestBody, promiseResponse bool) (*responsePromise, error) {
+func (b *Broker) send(rb protocolBody, promiseResponse bool) (*responsePromise, error) {
 	b.lock.Lock()
 	b.lock.Lock()
 	defer b.lock.Unlock()
 	defer b.lock.Unlock()
 
 
@@ -355,7 +355,7 @@ func (b *Broker) send(rb requestBody, promiseResponse bool) (*responsePromise, e
 	return &promise, nil
 	return &promise, nil
 }
 }
 
 
-func (b *Broker) sendAndReceive(req requestBody, res decoder) error {
+func (b *Broker) sendAndReceive(req protocolBody, res versionedDecoder) error {
 	promise, err := b.send(req, res != nil)
 	promise, err := b.send(req, res != nil)
 
 
 	if err != nil {
 	if err != nil {
@@ -368,7 +368,7 @@ func (b *Broker) sendAndReceive(req requestBody, res decoder) error {
 
 
 	select {
 	select {
 	case buf := <-promise.packets:
 	case buf := <-promise.packets:
-		return decode(buf, res)
+		return versionedDecode(buf, res, req.version())
 	case err = <-promise.errors:
 	case err = <-promise.errors:
 		return err
 		return err
 	}
 	}

+ 1 - 1
consumer_metadata_request.go

@@ -8,7 +8,7 @@ func (r *ConsumerMetadataRequest) encode(pe packetEncoder) error {
 	return pe.putString(r.ConsumerGroup)
 	return pe.putString(r.ConsumerGroup)
 }
 }
 
 
-func (r *ConsumerMetadataRequest) decode(pd packetDecoder) (err error) {
+func (r *ConsumerMetadataRequest) decode(pd packetDecoder, version int16) (err error) {
 	r.ConsumerGroup, err = pd.getString()
 	r.ConsumerGroup, err = pd.getString()
 	return err
 	return err
 }
 }

+ 9 - 1
consumer_metadata_response.go

@@ -13,7 +13,7 @@ type ConsumerMetadataResponse struct {
 	CoordinatorPort int32  // deprecated: use Coordinator.Addr()
 	CoordinatorPort int32  // deprecated: use Coordinator.Addr()
 }
 }
 
 
-func (r *ConsumerMetadataResponse) decode(pd packetDecoder) (err error) {
+func (r *ConsumerMetadataResponse) decode(pd packetDecoder, version int16) (err error) {
 	tmp, err := pd.getInt16()
 	tmp, err := pd.getInt16()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -71,3 +71,11 @@ func (r *ConsumerMetadataResponse) encode(pe packetEncoder) error {
 	pe.putInt32(r.CoordinatorPort)
 	pe.putInt32(r.CoordinatorPort)
 	return nil
 	return nil
 }
 }
+
+func (r *ConsumerMetadataResponse) key() int16 {
+	return 10
+}
+
+func (r *ConsumerMetadataResponse) version() int16 {
+	return 0
+}

+ 1 - 1
describe_groups_request.go

@@ -8,7 +8,7 @@ func (r *DescribeGroupsRequest) encode(pe packetEncoder) error {
 	return pe.putStringArray(r.Groups)
 	return pe.putStringArray(r.Groups)
 }
 }
 
 
-func (r *DescribeGroupsRequest) decode(pd packetDecoder) (err error) {
+func (r *DescribeGroupsRequest) decode(pd packetDecoder, version int16) (err error) {
 	r.Groups, err = pd.getStringArray()
 	r.Groups, err = pd.getStringArray()
 	return
 	return
 }
 }

+ 9 - 1
describe_groups_response.go

@@ -18,7 +18,7 @@ func (r *DescribeGroupsResponse) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *DescribeGroupsResponse) decode(pd packetDecoder) (err error) {
+func (r *DescribeGroupsResponse) decode(pd packetDecoder, version int16) (err error) {
 	n, err := pd.getArrayLength()
 	n, err := pd.getArrayLength()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -35,6 +35,14 @@ func (r *DescribeGroupsResponse) decode(pd packetDecoder) (err error) {
 	return nil
 	return nil
 }
 }
 
 
+func (r *DescribeGroupsResponse) key() int16 {
+	return 15
+}
+
+func (r *DescribeGroupsResponse) version() int16 {
+	return 0
+}
+
 type GroupDescription struct {
 type GroupDescription struct {
 	Err          KError
 	Err          KError
 	GroupId      string
 	GroupId      string

+ 2 - 2
describe_groups_response_test.go

@@ -38,13 +38,13 @@ func TestDescribeGroupsResponse(t *testing.T) {
 	var response *DescribeGroupsResponse
 	var response *DescribeGroupsResponse
 
 
 	response = new(DescribeGroupsResponse)
 	response = new(DescribeGroupsResponse)
-	testDecodable(t, "empty", response, describeGroupsResponseEmpty)
+	testVersionDecodable(t, "empty", response, describeGroupsResponseEmpty, 0)
 	if len(response.Groups) != 0 {
 	if len(response.Groups) != 0 {
 		t.Error("Expected no groups")
 		t.Error("Expected no groups")
 	}
 	}
 
 
 	response = new(DescribeGroupsResponse)
 	response = new(DescribeGroupsResponse)
-	testDecodable(t, "populated", response, describeGroupsResponsePopulated)
+	testVersionDecodable(t, "populated", response, describeGroupsResponsePopulated, 0)
 	if len(response.Groups) != 2 {
 	if len(response.Groups) != 2 {
 		t.Error("Expected two groups")
 		t.Error("Expected two groups")
 	}
 	}

+ 22 - 0
encoder_decoder.go

@@ -41,6 +41,10 @@ type decoder interface {
 	decode(pd packetDecoder) error
 	decode(pd packetDecoder) error
 }
 }
 
 
+type versionedDecoder interface {
+	decode(pd packetDecoder, version int16) error
+}
+
 // Decode takes bytes and a Decoder and fills the fields of the decoder from the bytes,
 // Decode takes bytes and a Decoder and fills the fields of the decoder from the bytes,
 // interpreted using Kafka's encoding rules.
 // interpreted using Kafka's encoding rules.
 func decode(buf []byte, in decoder) error {
 func decode(buf []byte, in decoder) error {
@@ -60,3 +64,21 @@ func decode(buf []byte, in decoder) error {
 
 
 	return nil
 	return nil
 }
 }
+
+func versionedDecode(buf []byte, in versionedDecoder, version int16) error {
+	if buf == nil {
+		return nil
+	}
+
+	helper := realDecoder{raw: buf}
+	err := in.decode(&helper, version)
+	if err != nil {
+		return err
+	}
+
+	if helper.off != len(buf) {
+		return PacketDecodingError{"invalid length"}
+	}
+
+	return nil
+}

+ 1 - 1
fetch_request.go

@@ -55,7 +55,7 @@ func (f *FetchRequest) encode(pe packetEncoder) (err error) {
 	return nil
 	return nil
 }
 }
 
 
-func (f *FetchRequest) decode(pd packetDecoder) (err error) {
+func (f *FetchRequest) decode(pd packetDecoder, version int16) (err error) {
 	if _, err = pd.getInt32(); err != nil {
 	if _, err = pd.getInt32(); err != nil {
 		return err
 		return err
 	}
 	}

+ 9 - 1
fetch_response.go

@@ -49,7 +49,7 @@ func (pr *FetchResponseBlock) encode(pe packetEncoder) (err error) {
 	return pe.pop()
 	return pe.pop()
 }
 }
 
 
-func (fr *FetchResponse) decode(pd packetDecoder) (err error) {
+func (fr *FetchResponse) decode(pd packetDecoder, version int16) (err error) {
 	numTopics, err := pd.getArrayLength()
 	numTopics, err := pd.getArrayLength()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -116,6 +116,14 @@ func (fr *FetchResponse) encode(pe packetEncoder) (err error) {
 	return nil
 	return nil
 }
 }
 
 
+func (r *FetchResponse) key() int16 {
+	return 1
+}
+
+func (r *FetchResponse) version() int16 {
+	return 0
+}
+
 func (fr *FetchResponse) GetBlock(topic string, partition int32) *FetchResponseBlock {
 func (fr *FetchResponse) GetBlock(topic string, partition int32) *FetchResponseBlock {
 	if fr.Blocks == nil {
 	if fr.Blocks == nil {
 		return nil
 		return nil

+ 2 - 2
fetch_response_test.go

@@ -30,7 +30,7 @@ var (
 
 
 func TestEmptyFetchResponse(t *testing.T) {
 func TestEmptyFetchResponse(t *testing.T) {
 	response := FetchResponse{}
 	response := FetchResponse{}
-	testDecodable(t, "empty", &response, emptyFetchResponse)
+	testVersionDecodable(t, "empty", &response, emptyFetchResponse, 0)
 
 
 	if len(response.Blocks) != 0 {
 	if len(response.Blocks) != 0 {
 		t.Error("Decoding produced topic blocks where there were none.")
 		t.Error("Decoding produced topic blocks where there were none.")
@@ -40,7 +40,7 @@ func TestEmptyFetchResponse(t *testing.T) {
 
 
 func TestOneMessageFetchResponse(t *testing.T) {
 func TestOneMessageFetchResponse(t *testing.T) {
 	response := FetchResponse{}
 	response := FetchResponse{}
-	testDecodable(t, "one message", &response, oneMessageFetchResponse)
+	testVersionDecodable(t, "one message", &response, oneMessageFetchResponse, 0)
 
 
 	if len(response.Blocks) != 1 {
 	if len(response.Blocks) != 1 {
 		t.Fatal("Decoding produced incorrect number of topic blocks.")
 		t.Fatal("Decoding produced incorrect number of topic blocks.")

+ 1 - 1
heartbeat_request.go

@@ -20,7 +20,7 @@ func (r *HeartbeatRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *HeartbeatRequest) decode(pd packetDecoder) (err error) {
+func (r *HeartbeatRequest) decode(pd packetDecoder, version int16) (err error) {
 	if r.GroupId, err = pd.getString(); err != nil {
 	if r.GroupId, err = pd.getString(); err != nil {
 		return
 		return
 	}
 	}

+ 9 - 1
heartbeat_response.go

@@ -9,7 +9,7 @@ func (r *HeartbeatResponse) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *HeartbeatResponse) decode(pd packetDecoder) error {
+func (r *HeartbeatResponse) decode(pd packetDecoder, version int16) error {
 	if kerr, err := pd.getInt16(); err != nil {
 	if kerr, err := pd.getInt16(); err != nil {
 		return err
 		return err
 	} else {
 	} else {
@@ -18,3 +18,11 @@ func (r *HeartbeatResponse) decode(pd packetDecoder) error {
 
 
 	return nil
 	return nil
 }
 }
+
+func (r *HeartbeatResponse) key() int16 {
+	return 12
+}
+
+func (r *HeartbeatResponse) version() int16 {
+	return 0
+}

+ 1 - 1
heartbeat_response_test.go

@@ -11,7 +11,7 @@ func TestHeartbeatResponse(t *testing.T) {
 	var response *HeartbeatResponse
 	var response *HeartbeatResponse
 
 
 	response = new(HeartbeatResponse)
 	response = new(HeartbeatResponse)
-	testDecodable(t, "no error", response, heartbeatResponseNoError)
+	testVersionDecodable(t, "no error", response, heartbeatResponseNoError, 0)
 	if response.Err != ErrNoError {
 	if response.Err != ErrNoError {
 		t.Error("Decoding error failed: no error expected but found", response.Err)
 		t.Error("Decoding error failed: no error expected but found", response.Err)
 	}
 	}

+ 1 - 1
join_group_request.go

@@ -35,7 +35,7 @@ func (r *JoinGroupRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *JoinGroupRequest) decode(pd packetDecoder) (err error) {
+func (r *JoinGroupRequest) decode(pd packetDecoder, version int16) (err error) {
 	if r.GroupId, err = pd.getString(); err != nil {
 	if r.GroupId, err = pd.getString(); err != nil {
 		return
 		return
 	}
 	}

+ 9 - 1
join_group_response.go

@@ -52,7 +52,7 @@ func (r *JoinGroupResponse) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *JoinGroupResponse) decode(pd packetDecoder) (err error) {
+func (r *JoinGroupResponse) decode(pd packetDecoder, version int16) (err error) {
 	if kerr, err := pd.getInt16(); err != nil {
 	if kerr, err := pd.getInt16(); err != nil {
 		return err
 		return err
 	} else {
 	} else {
@@ -100,3 +100,11 @@ func (r *JoinGroupResponse) decode(pd packetDecoder) (err error) {
 
 
 	return nil
 	return nil
 }
 }
+
+func (r *JoinGroupResponse) key() int16 {
+	return 11
+}
+
+func (r *JoinGroupResponse) version() int16 {
+	return 0
+}

+ 3 - 3
join_group_response_test.go

@@ -40,7 +40,7 @@ func TestJoinGroupResponse(t *testing.T) {
 	var response *JoinGroupResponse
 	var response *JoinGroupResponse
 
 
 	response = new(JoinGroupResponse)
 	response = new(JoinGroupResponse)
-	testDecodable(t, "no error", response, joinGroupResponseNoError)
+	testVersionDecodable(t, "no error", response, joinGroupResponseNoError, 0)
 	if response.Err != ErrNoError {
 	if response.Err != ErrNoError {
 		t.Error("Decoding Err failed: no error expected but found", response.Err)
 		t.Error("Decoding Err failed: no error expected but found", response.Err)
 	}
 	}
@@ -58,7 +58,7 @@ func TestJoinGroupResponse(t *testing.T) {
 	}
 	}
 
 
 	response = new(JoinGroupResponse)
 	response = new(JoinGroupResponse)
-	testDecodable(t, "with error", response, joinGroupResponseWithError)
+	testVersionDecodable(t, "with error", response, joinGroupResponseWithError, 0)
 	if response.Err != ErrInconsistentGroupProtocol {
 	if response.Err != ErrInconsistentGroupProtocol {
 		t.Error("Decoding Err failed: ErrInconsistentGroupProtocol expected but found", response.Err)
 		t.Error("Decoding Err failed: ErrInconsistentGroupProtocol expected but found", response.Err)
 	}
 	}
@@ -76,7 +76,7 @@ func TestJoinGroupResponse(t *testing.T) {
 	}
 	}
 
 
 	response = new(JoinGroupResponse)
 	response = new(JoinGroupResponse)
-	testDecodable(t, "with error", response, joinGroupResponseLeader)
+	testVersionDecodable(t, "with error", response, joinGroupResponseLeader, 0)
 	if response.Err != ErrNoError {
 	if response.Err != ErrNoError {
 		t.Error("Decoding Err failed: ErrNoError expected but found", response.Err)
 		t.Error("Decoding Err failed: ErrNoError expected but found", response.Err)
 	}
 	}

+ 1 - 1
leave_group_request.go

@@ -16,7 +16,7 @@ func (r *LeaveGroupRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *LeaveGroupRequest) decode(pd packetDecoder) (err error) {
+func (r *LeaveGroupRequest) decode(pd packetDecoder, version int16) (err error) {
 	if r.GroupId, err = pd.getString(); err != nil {
 	if r.GroupId, err = pd.getString(); err != nil {
 		return
 		return
 	}
 	}

+ 9 - 1
leave_group_response.go

@@ -9,7 +9,7 @@ func (r *LeaveGroupResponse) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *LeaveGroupResponse) decode(pd packetDecoder) (err error) {
+func (r *LeaveGroupResponse) decode(pd packetDecoder, version int16) (err error) {
 	if kerr, err := pd.getInt16(); err != nil {
 	if kerr, err := pd.getInt16(); err != nil {
 		return err
 		return err
 	} else {
 	} else {
@@ -18,3 +18,11 @@ func (r *LeaveGroupResponse) decode(pd packetDecoder) (err error) {
 
 
 	return nil
 	return nil
 }
 }
+
+func (r *LeaveGroupResponse) key() int16 {
+	return 13
+}
+
+func (r *LeaveGroupResponse) version() int16 {
+	return 0
+}

+ 2 - 2
leave_group_response_test.go

@@ -11,13 +11,13 @@ func TestLeaveGroupResponse(t *testing.T) {
 	var response *LeaveGroupResponse
 	var response *LeaveGroupResponse
 
 
 	response = new(LeaveGroupResponse)
 	response = new(LeaveGroupResponse)
-	testDecodable(t, "no error", response, leaveGroupResponseNoError)
+	testVersionDecodable(t, "no error", response, leaveGroupResponseNoError, 0)
 	if response.Err != ErrNoError {
 	if response.Err != ErrNoError {
 		t.Error("Decoding error failed: no error expected but found", response.Err)
 		t.Error("Decoding error failed: no error expected but found", response.Err)
 	}
 	}
 
 
 	response = new(LeaveGroupResponse)
 	response = new(LeaveGroupResponse)
-	testDecodable(t, "with error", response, leaveGroupResponseWithError)
+	testVersionDecodable(t, "with error", response, leaveGroupResponseWithError, 0)
 	if response.Err != ErrUnknownMemberId {
 	if response.Err != ErrUnknownMemberId {
 		t.Error("Decoding error failed: ErrUnknownMemberId expected but found", response.Err)
 		t.Error("Decoding error failed: ErrUnknownMemberId expected but found", response.Err)
 	}
 	}

+ 1 - 1
list_groups_request.go

@@ -7,7 +7,7 @@ func (r *ListGroupsRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *ListGroupsRequest) decode(pd packetDecoder) (err error) {
+func (r *ListGroupsRequest) decode(pd packetDecoder, version int16) (err error) {
 	return nil
 	return nil
 }
 }
 
 

+ 9 - 1
list_groups_response.go

@@ -23,7 +23,7 @@ func (r *ListGroupsResponse) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *ListGroupsResponse) decode(pd packetDecoder) error {
+func (r *ListGroupsResponse) decode(pd packetDecoder, version int16) error {
 	if kerr, err := pd.getInt16(); err != nil {
 	if kerr, err := pd.getInt16(); err != nil {
 		return err
 		return err
 	} else {
 	} else {
@@ -54,3 +54,11 @@ func (r *ListGroupsResponse) decode(pd packetDecoder) error {
 
 
 	return nil
 	return nil
 }
 }
+
+func (r *ListGroupsResponse) key() int16 {
+	return 16
+}
+
+func (r *ListGroupsResponse) version() int16 {
+	return 0
+}

+ 3 - 3
list_groups_response_test.go

@@ -27,7 +27,7 @@ func TestListGroupsResponse(t *testing.T) {
 	var response *ListGroupsResponse
 	var response *ListGroupsResponse
 
 
 	response = new(ListGroupsResponse)
 	response = new(ListGroupsResponse)
-	testDecodable(t, "no error", response, listGroupsResponseEmpty)
+	testVersionDecodable(t, "no error", response, listGroupsResponseEmpty, 0)
 	if response.Err != ErrNoError {
 	if response.Err != ErrNoError {
 		t.Error("Expected no gerror, found:", response.Err)
 		t.Error("Expected no gerror, found:", response.Err)
 	}
 	}
@@ -36,7 +36,7 @@ func TestListGroupsResponse(t *testing.T) {
 	}
 	}
 
 
 	response = new(ListGroupsResponse)
 	response = new(ListGroupsResponse)
-	testDecodable(t, "no error", response, listGroupsResponseError)
+	testVersionDecodable(t, "no error", response, listGroupsResponseError, 0)
 	if response.Err != ErrClusterAuthorizationFailed {
 	if response.Err != ErrClusterAuthorizationFailed {
 		t.Error("Expected no gerror, found:", response.Err)
 		t.Error("Expected no gerror, found:", response.Err)
 	}
 	}
@@ -45,7 +45,7 @@ func TestListGroupsResponse(t *testing.T) {
 	}
 	}
 
 
 	response = new(ListGroupsResponse)
 	response = new(ListGroupsResponse)
-	testDecodable(t, "no error", response, listGroupsResponseWithConsumer)
+	testVersionDecodable(t, "no error", response, listGroupsResponseWithConsumer, 0)
 	if response.Err != ErrNoError {
 	if response.Err != ErrNoError {
 		t.Error("Expected no gerror, found:", response.Err)
 		t.Error("Expected no gerror, found:", response.Err)
 	}
 	}

+ 1 - 1
metadata_request.go

@@ -19,7 +19,7 @@ func (mr *MetadataRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (mr *MetadataRequest) decode(pd packetDecoder) error {
+func (mr *MetadataRequest) decode(pd packetDecoder, version int16) error {
 	topicCount, err := pd.getArrayLength()
 	topicCount, err := pd.getArrayLength()
 	if err != nil {
 	if err != nil {
 		return err
 		return err

+ 9 - 1
metadata_response.go

@@ -118,7 +118,7 @@ type MetadataResponse struct {
 	Topics  []*TopicMetadata
 	Topics  []*TopicMetadata
 }
 }
 
 
-func (m *MetadataResponse) decode(pd packetDecoder) (err error) {
+func (m *MetadataResponse) decode(pd packetDecoder, version int16) (err error) {
 	n, err := pd.getArrayLength()
 	n, err := pd.getArrayLength()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -176,6 +176,14 @@ func (m *MetadataResponse) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
+func (r *MetadataResponse) key() int16 {
+	return 3
+}
+
+func (r *MetadataResponse) version() int16 {
+	return 0
+}
+
 // testing API
 // testing API
 
 
 func (m *MetadataResponse) AddBroker(addr string, id int32) {
 func (m *MetadataResponse) AddBroker(addr string, id int32) {

+ 3 - 3
metadata_response_test.go

@@ -41,7 +41,7 @@ var (
 func TestEmptyMetadataResponse(t *testing.T) {
 func TestEmptyMetadataResponse(t *testing.T) {
 	response := MetadataResponse{}
 	response := MetadataResponse{}
 
 
-	testDecodable(t, "empty", &response, emptyMetadataResponse)
+	testVersionDecodable(t, "empty", &response, emptyMetadataResponse, 0)
 	if len(response.Brokers) != 0 {
 	if len(response.Brokers) != 0 {
 		t.Error("Decoding produced", len(response.Brokers), "brokers where there were none!")
 		t.Error("Decoding produced", len(response.Brokers), "brokers where there were none!")
 	}
 	}
@@ -53,7 +53,7 @@ func TestEmptyMetadataResponse(t *testing.T) {
 func TestMetadataResponseWithBrokers(t *testing.T) {
 func TestMetadataResponseWithBrokers(t *testing.T) {
 	response := MetadataResponse{}
 	response := MetadataResponse{}
 
 
-	testDecodable(t, "brokers, no topics", &response, brokersNoTopicsMetadataResponse)
+	testVersionDecodable(t, "brokers, no topics", &response, brokersNoTopicsMetadataResponse, 0)
 	if len(response.Brokers) != 2 {
 	if len(response.Brokers) != 2 {
 		t.Fatal("Decoding produced", len(response.Brokers), "brokers where there were two!")
 		t.Fatal("Decoding produced", len(response.Brokers), "brokers where there were two!")
 	}
 	}
@@ -79,7 +79,7 @@ func TestMetadataResponseWithBrokers(t *testing.T) {
 func TestMetadataResponseWithTopics(t *testing.T) {
 func TestMetadataResponseWithTopics(t *testing.T) {
 	response := MetadataResponse{}
 	response := MetadataResponse{}
 
 
-	testDecodable(t, "topics, no brokers", &response, topicsNoBrokersMetadataResponse)
+	testVersionDecodable(t, "topics, no brokers", &response, topicsNoBrokersMetadataResponse, 0)
 	if len(response.Brokers) != 0 {
 	if len(response.Brokers) != 0 {
 		t.Error("Decoding produced", len(response.Brokers), "brokers where there were none!")
 		t.Error("Decoding produced", len(response.Brokers), "brokers where there were none!")
 	}
 	}

+ 1 - 1
mockbroker.go

@@ -60,7 +60,7 @@ type MockBroker struct {
 
 
 // RequestResponse represents a Request/Response pair processed by MockBroker.
 // RequestResponse represents a Request/Response pair processed by MockBroker.
 type RequestResponse struct {
 type RequestResponse struct {
-	Request  requestBody
+	Request  protocolBody
 	Response encoder
 	Response encoder
 }
 }
 
 

+ 10 - 10
mockresponses.go

@@ -17,7 +17,7 @@ type TestReporter interface {
 // allows generating a response based on a request body. MockResponses are used
 // allows generating a response based on a request body. MockResponses are used
 // to program behavior of MockBroker in tests.
 // to program behavior of MockBroker in tests.
 type MockResponse interface {
 type MockResponse interface {
-	For(reqBody decoder) (res encoder)
+	For(reqBody versionedDecoder) (res encoder)
 }
 }
 
 
 // MockWrapper is a mock response builder that returns a particular concrete
 // MockWrapper is a mock response builder that returns a particular concrete
@@ -26,7 +26,7 @@ type MockWrapper struct {
 	res encoder
 	res encoder
 }
 }
 
 
-func (mw *MockWrapper) For(reqBody decoder) (res encoder) {
+func (mw *MockWrapper) For(reqBody versionedDecoder) (res encoder) {
 	return mw.res
 	return mw.res
 }
 }
 
 
@@ -58,7 +58,7 @@ func NewMockSequence(responses ...interface{}) *MockSequence {
 	return ms
 	return ms
 }
 }
 
 
-func (mc *MockSequence) For(reqBody decoder) (res encoder) {
+func (mc *MockSequence) For(reqBody versionedDecoder) (res encoder) {
 	res = mc.responses[0].For(reqBody)
 	res = mc.responses[0].For(reqBody)
 	if len(mc.responses) > 1 {
 	if len(mc.responses) > 1 {
 		mc.responses = mc.responses[1:]
 		mc.responses = mc.responses[1:]
@@ -96,7 +96,7 @@ func (mmr *MockMetadataResponse) SetBroker(addr string, brokerID int32) *MockMet
 	return mmr
 	return mmr
 }
 }
 
 
-func (mmr *MockMetadataResponse) For(reqBody decoder) encoder {
+func (mmr *MockMetadataResponse) For(reqBody versionedDecoder) encoder {
 	metadataRequest := reqBody.(*MetadataRequest)
 	metadataRequest := reqBody.(*MetadataRequest)
 	metadataResponse := &MetadataResponse{}
 	metadataResponse := &MetadataResponse{}
 	for addr, brokerID := range mmr.brokers {
 	for addr, brokerID := range mmr.brokers {
@@ -146,7 +146,7 @@ func (mor *MockOffsetResponse) SetOffset(topic string, partition int32, time, of
 	return mor
 	return mor
 }
 }
 
 
-func (mor *MockOffsetResponse) For(reqBody decoder) encoder {
+func (mor *MockOffsetResponse) For(reqBody versionedDecoder) encoder {
 	offsetRequest := reqBody.(*OffsetRequest)
 	offsetRequest := reqBody.(*OffsetRequest)
 	offsetResponse := &OffsetResponse{}
 	offsetResponse := &OffsetResponse{}
 	for topic, partitions := range offsetRequest.blocks {
 	for topic, partitions := range offsetRequest.blocks {
@@ -216,7 +216,7 @@ func (mfr *MockFetchResponse) SetHighWaterMark(topic string, partition int32, of
 	return mfr
 	return mfr
 }
 }
 
 
-func (mfr *MockFetchResponse) For(reqBody decoder) encoder {
+func (mfr *MockFetchResponse) For(reqBody versionedDecoder) encoder {
 	fetchRequest := reqBody.(*FetchRequest)
 	fetchRequest := reqBody.(*FetchRequest)
 	res := &FetchResponse{}
 	res := &FetchResponse{}
 	for topic, partitions := range fetchRequest.blocks {
 	for topic, partitions := range fetchRequest.blocks {
@@ -298,7 +298,7 @@ func (mr *MockConsumerMetadataResponse) SetError(group string, kerror KError) *M
 	return mr
 	return mr
 }
 }
 
 
-func (mr *MockConsumerMetadataResponse) For(reqBody decoder) encoder {
+func (mr *MockConsumerMetadataResponse) For(reqBody versionedDecoder) encoder {
 	req := reqBody.(*ConsumerMetadataRequest)
 	req := reqBody.(*ConsumerMetadataRequest)
 	group := req.ConsumerGroup
 	group := req.ConsumerGroup
 	res := &ConsumerMetadataResponse{}
 	res := &ConsumerMetadataResponse{}
@@ -340,7 +340,7 @@ func (mr *MockOffsetCommitResponse) SetError(group, topic string, partition int3
 	return mr
 	return mr
 }
 }
 
 
-func (mr *MockOffsetCommitResponse) For(reqBody decoder) encoder {
+func (mr *MockOffsetCommitResponse) For(reqBody versionedDecoder) encoder {
 	req := reqBody.(*OffsetCommitRequest)
 	req := reqBody.(*OffsetCommitRequest)
 	group := req.ConsumerGroup
 	group := req.ConsumerGroup
 	res := &OffsetCommitResponse{}
 	res := &OffsetCommitResponse{}
@@ -391,7 +391,7 @@ func (mr *MockProduceResponse) SetError(topic string, partition int32, kerror KE
 	return mr
 	return mr
 }
 }
 
 
-func (mr *MockProduceResponse) For(reqBody decoder) encoder {
+func (mr *MockProduceResponse) For(reqBody versionedDecoder) encoder {
 	req := reqBody.(*ProduceRequest)
 	req := reqBody.(*ProduceRequest)
 	res := &ProduceResponse{}
 	res := &ProduceResponse{}
 	for topic, partitions := range req.msgSets {
 	for topic, partitions := range req.msgSets {
@@ -442,7 +442,7 @@ func (mr *MockOffsetFetchResponse) SetOffset(group, topic string, partition int3
 	return mr
 	return mr
 }
 }
 
 
-func (mr *MockOffsetFetchResponse) For(reqBody decoder) encoder {
+func (mr *MockOffsetFetchResponse) For(reqBody versionedDecoder) encoder {
 	req := reqBody.(*OffsetFetchRequest)
 	req := reqBody.(*OffsetFetchRequest)
 	group := req.ConsumerGroup
 	group := req.ConsumerGroup
 	res := &OffsetFetchResponse{}
 	res := &OffsetFetchResponse{}

+ 3 - 1
offset_commit_request.go

@@ -103,7 +103,9 @@ func (r *OffsetCommitRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *OffsetCommitRequest) decode(pd packetDecoder) (err error) {
+func (r *OffsetCommitRequest) decode(pd packetDecoder, version int16) (err error) {
+	r.Version = version
+
 	if r.ConsumerGroup, err = pd.getString(); err != nil {
 	if r.ConsumerGroup, err = pd.getString(); err != nil {
 		return err
 		return err
 	}
 	}

+ 9 - 1
offset_commit_response.go

@@ -35,7 +35,7 @@ func (r *OffsetCommitResponse) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *OffsetCommitResponse) decode(pd packetDecoder) (err error) {
+func (r *OffsetCommitResponse) decode(pd packetDecoder, version int16) (err error) {
 	numTopics, err := pd.getArrayLength()
 	numTopics, err := pd.getArrayLength()
 	if err != nil || numTopics == 0 {
 	if err != nil || numTopics == 0 {
 		return err
 		return err
@@ -71,3 +71,11 @@ func (r *OffsetCommitResponse) decode(pd packetDecoder) (err error) {
 
 
 	return nil
 	return nil
 }
 }
+
+func (r *OffsetCommitResponse) key() int16 {
+	return 8
+}
+
+func (r *OffsetCommitResponse) version() int16 {
+	return 0
+}

+ 2 - 1
offset_fetch_request.go

@@ -28,7 +28,8 @@ func (r *OffsetFetchRequest) encode(pe packetEncoder) (err error) {
 	return nil
 	return nil
 }
 }
 
 
-func (r *OffsetFetchRequest) decode(pd packetDecoder) (err error) {
+func (r *OffsetFetchRequest) decode(pd packetDecoder, version int16) (err error) {
+	r.Version = version
 	if r.ConsumerGroup, err = pd.getString(); err != nil {
 	if r.ConsumerGroup, err = pd.getString(); err != nil {
 		return err
 		return err
 	}
 	}

+ 9 - 1
offset_fetch_response.go

@@ -64,7 +64,7 @@ func (r *OffsetFetchResponse) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *OffsetFetchResponse) decode(pd packetDecoder) (err error) {
+func (r *OffsetFetchResponse) decode(pd packetDecoder, version int16) (err error) {
 	numTopics, err := pd.getArrayLength()
 	numTopics, err := pd.getArrayLength()
 	if err != nil || numTopics == 0 {
 	if err != nil || numTopics == 0 {
 		return err
 		return err
@@ -106,6 +106,14 @@ func (r *OffsetFetchResponse) decode(pd packetDecoder) (err error) {
 	return nil
 	return nil
 }
 }
 
 
+func (r *OffsetFetchResponse) key() int16 {
+	return 9
+}
+
+func (r *OffsetFetchResponse) version() int16 {
+	return 0
+}
+
 func (r *OffsetFetchResponse) GetBlock(topic string, partition int32) *OffsetFetchResponseBlock {
 func (r *OffsetFetchResponse) GetBlock(topic string, partition int32) *OffsetFetchResponseBlock {
 	if r.Blocks == nil {
 	if r.Blocks == nil {
 		return nil
 		return nil

+ 1 - 1
offset_request.go

@@ -50,7 +50,7 @@ func (r *OffsetRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *OffsetRequest) decode(pd packetDecoder) error {
+func (r *OffsetRequest) decode(pd packetDecoder, version int16) error {
 	// Ignore replica ID
 	// Ignore replica ID
 	if _, err := pd.getInt32(); err != nil {
 	if _, err := pd.getInt32(); err != nil {
 		return err
 		return err

+ 9 - 1
offset_response.go

@@ -27,7 +27,7 @@ type OffsetResponse struct {
 	Blocks map[string]map[int32]*OffsetResponseBlock
 	Blocks map[string]map[int32]*OffsetResponseBlock
 }
 }
 
 
-func (r *OffsetResponse) decode(pd packetDecoder) (err error) {
+func (r *OffsetResponse) decode(pd packetDecoder, version int16) (err error) {
 	numTopics, err := pd.getArrayLength()
 	numTopics, err := pd.getArrayLength()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -115,6 +115,14 @@ func (r *OffsetResponse) encode(pe packetEncoder) (err error) {
 	return nil
 	return nil
 }
 }
 
 
+func (r *OffsetResponse) key() int16 {
+	return 2
+}
+
+func (r *OffsetResponse) version() int16 {
+	return 0
+}
+
 // testing API
 // testing API
 
 
 func (r *OffsetResponse) AddTopicPartition(topic string, partition int32, offset int64) {
 func (r *OffsetResponse) AddTopicPartition(topic string, partition int32, offset int64) {

+ 2 - 2
offset_response_test.go

@@ -24,7 +24,7 @@ var (
 func TestEmptyOffsetResponse(t *testing.T) {
 func TestEmptyOffsetResponse(t *testing.T) {
 	response := OffsetResponse{}
 	response := OffsetResponse{}
 
 
-	testDecodable(t, "empty", &response, emptyOffsetResponse)
+	testVersionDecodable(t, "empty", &response, emptyOffsetResponse, 0)
 	if len(response.Blocks) != 0 {
 	if len(response.Blocks) != 0 {
 		t.Error("Decoding produced", len(response.Blocks), "topics where there were none.")
 		t.Error("Decoding produced", len(response.Blocks), "topics where there were none.")
 	}
 	}
@@ -33,7 +33,7 @@ func TestEmptyOffsetResponse(t *testing.T) {
 func TestNormalOffsetResponse(t *testing.T) {
 func TestNormalOffsetResponse(t *testing.T) {
 	response := OffsetResponse{}
 	response := OffsetResponse{}
 
 
-	testDecodable(t, "normal", &response, normalOffsetResponse)
+	testVersionDecodable(t, "normal", &response, normalOffsetResponse, 0)
 
 
 	if len(response.Blocks) != 2 {
 	if len(response.Blocks) != 2 {
 		t.Fatal("Decoding produced", len(response.Blocks), "topics where there were two.")
 		t.Fatal("Decoding produced", len(response.Blocks), "topics where there were two.")

+ 1 - 1
produce_request.go

@@ -54,7 +54,7 @@ func (p *ProduceRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (p *ProduceRequest) decode(pd packetDecoder) error {
+func (p *ProduceRequest) decode(pd packetDecoder, version int16) error {
 	requiredAcks, err := pd.getInt16()
 	requiredAcks, err := pd.getInt16()
 	if err != nil {
 	if err != nil {
 		return err
 		return err

+ 9 - 1
produce_response.go

@@ -24,7 +24,7 @@ type ProduceResponse struct {
 	Blocks map[string]map[int32]*ProduceResponseBlock
 	Blocks map[string]map[int32]*ProduceResponseBlock
 }
 }
 
 
-func (pr *ProduceResponse) decode(pd packetDecoder) (err error) {
+func (pr *ProduceResponse) decode(pd packetDecoder, version int16) (err error) {
 	numTopics, err := pd.getArrayLength()
 	numTopics, err := pd.getArrayLength()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -85,6 +85,14 @@ func (pr *ProduceResponse) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
+func (r *ProduceResponse) key() int16 {
+	return 0
+}
+
+func (r *ProduceResponse) version() int16 {
+	return 0
+}
+
 func (pr *ProduceResponse) GetBlock(topic string, partition int32) *ProduceResponseBlock {
 func (pr *ProduceResponse) GetBlock(topic string, partition int32) *ProduceResponseBlock {
 	if pr.Blocks == nil {
 	if pr.Blocks == nil {
 		return nil
 		return nil

+ 2 - 2
produce_response_test.go

@@ -27,12 +27,12 @@ var (
 func TestProduceResponse(t *testing.T) {
 func TestProduceResponse(t *testing.T) {
 	response := ProduceResponse{}
 	response := ProduceResponse{}
 
 
-	testDecodable(t, "no blocks", &response, produceResponseNoBlocks)
+	testVersionDecodable(t, "no blocks", &response, produceResponseNoBlocks, 0)
 	if len(response.Blocks) != 0 {
 	if len(response.Blocks) != 0 {
 		t.Error("Decoding produced", len(response.Blocks), "topics where there were none")
 		t.Error("Decoding produced", len(response.Blocks), "topics where there were none")
 	}
 	}
 
 
-	testDecodable(t, "many blocks", &response, produceResponseManyBlocks)
+	testVersionDecodable(t, "many blocks", &response, produceResponseManyBlocks, 0)
 	if len(response.Blocks) != 2 {
 	if len(response.Blocks) != 2 {
 		t.Error("Decoding produced", len(response.Blocks), "topics where there were 2")
 		t.Error("Decoding produced", len(response.Blocks), "topics where there were 2")
 	}
 	}

+ 5 - 5
request.go

@@ -6,9 +6,9 @@ import (
 	"io"
 	"io"
 )
 )
 
 
-type requestBody interface {
+type protocolBody interface {
 	encoder
 	encoder
-	decoder
+	versionedDecoder
 	key() int16
 	key() int16
 	version() int16
 	version() int16
 }
 }
@@ -16,7 +16,7 @@ type requestBody interface {
 type request struct {
 type request struct {
 	correlationID int32
 	correlationID int32
 	clientID      string
 	clientID      string
-	body          requestBody
+	body          protocolBody
 }
 }
 
 
 func (r *request) encode(pe packetEncoder) (err error) {
 func (r *request) encode(pe packetEncoder) (err error) {
@@ -53,7 +53,7 @@ func (r *request) decode(pd packetDecoder) (err error) {
 	if r.body == nil {
 	if r.body == nil {
 		return PacketDecodingError{fmt.Sprintf("unknown request key (%d)", key)}
 		return PacketDecodingError{fmt.Sprintf("unknown request key (%d)", key)}
 	}
 	}
-	return r.body.decode(pd)
+	return r.body.decode(pd, version)
 }
 }
 
 
 func decodeRequest(r io.Reader) (req *request, err error) {
 func decodeRequest(r io.Reader) (req *request, err error) {
@@ -79,7 +79,7 @@ func decodeRequest(r io.Reader) (req *request, err error) {
 	return req, nil
 	return req, nil
 }
 }
 
 
-func allocateBody(key, version int16) requestBody {
+func allocateBody(key, version int16) protocolBody {
 	switch key {
 	switch key {
 	case 0:
 	case 0:
 		return &ProduceRequest{}
 		return &ProduceRequest{}

+ 11 - 4
request_test.go

@@ -40,7 +40,14 @@ func testDecodable(t *testing.T, name string, out decoder, in []byte) {
 	}
 	}
 }
 }
 
 
-func testRequest(t *testing.T, name string, rb requestBody, expected []byte) {
+func testVersionDecodable(t *testing.T, name string, out versionedDecoder, in []byte, version int16) {
+	err := versionedDecode(in, out, version)
+	if err != nil {
+		t.Error("Decoding", name, "version", version, "failed:", err)
+	}
+}
+
+func testRequest(t *testing.T, name string, rb protocolBody, expected []byte) {
 	// Encoder request
 	// Encoder request
 	req := &request{correlationID: 123, clientID: "foo", body: rb}
 	req := &request{correlationID: 123, clientID: "foo", body: rb}
 	packet, err := encode(req)
 	packet, err := encode(req)
@@ -61,7 +68,7 @@ func testRequest(t *testing.T, name string, rb requestBody, expected []byte) {
 	}
 	}
 }
 }
 
 
-func testResponse(t *testing.T, name string, res encoder, expected []byte) {
+func testResponse(t *testing.T, name string, res protocolBody, expected []byte) {
 	encoded, err := encode(res)
 	encoded, err := encode(res)
 	if err != nil {
 	if err != nil {
 		t.Error(err)
 		t.Error(err)
@@ -69,8 +76,8 @@ func testResponse(t *testing.T, name string, res encoder, expected []byte) {
 		t.Error("Encoding", name, "failed\ngot ", encoded, "\nwant", expected)
 		t.Error("Encoding", name, "failed\ngot ", encoded, "\nwant", expected)
 	}
 	}
 
 
-	decoded := reflect.New(reflect.TypeOf(res).Elem()).Interface().(decoder)
-	if err := decode(encoded, decoded); err != nil {
+	decoded := reflect.New(reflect.TypeOf(res).Elem()).Interface().(versionedDecoder)
+	if err := versionedDecode(encoded, decoded, res.version()); err != nil {
 		t.Error("Decoding", name, "failed:", err)
 		t.Error("Decoding", name, "failed:", err)
 	}
 	}
 
 

+ 1 - 1
sasl_handshake_request.go

@@ -12,7 +12,7 @@ func (r *SaslHandshakeRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *SaslHandshakeRequest) decode(pd packetDecoder) (err error) {
+func (r *SaslHandshakeRequest) decode(pd packetDecoder, version int16) (err error) {
 	if r.Mechanism, err = pd.getString(); err != nil {
 	if r.Mechanism, err = pd.getString(); err != nil {
 		return err
 		return err
 	}
 	}

+ 9 - 1
sasl_handshake_response.go

@@ -10,7 +10,7 @@ func (r *SaslHandshakeResponse) encode(pe packetEncoder) error {
 	return pe.putStringArray(r.EnabledMechanisms)
 	return pe.putStringArray(r.EnabledMechanisms)
 }
 }
 
 
-func (r *SaslHandshakeResponse) decode(pd packetDecoder) error {
+func (r *SaslHandshakeResponse) decode(pd packetDecoder, version int16) error {
 	if kerr, err := pd.getInt16(); err != nil {
 	if kerr, err := pd.getInt16(); err != nil {
 		return err
 		return err
 	} else {
 	} else {
@@ -24,3 +24,11 @@ func (r *SaslHandshakeResponse) decode(pd packetDecoder) error {
 
 
 	return nil
 	return nil
 }
 }
+
+func (r *SaslHandshakeResponse) key() int16 {
+	return 17
+}
+
+func (r *SaslHandshakeResponse) version() int16 {
+	return 0
+}

+ 1 - 1
sasl_handshake_response_test.go

@@ -14,7 +14,7 @@ func TestSaslHandshakeResponse(t *testing.T) {
 	var response *SaslHandshakeResponse
 	var response *SaslHandshakeResponse
 
 
 	response = new(SaslHandshakeResponse)
 	response = new(SaslHandshakeResponse)
-	testDecodable(t, "no error", response, saslHandshakeResponse)
+	testVersionDecodable(t, "no error", response, saslHandshakeResponse, 0)
 	if response.Err != ErrNoError {
 	if response.Err != ErrNoError {
 		t.Error("Decoding error failed: no error expected but found", response.Err)
 		t.Error("Decoding error failed: no error expected but found", response.Err)
 	}
 	}

+ 1 - 1
sync_group_request.go

@@ -33,7 +33,7 @@ func (r *SyncGroupRequest) encode(pe packetEncoder) error {
 	return nil
 	return nil
 }
 }
 
 
-func (r *SyncGroupRequest) decode(pd packetDecoder) (err error) {
+func (r *SyncGroupRequest) decode(pd packetDecoder, version int16) (err error) {
 	if r.GroupId, err = pd.getString(); err != nil {
 	if r.GroupId, err = pd.getString(); err != nil {
 		return
 		return
 	}
 	}

+ 9 - 1
sync_group_response.go

@@ -16,7 +16,7 @@ func (r *SyncGroupResponse) encode(pe packetEncoder) error {
 	return pe.putBytes(r.MemberAssignment)
 	return pe.putBytes(r.MemberAssignment)
 }
 }
 
 
-func (r *SyncGroupResponse) decode(pd packetDecoder) (err error) {
+func (r *SyncGroupResponse) decode(pd packetDecoder, version int16) (err error) {
 	if kerr, err := pd.getInt16(); err != nil {
 	if kerr, err := pd.getInt16(); err != nil {
 		return err
 		return err
 	} else {
 	} else {
@@ -26,3 +26,11 @@ func (r *SyncGroupResponse) decode(pd packetDecoder) (err error) {
 	r.MemberAssignment, err = pd.getBytes()
 	r.MemberAssignment, err = pd.getBytes()
 	return
 	return
 }
 }
+
+func (r *SyncGroupResponse) key() int16 {
+	return 14
+}
+
+func (r *SyncGroupResponse) version() int16 {
+	return 0
+}

+ 2 - 2
sync_group_response_test.go

@@ -21,7 +21,7 @@ func TestSyncGroupResponse(t *testing.T) {
 	var response *SyncGroupResponse
 	var response *SyncGroupResponse
 
 
 	response = new(SyncGroupResponse)
 	response = new(SyncGroupResponse)
-	testDecodable(t, "no error", response, syncGroupResponseNoError)
+	testVersionDecodable(t, "no error", response, syncGroupResponseNoError, 0)
 	if response.Err != ErrNoError {
 	if response.Err != ErrNoError {
 		t.Error("Decoding Err failed: no error expected but found", response.Err)
 		t.Error("Decoding Err failed: no error expected but found", response.Err)
 	}
 	}
@@ -30,7 +30,7 @@ func TestSyncGroupResponse(t *testing.T) {
 	}
 	}
 
 
 	response = new(SyncGroupResponse)
 	response = new(SyncGroupResponse)
-	testDecodable(t, "no error", response, syncGroupResponseWithError)
+	testVersionDecodable(t, "no error", response, syncGroupResponseWithError, 0)
 	if response.Err != ErrRebalanceInProgress {
 	if response.Err != ErrRebalanceInProgress {
 		t.Error("Decoding Err failed: ErrRebalanceInProgress expected but found", response.Err)
 		t.Error("Decoding Err failed: ErrRebalanceInProgress expected but found", response.Err)
 	}
 	}