...

Source file src/sigs.k8s.io/gateway-api/conformance/utils/echo/parse.go

Documentation: sigs.k8s.io/gateway-api/conformance/utils/echo

     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  }
   274  

View as plain text