Go API Documentation

github.com/TykTechnologies/tyk/internal/httputil

No package summary is available.

Package

Files: 7. Third party imports: 1. Imports from organisation: 0. Tests: 0. Benchmarks: 0.

Constants

const (
	headerContentType	= "Content-Type"
	headerUpgrade		= "Upgrade"
	headerConnection	= "Connection"
)

Vars

CORSHeaders is a list of CORS headers.

var CORSHeaders = []string{
	"Access-Control-Allow-Origin",
	"Access-Control-Expose-Headers",
	"Access-Control-Max-Age",
	"Access-Control-Allow-Credentials",
	"Access-Control-Allow-Methods",
	"Access-Control-Allow-Headers",
}
var DumpRequest = httputil.DumpRequest
var DumpResponse = httputil.DumpResponse

apiLandIDsRegex matches mux-style parameters like {id}.

var apiLangIDsRegex = regexp.MustCompile(`{([^}]+)}`)

routeCache holds the raw routes as they are mapped from mux parameters to regular expressions. e.g. /foo/{id} becomes ^/foo/([^/]+)$ or similar.

var pathRegexpCache = maps.NewStringMap()

Types

ConnectionWatcher

ConnectionWatcher counts http server connections.

Field name Field type Comment
n

int64

No comment on field.
type ConnectionWatcher struct {
	n int64
}

Functions

func AuthHeader

AuthHeader will take username and password and return "Basic " + base64 encoded username:password for use in an Authorization header.

