sigs.k8s.io/gateway-api@v1.0.0/conformance/utils/echo/parse.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package echo 18 19 import ( 20 "fmt" 21 "net/http" 22 "regexp" 23 "sort" 24 "strings" 25 ) 26 27 // Field is a list of fields returned in responses from the Echo server. 28 type Field string 29 30 const ( 31 RequestIDField Field = "X-Request-Id" 32 ServiceVersionField Field = "ServiceVersion" 33 ServicePortField Field = "ServicePort" 34 StatusCodeField Field = "StatusCode" 35 URLField Field = "URL" 36 ForwarderURLField Field = "Url" 37 ForwarderMessageField Field = "Echo" 38 ForwarderHeaderField Field = "Header" 39 HostField Field = "Host" 40 HostnameField Field = "Hostname" 41 MethodField Field = "Method" 42 ProtocolField Field = "Proto" 43 AlpnField Field = "Alpn" 44 RequestHeaderField Field = "RequestHeader" 45 ResponseHeaderField Field = "ResponseHeader" 46 ClusterField Field = "Cluster" 47 IPField Field = "IP" // The Requester’s IP Address. 48 LatencyField Field = "Latency" 49 ActiveRequestsField Field = "ActiveRequests" 50 DNSProtocolField Field = "Protocol" 51 DNSQueryField Field = "Query" 52 DNSServerField Field = "DnsServer" 53 CipherField Field = "Cipher" 54 TLSVersionField Field = "Version" 55 TLSServerName Field = "ServerName" 56 ) 57 58 var ( 59 requestIDFieldRegex = regexp.MustCompile("(?i)" + string(RequestIDField) + "=(.*)") 60 serviceVersionFieldRegex = regexp.MustCompile(string(ServiceVersionField) + "=(.*)") 61 servicePortFieldRegex = regexp.MustCompile(string(ServicePortField) + "=(.*)") 62 statusCodeFieldRegex = regexp.MustCompile(string(StatusCodeField) + "=(.*)") 63 hostFieldRegex = regexp.MustCompile(string(HostField) + "=(.*)") 64 hostnameFieldRegex = regexp.MustCompile(string(HostnameField) + "=(.*)") 65 requestHeaderFieldRegex = regexp.MustCompile(string(RequestHeaderField) + "=(.*)") 66 responseHeaderFieldRegex = regexp.MustCompile(string(ResponseHeaderField) + "=(.*)") 67 URLFieldRegex = regexp.MustCompile(string(URLField) + "=(.*)") 68 ClusterFieldRegex = regexp.MustCompile(string(ClusterField) + "=(.*)") 69 IPFieldRegex = regexp.MustCompile(string(IPField) + "=(.*)") 70 methodFieldRegex = regexp.MustCompile(string(MethodField) + "=(.*)") 71 protocolFieldRegex = regexp.MustCompile(string(ProtocolField) + "=(.*)") 72 alpnFieldRegex = regexp.MustCompile(string(AlpnField) + "=(.*)") 73 ) 74 75 // Response represents a response to a single echo request. 76 type Response struct { 77 // RequestURL is the requested URL. This differs from URL, which is the just the path. 78 // For example, RequestURL=http://foo/bar, URL=/bar 79 RequestURL string 80 // Method used (for HTTP). 81 Method string 82 // Protocol used for the request. 83 Protocol string 84 // Alpn value (for HTTP). 85 Alpn string 86 // RawContent is the original unparsed content for this response 87 RawContent string 88 // ID is a unique identifier of the resource in the response 89 ID string 90 // URL is the url the request is sent to 91 URL string 92 // Version is the version of the resource in the response 93 Version string 94 // Port is the port of the resource in the response 95 Port string 96 // Code is the response code 97 Code string 98 // Host is the host called by the request 99 Host string 100 // Hostname is the host that responded to the request 101 Hostname string 102 // The cluster where the server is deployed. 103 Cluster string 104 // IP is the requester's ip address 105 IP string 106 // rawBody gives a map of all key/values in the body of the response. 107 rawBody map[string]string 108 RequestHeaders http.Header 109 ResponseHeaders http.Header 110 } 111 112 func ParseResponse(output string) Response { 113 out := Response{ 114 RawContent: output, 115 RequestHeaders: make(http.Header), 116 ResponseHeaders: make(http.Header), 117 } 118 119 match := requestIDFieldRegex.FindStringSubmatch(output) 120 if match != nil { 121 out.ID = match[1] 122 } 123 124 match = methodFieldRegex.FindStringSubmatch(output) 125 if match != nil { 126 out.Method = match[1] 127 } 128 129 match = protocolFieldRegex.FindStringSubmatch(output) 130 if match != nil { 131 out.Protocol = match[1] 132 } 133 134 match = alpnFieldRegex.FindStringSubmatch(output) 135 if match != nil { 136 out.Alpn = match[1] 137 } 138 139 match = serviceVersionFieldRegex.FindStringSubmatch(output) 140 if match != nil { 141 out.Version = match[1] 142 } 143 144 match = servicePortFieldRegex.FindStringSubmatch(output) 145 if match != nil { 146 out.Port = match[1] 147 } 148 149 match = statusCodeFieldRegex.FindStringSubmatch(output) 150 if match != nil { 151 out.Code = match[1] 152 } 153 154 match = hostFieldRegex.FindStringSubmatch(output) 155 if match != nil { 156 out.Host = match[1] 157 } 158 159 match = hostnameFieldRegex.FindStringSubmatch(output) 160 if match != nil { 161 out.Hostname = match[1] 162 } 163 164 match = URLFieldRegex.FindStringSubmatch(output) 165 if match != nil { 166 out.URL = match[1] 167 } 168 169 match = ClusterFieldRegex.FindStringSubmatch(output) 170 if match != nil { 171 out.Cluster = match[1] 172 } 173 174 match = IPFieldRegex.FindStringSubmatch(output) 175 if match != nil { 176 out.IP = match[1] 177 } 178 179 out.rawBody = map[string]string{} 180 181 matches := requestHeaderFieldRegex.FindAllStringSubmatch(output, -1) 182 for _, kv := range matches { 183 sl := strings.SplitN(kv[1], ":", 2) 184 if len(sl) != 2 { 185 continue 186 } 187 out.RequestHeaders.Set(sl[0], sl[1]) 188 } 189 190 matches = responseHeaderFieldRegex.FindAllStringSubmatch(output, -1) 191 for _, kv := range matches { 192 sl := strings.SplitN(kv[1], ":", 2) 193 if len(sl) != 2 { 194 continue 195 } 196 out.ResponseHeaders.Set(sl[0], sl[1]) 197 } 198 199 for _, l := range strings.Split(output, "\n") { 200 prefixSplit := strings.Split(l, "body] ") 201 if len(prefixSplit) != 2 { 202 continue 203 } 204 kv := strings.SplitN(prefixSplit[1], "=", 2) 205 if len(kv) != 2 { 206 continue 207 } 208 out.rawBody[kv[0]] = kv[1] 209 } 210 211 return out 212 } 213 214 // HeaderType is a helper enum for retrieving Headers from a Response. 215 type HeaderType string 216 217 const ( 218 RequestHeader HeaderType = "request" 219 ResponseHeader HeaderType = "response" 220 ) 221 222 // GetHeaders returns the appropriate headers for the given type. 223 func (r Response) GetHeaders(hType HeaderType) http.Header { 224 switch hType { 225 case RequestHeader: 226 return r.RequestHeaders 227 case ResponseHeader: 228 return r.ResponseHeaders 229 default: 230 panic("invalid HeaderType enum: " + hType) 231 } 232 } 233 234 // Body returns the lines of the response body, in order 235 func (r Response) Body() []string { 236 type keyValue struct { 237 k, v string 238 } 239 var keyValues []keyValue 240 // rawBody is in random order, so get the order back via sorting. 241 for k, v := range r.rawBody { 242 keyValues = append(keyValues, keyValue{k, v}) 243 } 244 sort.Slice(keyValues, func(i, j int) bool { 245 return keyValues[i].k < keyValues[j].k 246 }) 247 var resp []string 248 for _, kv := range keyValues { 249 resp = append(resp, kv.v) 250 } 251 return resp 252 } 253 254 func (r Response) String() string { 255 out := "" 256 out += fmt.Sprintf("RawContent: %s\n", r.RawContent) 257 out += fmt.Sprintf("ID: %s\n", r.ID) 258 out += fmt.Sprintf("Method: %s\n", r.Method) 259 out += fmt.Sprintf("Protocol: %s\n", r.Protocol) 260 out += fmt.Sprintf("Alpn: %s\n", r.Alpn) 261 out += fmt.Sprintf("URL: %s\n", r.URL) 262 out += fmt.Sprintf("Version: %s\n", r.Version) 263 out += fmt.Sprintf("Port: %s\n", r.Port) 264 out += fmt.Sprintf("Code: %s\n", r.Code) 265 out += fmt.Sprintf("Host: %s\n", r.Host) 266 out += fmt.Sprintf("Hostname: %s\n", r.Hostname) 267 out += fmt.Sprintf("Cluster: %s\n", r.Cluster) 268 out += fmt.Sprintf("IP: %s\n", r.IP) 269 out += fmt.Sprintf("Request Headers: %v\n", r.RequestHeaders) 270 out += fmt.Sprintf("Response Headers: %v\n", r.ResponseHeaders) 271 272 return out 273 }