github.com/QuangHoangHao/kafka-go@v0.4.36/protocol/response.go (about) 1 package protocol 2 3 import ( 4 "crypto/tls" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "io" 9 ) 10 11 func ReadResponse(r io.Reader, apiKey ApiKey, apiVersion int16) (correlationID int32, msg Message, err error) { 12 if i := int(apiKey); i < 0 || i >= len(apiTypes) { 13 err = fmt.Errorf("unsupported api key: %d", i) 14 return 15 } 16 17 t := &apiTypes[apiKey] 18 if t == nil { 19 err = fmt.Errorf("unsupported api: %s", apiNames[apiKey]) 20 return 21 } 22 23 minVersion := t.minVersion() 24 maxVersion := t.maxVersion() 25 26 if apiVersion < minVersion || apiVersion > maxVersion { 27 err = fmt.Errorf("unsupported %s version: v%d not in range v%d-v%d", apiKey, apiVersion, minVersion, maxVersion) 28 return 29 } 30 31 d := &decoder{reader: r, remain: 4} 32 size := d.readInt32() 33 34 if err = d.err; err != nil { 35 err = dontExpectEOF(err) 36 return 37 } 38 39 d.remain = int(size) 40 correlationID = d.readInt32() 41 if err = d.err; err != nil { 42 if errors.Is(err, io.ErrUnexpectedEOF) { 43 // If a Writer/Reader is configured without TLS and connects 44 // to a broker expecting TLS the only message we return to the 45 // caller is io.ErrUnexpetedEOF which is opaque. This section 46 // tries to determine if that's what has happened. 47 // We first deconstruct the initial 4 bytes of the message 48 // from the size which was read earlier. 49 // Next, we examine those bytes to see if they looks like a TLS 50 // error message. If they do we wrap the io.ErrUnexpectedEOF 51 // with some context. 52 if looksLikeUnexpectedTLS(size) { 53 err = fmt.Errorf("%w: broker appears to be expecting TLS", io.ErrUnexpectedEOF) 54 } 55 return 56 } 57 err = dontExpectEOF(err) 58 return 59 } 60 61 res := &t.responses[apiVersion-minVersion] 62 63 if res.flexible { 64 // In the flexible case, there's a tag buffer at the end of the response header 65 taggedCount := int(d.readUnsignedVarInt()) 66 for i := 0; i < taggedCount; i++ { 67 d.readUnsignedVarInt() // tagID 68 size := d.readUnsignedVarInt() 69 70 // Just throw away the values for now 71 d.read(int(size)) 72 } 73 } 74 75 msg = res.new() 76 res.decode(d, valueOf(msg)) 77 d.discardAll() 78 79 if err = d.err; err != nil { 80 err = dontExpectEOF(err) 81 } 82 83 return 84 } 85 86 func WriteResponse(w io.Writer, apiVersion int16, correlationID int32, msg Message) error { 87 apiKey := msg.ApiKey() 88 89 if i := int(apiKey); i < 0 || i >= len(apiTypes) { 90 return fmt.Errorf("unsupported api key: %d", i) 91 } 92 93 t := &apiTypes[apiKey] 94 if t == nil { 95 return fmt.Errorf("unsupported api: %s", apiNames[apiKey]) 96 } 97 98 minVersion := t.minVersion() 99 maxVersion := t.maxVersion() 100 101 if apiVersion < minVersion || apiVersion > maxVersion { 102 return fmt.Errorf("unsupported %s version: v%d not in range v%d-v%d", apiKey, apiVersion, minVersion, maxVersion) 103 } 104 105 r := &t.responses[apiVersion-minVersion] 106 v := valueOf(msg) 107 b := newPageBuffer() 108 defer b.unref() 109 110 e := &encoder{writer: b} 111 e.writeInt32(0) // placeholder for the response size 112 e.writeInt32(correlationID) 113 if r.flexible { 114 // Flexible messages use extra space for a tag buffer, 115 // which begins with a size value. Since we're not writing any fields into the 116 // latter, we can just write zero for now. 117 // 118 // See 119 // https://cwiki.apache.org/confluence/display/KAFKA/KIP-482%3A+The+Kafka+Protocol+should+Support+Optional+Tagged+Fields 120 // for details. 121 e.writeUnsignedVarInt(0) 122 } 123 r.encode(e, v) 124 err := e.err 125 126 if err == nil { 127 size := packUint32(uint32(b.Size()) - 4) 128 b.WriteAt(size[:], 0) 129 _, err = b.WriteTo(w) 130 } 131 132 return err 133 } 134 135 const ( 136 tlsAlertByte byte = 0x15 137 ) 138 139 // looksLikeUnexpectedTLS returns true if the size passed in resemble 140 // the TLS alert message that is returned to a client which sends 141 // an invalid ClientHello message. 142 func looksLikeUnexpectedTLS(size int32) bool { 143 var sizeBytes [4]byte 144 binary.BigEndian.PutUint32(sizeBytes[:], uint32(size)) 145 146 if sizeBytes[0] != tlsAlertByte { 147 return false 148 } 149 version := int(sizeBytes[1])<<8 | int(sizeBytes[2]) 150 return version <= tls.VersionTLS13 && version >= tls.VersionTLS10 151 }