github.com/matrixorigin/matrixone@v1.2.0/pkg/proxy/util.go (about) 1 // Copyright 2021 - 2023 Matrix Origin 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 proxy 16 17 import ( 18 "crypto/md5" 19 "encoding/hex" 20 "encoding/json" 21 "net" 22 "sort" 23 24 "github.com/matrixorigin/matrixone/pkg/frontend" 25 ) 26 27 // makeOKPacket returns an OK packet 28 func makeOKPacket(l int) []byte { 29 data := make([]byte, l+4) 30 data[4] = 0 31 data[0] = byte(l) 32 data[1] = byte(l >> 8) 33 data[2] = byte(l >> 16) 34 data[3] = 1 35 return data 36 } 37 38 func isCmdQuery(p []byte) bool { 39 if len(p) > 4 && p[4] == byte(cmdQuery) { 40 return true 41 } 42 return false 43 } 44 45 func isCmdInitDB(p []byte) bool { 46 if len(p) > 4 && p[4] == byte(cmdInitDB) { 47 return true 48 } 49 return false 50 } 51 52 func isCmdStmtPrepare(p []byte) bool { 53 if len(p) > 4 && p[4] == byte(cmdStmtPrepare) { 54 return true 55 } 56 return false 57 } 58 59 func isCmdStmtClose(p []byte) bool { 60 if len(p) > 4 && p[4] == byte(cmdStmtClose) { 61 return true 62 } 63 return false 64 } 65 66 // isOKPacket returns true if []byte is a MySQL OK packet. 67 func isOKPacket(p []byte) bool { 68 if len(p) > 4 && p[4] == 0 { 69 return true 70 } 71 return false 72 } 73 74 // isOKPacket returns true if []byte is a MySQL EOF packet. 75 func isEOFPacket(p []byte) bool { 76 if len(p) > 4 && p[4] == 0xFE { 77 return true 78 } 79 return false 80 } 81 82 // isErrPacket returns true if []byte is a MySQL Err packet. 83 func isErrPacket(p []byte) bool { 84 if len(p) > 4 && p[4] == 0xFF { 85 return true 86 } 87 return false 88 } 89 90 // packetToBytes convert Packet to bytes. 91 func packetToBytes(p *frontend.Packet) []byte { 92 if p == nil || len(p.Payload) == 0 { 93 return nil 94 } 95 res := make([]byte, 4, 4+len(p.Payload)) 96 length := len(p.Payload) 97 res[0] = byte(length) 98 res[1] = byte(length >> 8) 99 res[2] = byte(length >> 16) 100 res[3] = byte(p.SequenceID) 101 return append(res, p.Payload...) 102 } 103 104 // bytesToPacket convert bytes to Packet. 105 func bytesToPacket(bs []byte) *frontend.Packet { 106 if len(bs) < 4 { 107 return nil 108 } 109 p := &frontend.Packet{ 110 Length: int32(bs[0]) | int32(bs[1])<<8 | int32(bs[2])<<16, 111 SequenceID: int8(bs[3]), 112 Payload: bs[4:], 113 } 114 return p 115 } 116 117 // getStatement gets a statement from message bytes which is MySQL protocol. 118 func getStatement(msg []byte) string { 119 return string(msg[5:]) 120 } 121 122 // pickTunnels pick N tunnels from the given tunnels. Simply, just 123 // pick the first N tunnels. 124 func pickTunnels(tuns tunnelSet, n int) []*tunnel { 125 if len(tuns) == 0 || n <= 0 { 126 return nil 127 } 128 size := n 129 if len(tuns) < n { 130 size = len(tuns) 131 } 132 ret := make([]*tunnel, 0, size) 133 i := 1 134 for t := range tuns { 135 // if the tunnel is in transfer intent state, we need to put it 136 // into the queue to speed up its transfer, and it does not count 137 // in the 'size'. 138 if t.transferIntent.Load() { 139 ret = append(ret, t) 140 continue 141 } 142 ret = append(ret, t) 143 i++ 144 if i > size { 145 break 146 } 147 } 148 return ret 149 } 150 151 // sortMap sorts a complex map instance. 152 func sortMap(target map[string]any) map[string]any { 153 sorted := sortSimpleMap(target) 154 res := make(map[string]any) 155 for k, v := range sorted { 156 if tv, s := v.(map[string]any); s { 157 res[k] = sortMap(tv) 158 } else if tv, s := v.([]any); s { 159 res[k] = sortSlice(tv) 160 } else { 161 res[k] = v 162 } 163 } 164 return res 165 } 166 167 // sortSlice sorts a slice instance. 168 func sortSlice(target []any) []any { 169 hashArr := make(map[string]any) 170 for _, i := range target { 171 var tmpV any 172 var ha string 173 if ttv, ts := i.(map[string]any); ts { 174 tmpV = sortMap(ttv) 175 ha = rawHash(tmpV) 176 } else if ttv, ts := i.([]any); ts { 177 tmpV = sortSlice(ttv) 178 ha = rawHash(tmpV) 179 } else { 180 tmpV = i 181 ha = tmpV.(string) 182 } 183 hashArr[ha] = tmpV 184 } 185 186 sor := sortSimpleMap(hashArr) 187 sortKeys := getSortKeys(sor) 188 r := make([]any, 0, len(sortKeys)) 189 for _, v := range sortKeys { 190 r = append(r, sor[v]) 191 } 192 return r 193 } 194 195 // sortSimpleMap sort simple map by keys. 196 func sortSimpleMap(target map[string]any) map[string]any { 197 keys := getSortKeys(target) 198 res := make(map[string]any, len(keys)) 199 for _, k := range keys { 200 res[k] = target[k] 201 } 202 return res 203 } 204 205 // getSortKeys returns sorted keys in the map. 206 func getSortKeys(target map[string]any) []string { 207 keys := make([]string, 0, len(target)) 208 for k := range target { 209 keys = append(keys, k) 210 } 211 sort.Strings(keys) 212 return keys 213 } 214 215 // rawHash returns a string value as the hash result. 216 func rawHash(t any) string { 217 sortBytes, err := json.Marshal(t) 218 if err != nil { 219 return "" 220 } 221 hash := md5.Sum(sortBytes) 222 return hex.EncodeToString(hash[:]) 223 } 224 225 // containIP returns if the list of net.IPNet contains the IP address. 226 func containIP(ipNetList []*net.IPNet, ip net.IP) bool { 227 for _, ipNet := range ipNetList { 228 if ipNet.Contains(ip) { 229 return true 230 } 231 } 232 return false 233 }