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 }