github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/kafka/response.go (about)

     1  // Copyright 2017 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package kafka
    16  
    17  import (
    18  	"encoding/binary"
    19  	"encoding/json"
    20  	"fmt"
    21  
    22  	"github.com/optiopay/kafka/proto"
    23  	"io"
    24  )
    25  
    26  // ResponseMessage represents a Kafka response message.
    27  type ResponseMessage struct {
    28  	rawMsg   []byte
    29  	response interface{}
    30  }
    31  
    32  // GetCorrelationID returns the Kafka request correlationID
    33  func (res *ResponseMessage) GetCorrelationID() CorrelationID {
    34  	if len(res.rawMsg) >= 8 {
    35  		return CorrelationID(binary.BigEndian.Uint32(res.rawMsg[4:8]))
    36  	}
    37  
    38  	return CorrelationID(0)
    39  }
    40  
    41  // SetCorrelationID modified the correlation ID of the Kafka request
    42  func (res *ResponseMessage) SetCorrelationID(id CorrelationID) {
    43  	if len(res.rawMsg) >= 8 {
    44  		binary.BigEndian.PutUint32(res.rawMsg[4:8], uint32(id))
    45  	}
    46  }
    47  
    48  // GetRaw returns the raw Kafka response
    49  func (res *ResponseMessage) GetRaw() []byte {
    50  	return res.rawMsg
    51  }
    52  
    53  // String returns a human readable representation of the response message
    54  func (res *ResponseMessage) String() string {
    55  	b, err := json.Marshal(res.response)
    56  	if err != nil {
    57  		return err.Error()
    58  	}
    59  	return string(b)
    60  }
    61  
    62  // ReadResponse will read a Kafka response from an io.Reader and return the
    63  // message or an error.
    64  func ReadResponse(reader io.Reader) (*ResponseMessage, error) {
    65  	rsp := &ResponseMessage{}
    66  	var err error
    67  
    68  	_, rsp.rawMsg, err = proto.ReadResp(reader)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	if len(rsp.rawMsg) < 6 {
    74  		return nil,
    75  			fmt.Errorf("unexpected end of response (length < 6 bytes)")
    76  	}
    77  
    78  	return rsp, nil
    79  }
    80  
    81  func createProduceResponse(req *proto.ProduceReq, err error) (*ResponseMessage, error) {
    82  	if req == nil {
    83  		return nil, fmt.Errorf("request is nil")
    84  	}
    85  
    86  	resp := &proto.ProduceResp{
    87  		CorrelationID: req.CorrelationID,
    88  		Topics:        make([]proto.ProduceRespTopic, len(req.Topics)),
    89  	}
    90  
    91  	for k, topic := range req.Topics {
    92  		resp.Topics[k] = proto.ProduceRespTopic{
    93  			Name:       topic.Name,
    94  			Partitions: make([]proto.ProduceRespPartition, len(topic.Partitions)),
    95  		}
    96  
    97  		for k2, partition := range topic.Partitions {
    98  			resp.Topics[k].Partitions[k2] = proto.ProduceRespPartition{
    99  				ID:  partition.ID,
   100  				Err: err,
   101  			}
   102  		}
   103  	}
   104  
   105  	b, err := resp.Bytes(req.Version)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	return &ResponseMessage{
   111  		response: resp,
   112  		rawMsg:   b,
   113  	}, nil
   114  }
   115  
   116  func createFetchResponse(req *proto.FetchReq, err error) (*ResponseMessage, error) {
   117  	if req == nil {
   118  		return nil, fmt.Errorf("request is nil")
   119  	}
   120  
   121  	resp := &proto.FetchResp{
   122  		CorrelationID: req.CorrelationID,
   123  		Topics:        make([]proto.FetchRespTopic, len(req.Topics)),
   124  	}
   125  
   126  	for k, topic := range req.Topics {
   127  		resp.Topics[k] = proto.FetchRespTopic{
   128  			Name:       topic.Name,
   129  			Partitions: make([]proto.FetchRespPartition, len(topic.Partitions)),
   130  		}
   131  
   132  		for k2, partition := range topic.Partitions {
   133  			resp.Topics[k].Partitions[k2] = proto.FetchRespPartition{
   134  				ID:                  partition.ID,
   135  				Err:                 err,
   136  				AbortedTransactions: nil, // nullable
   137  			}
   138  		}
   139  	}
   140  
   141  	b, err := resp.Bytes(req.Version)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	return &ResponseMessage{
   147  		response: resp,
   148  		rawMsg:   b,
   149  	}, nil
   150  }
   151  
   152  func createOffsetResponse(req *proto.OffsetReq, err error) (*ResponseMessage, error) {
   153  	if req == nil {
   154  		return nil, fmt.Errorf("request is nil")
   155  	}
   156  
   157  	resp := &proto.OffsetResp{
   158  		CorrelationID: req.CorrelationID,
   159  		Topics:        make([]proto.OffsetRespTopic, len(req.Topics)),
   160  	}
   161  
   162  	for k, topic := range req.Topics {
   163  		resp.Topics[k] = proto.OffsetRespTopic{
   164  			Name:       topic.Name,
   165  			Partitions: make([]proto.OffsetRespPartition, len(topic.Partitions)),
   166  		}
   167  
   168  		for k2, partition := range topic.Partitions {
   169  			resp.Topics[k].Partitions[k2] = proto.OffsetRespPartition{
   170  				ID:      partition.ID,
   171  				Err:     err,
   172  				Offsets: make([]int64, 0), // Not nullable, so must never be nil.
   173  			}
   174  		}
   175  	}
   176  
   177  	b, err := resp.Bytes(req.Version)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  
   182  	return &ResponseMessage{
   183  		response: resp,
   184  		rawMsg:   b,
   185  	}, nil
   186  }
   187  
   188  func createMetadataResponse(req *proto.MetadataReq, err error) (*ResponseMessage, error) {
   189  	if req == nil {
   190  		return nil, fmt.Errorf("request is nil")
   191  	}
   192  
   193  	var topics []proto.MetadataRespTopic
   194  	if req.Topics != nil {
   195  		topics = make([]proto.MetadataRespTopic, len(req.Topics))
   196  	}
   197  	resp := &proto.MetadataResp{
   198  		CorrelationID: req.CorrelationID,
   199  		Brokers:       make([]proto.MetadataRespBroker, 0), // Not nullable, so must never be nil.
   200  		Topics:        topics,
   201  	}
   202  
   203  	for k, topic := range req.Topics {
   204  		resp.Topics[k] = proto.MetadataRespTopic{
   205  			Name:       topic,
   206  			Err:        err,
   207  			Partitions: make([]proto.MetadataRespPartition, 0), // Not nullable, so must never be nil.
   208  		}
   209  	}
   210  
   211  	b, err := resp.Bytes(req.Version)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	return &ResponseMessage{
   217  		response: resp,
   218  		rawMsg:   b,
   219  	}, nil
   220  }
   221  
   222  func createConsumerMetadataResponse(req *proto.ConsumerMetadataReq, err error) (*ResponseMessage, error) {
   223  	if req == nil {
   224  		return nil, fmt.Errorf("request is nil")
   225  	}
   226  
   227  	resp := &proto.ConsumerMetadataResp{
   228  		CorrelationID: req.CorrelationID,
   229  		Err:           err,
   230  	}
   231  
   232  	b, err := resp.Bytes(req.Version)
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  
   237  	return &ResponseMessage{
   238  		response: resp,
   239  		rawMsg:   b,
   240  	}, nil
   241  }
   242  
   243  func createOffsetCommitResponse(req *proto.OffsetCommitReq, err error) (*ResponseMessage, error) {
   244  	if req == nil {
   245  		return nil, fmt.Errorf("request is nil")
   246  	}
   247  
   248  	resp := &proto.OffsetCommitResp{
   249  		CorrelationID: req.CorrelationID,
   250  		Topics:        make([]proto.OffsetCommitRespTopic, len(req.Topics)),
   251  	}
   252  
   253  	for k, topic := range req.Topics {
   254  		resp.Topics[k] = proto.OffsetCommitRespTopic{
   255  			Name:       topic.Name,
   256  			Partitions: make([]proto.OffsetCommitRespPartition, len(topic.Partitions)),
   257  		}
   258  
   259  		for k2, partition := range topic.Partitions {
   260  			resp.Topics[k].Partitions[k2] = proto.OffsetCommitRespPartition{
   261  				ID:  partition.ID,
   262  				Err: err,
   263  			}
   264  		}
   265  	}
   266  
   267  	b, err := resp.Bytes(req.Version)
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  
   272  	return &ResponseMessage{
   273  		response: resp,
   274  		rawMsg:   b,
   275  	}, nil
   276  }
   277  
   278  func createOffsetFetchResponse(req *proto.OffsetFetchReq, err error) (*ResponseMessage, error) {
   279  	if req == nil {
   280  		return nil, fmt.Errorf("request is nil")
   281  	}
   282  
   283  	var topics []proto.OffsetFetchRespTopic
   284  	if req.Topics != nil {
   285  		topics = make([]proto.OffsetFetchRespTopic, len(req.Topics))
   286  	}
   287  	resp := &proto.OffsetFetchResp{
   288  		CorrelationID: req.CorrelationID,
   289  		Topics:        topics,
   290  	}
   291  
   292  	for k, topic := range req.Topics {
   293  		resp.Topics[k] = proto.OffsetFetchRespTopic{
   294  			Name:       topic.Name,
   295  			Partitions: make([]proto.OffsetFetchRespPartition, len(topic.Partitions)),
   296  		}
   297  
   298  		for k2, partition := range topic.Partitions {
   299  			resp.Topics[k].Partitions[k2] = proto.OffsetFetchRespPartition{
   300  				ID:  partition,
   301  				Err: err,
   302  			}
   303  		}
   304  	}
   305  
   306  	b, err := resp.Bytes(req.Version)
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	return &ResponseMessage{
   312  		response: resp,
   313  		rawMsg:   b,
   314  	}, nil
   315  }