func AuthHeader(username, password string) string {
	toEncode := strings.Join([]string{username, password}, ":")
	encodedPass := base64.StdEncoding.EncodeToString([]byte(toEncode))
	return fmt.Sprintf("Basic %s", encodedPass)
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: base64.StdEncoding, fmt.Sprintf, strings.Join.

func EntityTooLarge

EntityTooLarge responds with HTTP 413 Request Entity Too Large. The function is used for a response when blocking requests by size.

func EntityTooLarge(w http.ResponseWriter, _ *http.Request) {
	status := http.StatusRequestEntityTooLarge
	http.Error(w, http.StatusText(status), status)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: http.Error, http.StatusRequestEntityTooLarge, http.StatusText.

func HasTransferEncoding

HasTransferEncoding returns true if a transfer encoding header is present.

func HasTransferEncoding(req *http.Request) bool {
	return TransferEncoding(req) != ""
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func InternalServerError

InternalServerError responds with HTTP 503 Internal Server Error.

func InternalServerError(w http.ResponseWriter, _ *http.Request) {
	status := http.StatusInternalServerError
	http.Error(w, http.StatusText(status), status)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: http.Error, http.StatusInternalServerError, http.StatusText.

func IsGrpcStreaming

IsGrpcStreaming returns true if the request designates gRPC streaming.

func IsGrpcStreaming(r *http.Request) bool {
	return r.ContentLength == -1 && r.Header.Get(headerContentType) == "application/grpc"
}

Cognitive complexity: 0, Cyclomatic complexity: 2

func IsMuxTemplate

IsMuxTemplate determines if a pattern is a mux template by counting the number of opening and closing braces.

func IsMuxTemplate(pattern string) bool {
	openBraces := strings.Count(pattern, "{")
	closeBraces := strings.Count(pattern, "}")
	return openBraces > 0 && openBraces == closeBraces
}

Cognitive complexity: 0, Cyclomatic complexity: 2

Uses: strings.Count.

func IsSseStreamingResponse

IsSseStreamingResponse returns true if the response designates SSE streaming.

func IsSseStreamingResponse(r *http.Response) bool {
	return r.Header.Get(headerContentType) == "text/event-stream"
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func IsStreamingRequest

IsStreamingRequest returns true if the request designates streaming (gRPC or WebSocket).

func IsStreamingRequest(r *http.Request) bool {
	_, upgrade := IsUpgrade(r)
	return upgrade || IsGrpcStreaming(r)
}

Cognitive complexity: 0, Cyclomatic complexity: 2

func IsStreamingResponse

IsStreamingResponse returns true if the response designates streaming (SSE).

func IsStreamingResponse(r *http.Response) bool {
	return IsSseStreamingResponse(r)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func IsUpgrade

IsUpgrade checks if the request is an upgrade request and returns the upgrade type.

func IsUpgrade(req *http.Request) (string, bool) {
	connection := strings.ToLower(strings.TrimSpace(req.Header.Get(headerConnection)))
	if connection != "upgrade" {
		return "", false
	}

	upgrade := strings.ToLower(strings.TrimSpace(req.Header.Get(headerUpgrade)))
	if upgrade != "" {
		return upgrade, true
	}

	return "", false
}

Cognitive complexity: 4, Cyclomatic complexity: 3

Uses: strings.ToLower, strings.TrimSpace.

func LengthRequired

LengthRequired responds with HTTP 411 Length Required. The function is used in places where Content-Length is required.

func LengthRequired(w http.ResponseWriter, _ *http.Request) {
	status := http.StatusLengthRequired
	http.Error(w, http.StatusText(status), status)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: http.Error, http.StatusLengthRequired, http.StatusText.

func MatchPath

MatchPath matches regexp pattern with request endpoint.

func MatchPath(pattern string, endpoint string) (bool, error) {
	if strings.Trim(pattern, "^$") == "" || endpoint == "" {
		return false, nil
	}
	if pattern == endpoint || pattern == "^"+endpoint+"$" {
		return true, nil
	}

	asRegex, err := regexp.Compile(pattern)
	if err != nil {
		return false, err
	}

	return asRegex.MatchString(endpoint), nil
}

Cognitive complexity: 6, Cyclomatic complexity: 6

Uses: regexp.Compile, strings.Trim.

func MatchPaths

MatchPaths matches regexp pattern with multiple request URLs endpoint paths. It will return true if any of them is correctly matched, with no error. If no matches occur, any errors will be retured joined with errors.Join.

func MatchPaths(pattern string, endpoints []string) (bool, error) {
	var errs []error

	for _, endpoint := range endpoints {
		match, err := MatchPath(pattern, endpoint)
		if err != nil {
			errs = append(errs, err)
			continue
		}
		if match {
			return true, nil
		}
	}

	return false, errors.Join(errs...)
}

Cognitive complexity: 7, Cyclomatic complexity: 4

Uses: errors.Join.

func NewConnectionWatcher

NewConnectionWatcher returns a new *ConnectionWatcher.

func NewConnectionWatcher() *ConnectionWatcher {
	return &ConnectionWatcher{}
}

Cognitive complexity: 1, Cyclomatic complexity: 1

func PreparePathRegexp

PreparePathRexep will replace mux-style parameters in input with a compatible regular expression. Parameters like {id} would be replaced to ([^/]+). If the input pattern provides a starting or ending delimiters (^ or $), the pattern is returned. If prefix is true, and pattern starts with /, the returned pattern prefixes a ^ to the regex. No other prefix matches are possible so only / to ^/ conversion is considered. If suffix is true, the returned pattern suffixes a $ to the regex. If both prefix and suffixes are achieved, an explicit match is made.

func PreparePathRegexp(pattern string, prefix bool, suffix bool) string {
	// Construct cache key from pattern and flags
	key := fmt.Sprintf("%s:%v:%v", pattern, prefix, suffix)
	val, ok := pathRegexpCache.Get(key)
	if ok {
		return val
	}

	// Replace mux named parameters with regex path match.
	if IsMuxTemplate(pattern) {
		pattern = apiLangIDsRegex.ReplaceAllString(pattern, `([^/]+)`)
	}

	// Replace mux wildcard path with a `.*` (match 0 or more characters)
	if strings.Contains(pattern, "/*") {
		pattern = strings.ReplaceAll(pattern, "/*/", "/[^/]+/")
		pattern = strings.ReplaceAll(pattern, "/*", "/.*")
	}

	// Pattern `/users` becomes `^/users`.
	if prefix && strings.HasPrefix(pattern, "/") {
		pattern = "^" + pattern
	}

	// Append $ if necessary to enforce suffix matching.
	// Pattern `/users` becomes `/users$`.
	// Pattern `^/users` becomes `^/users$`.
	if suffix && !strings.HasSuffix(pattern, "$") {
		pattern = pattern + "$"
	}

	// Save cache for following invocations.
	pathRegexpCache.Set(key, pattern)

	return pattern
}

Cognitive complexity: 9, Cyclomatic complexity: 8

Uses: fmt.Sprintf, strings.Contains, strings.HasPrefix, strings.HasSuffix, strings.ReplaceAll.

func RemoveResponseTransferEncoding

RemoveResponseTransferEncoding will remove a transfer encoding hint from the response.

func RemoveResponseTransferEncoding(response *http.Response, victim string) {
	for i, value := range response.TransferEncoding {
		if value == victim {
			response.TransferEncoding = append(response.TransferEncoding[:i], response.TransferEncoding[i+1:]...)
		}
	}
}

Cognitive complexity: 5, Cyclomatic complexity: 3

func StripListenPath

StripListenPath will strip the listenPath from the passed urlPath. If the listenPath contains mux variables, it will trim away the matching pattern with a regular expression that mux provides.

func StripListenPath(listenPath, urlPath string) (res string) {
	defer func() {
		if !strings.HasPrefix(res, "/") {
			res = "/" + res
		}
	}()

	res = urlPath

	// early return on the simple case
	if strings.HasPrefix(urlPath, listenPath) {
		res = strings.TrimPrefix(res, listenPath)
		return res
	}

	if !IsMuxTemplate(listenPath) {
		return res
	}

	tmp := new(mux.Route).PathPrefix(listenPath)
	s, err := tmp.GetPathRegexp()
	if err != nil {
		return res
	}

	reg := regexp.MustCompile(s)
	return reg.ReplaceAllString(res, "")
}

Cognitive complexity: 9, Cyclomatic complexity: 5

Uses: mux.Route, regexp.MustCompile, strings.HasPrefix, strings.TrimPrefix.

func TransferEncoding

TransferEncoding gets the header value from the request.

func TransferEncoding(req *http.Request) string {
	for _, val := range req.TransferEncoding {
		if val != "" {
			return val
		}
	}
	return ""
}

Cognitive complexity: 5, Cyclomatic complexity: 3

func ValidatePath

ValidatePath validates if the path is valid. Returns an error.

func ValidatePath(in string) error {
	router := mux.NewRouter()
	route := router.PathPrefix(in)
	return route.GetError()
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: mux.NewRouter.

func (*ConnectionWatcher) Add

Add adds c to the number of active connections.

func (cw *ConnectionWatcher) Add(c int64) {
	atomic.AddInt64(&cw.n, c)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: atomic.AddInt64.

func (*ConnectionWatcher) Count

Count returns the number of connections at the time the call.

func (cw *ConnectionWatcher) Count() int {
	return int(atomic.LoadInt64(&cw.n))
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: atomic.LoadInt64.

func (*ConnectionWatcher) OnStateChange

OnStateChange records open connections in response to connection state changes. Set net/http Server.ConnState to this method as value.

func (cw *ConnectionWatcher) OnStateChange(_ net.Conn, state http.ConnState) {
	switch state {
	case http.StateNew:
		cw.Add(1)
	case http.StateHijacked, http.StateClosed:
		cw.Add(-1)
	}
}

Cognitive complexity: 4, Cyclomatic complexity: 4

Uses: http.StateClosed, http.StateHijacked, http.StateNew.

Tests

Files: 1. Third party imports: 1. Imports from organisation: 0. Tests: 2. Benchmarks: 0.

Test functions

TestRemoveResponseTransferEncoding

References: assert.Equal, http.Response, testing.T.

TestRequestUtilities

References: assert.Equal, http.StatusInternalServerError, http.StatusLengthRequired, http.StatusRequestEntityTooLarge, httptest.NewRecorder.