...

Source file src/github.com/googleapis/gax-go/v2/header.go

Documentation: github.com/googleapis/gax-go/v2

     1  // Copyright 2018, Google Inc.
     2  // All rights reserved.
     3  //
     4  // Redistribution and use in source and binary forms, with or without
     5  // modification, are permitted provided that the following conditions are
     6  // met:
     7  //
     8  //     * Redistributions of source code must retain the above copyright
     9  // notice, this list of conditions and the following disclaimer.
    10  //     * Redistributions in binary form must reproduce the above
    11  // copyright notice, this list of conditions and the following disclaimer
    12  // in the documentation and/or other materials provided with the
    13  // distribution.
    14  //     * Neither the name of Google Inc. nor the names of its
    15  // contributors may be used to endorse or promote products derived from
    16  // this software without specific prior written permission.
    17  //
    18  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    19  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    20  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    21  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    22  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    23  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    24  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    25  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    26  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    27  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    28  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    29  
    30  package gax
    31  
    32  import (
    33  	"bytes"
    34  	"context"
    35  	"fmt"
    36  	"net/http"
    37  	"runtime"
    38  	"strings"
    39  	"unicode"
    40  
    41  	"github.com/googleapis/gax-go/v2/callctx"
    42  	"google.golang.org/grpc/metadata"
    43  )
    44  
    45  var (
    46  	// GoVersion is a header-safe representation of the current runtime
    47  	// environment's Go version. This is for GAX consumers that need to
    48  	// report the Go runtime version in API calls.
    49  	GoVersion string
    50  	// version is a package internal global variable for testing purposes.
    51  	version = runtime.Version
    52  )
    53  
    54  // versionUnknown is only used when the runtime version cannot be determined.
    55  const versionUnknown = "UNKNOWN"
    56  
    57  func init() {
    58  	GoVersion = goVersion()
    59  }
    60  
    61  // goVersion returns a Go runtime version derived from the runtime environment
    62  // that is modified to be suitable for reporting in a header, meaning it has no
    63  // whitespace. If it is unable to determine the Go runtime version, it returns
    64  // versionUnknown.
    65  func goVersion() string {
    66  	const develPrefix = "devel +"
    67  
    68  	s := version()
    69  	if strings.HasPrefix(s, develPrefix) {
    70  		s = s[len(develPrefix):]
    71  		if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
    72  			s = s[:p]
    73  		}
    74  		return s
    75  	} else if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
    76  		s = s[:p]
    77  	}
    78  
    79  	notSemverRune := func(r rune) bool {
    80  		return !strings.ContainsRune("0123456789.", r)
    81  	}
    82  
    83  	if strings.HasPrefix(s, "go1") {
    84  		s = s[2:]
    85  		var prerelease string
    86  		if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
    87  			s, prerelease = s[:p], s[p:]
    88  		}
    89  		if strings.HasSuffix(s, ".") {
    90  			s += "0"
    91  		} else if strings.Count(s, ".") < 2 {
    92  			s += ".0"
    93  		}
    94  		if prerelease != "" {
    95  			// Some release candidates already have a dash in them.
    96  			if !strings.HasPrefix(prerelease, "-") {
    97  				prerelease = "-" + prerelease
    98  			}
    99  			s += prerelease
   100  		}
   101  		return s
   102  	}
   103  	return "UNKNOWN"
   104  }
   105  
   106  // XGoogHeader is for use by the Google Cloud Libraries only. See package
   107  // [github.com/googleapis/gax-go/v2/callctx] for help setting/retrieving
   108  // request/response headers.
   109  //
   110  // XGoogHeader formats key-value pairs.
   111  // The resulting string is suitable for x-goog-api-client header.
   112  func XGoogHeader(keyval ...string) string {
   113  	if len(keyval) == 0 {
   114  		return ""
   115  	}
   116  	if len(keyval)%2 != 0 {
   117  		panic("gax.Header: odd argument count")
   118  	}
   119  	var buf bytes.Buffer
   120  	for i := 0; i < len(keyval); i += 2 {
   121  		buf.WriteByte(' ')
   122  		buf.WriteString(keyval[i])
   123  		buf.WriteByte('/')
   124  		buf.WriteString(keyval[i+1])
   125  	}
   126  	return buf.String()[1:]
   127  }
   128  
   129  // InsertMetadataIntoOutgoingContext is for use by the Google Cloud Libraries
   130  // only. See package [github.com/googleapis/gax-go/v2/callctx] for help
   131  // setting/retrieving request/response headers.
   132  //
   133  // InsertMetadataIntoOutgoingContext returns a new context that merges the
   134  // provided keyvals metadata pairs with any existing metadata/headers in the
   135  // provided context. keyvals should have a corresponding value for every key
   136  // provided. If there is an odd number of keyvals this method will panic.
   137  // Existing values for keys will not be overwritten, instead provided values
   138  // will be appended to the list of existing values.
   139  func InsertMetadataIntoOutgoingContext(ctx context.Context, keyvals ...string) context.Context {
   140  	return metadata.NewOutgoingContext(ctx, insertMetadata(ctx, keyvals...))
   141  }
   142  
   143  // BuildHeaders is for use by the Google Cloud Libraries only. See package
   144  // [github.com/googleapis/gax-go/v2/callctx] for help setting/retrieving
   145  // request/response headers.
   146  //
   147  // BuildHeaders returns a new http.Header that merges the provided
   148  // keyvals header pairs with any existing metadata/headers in the provided
   149  // context. keyvals should have a corresponding value for every key provided.
   150  // If there is an odd number of keyvals this method will panic.
   151  // Existing values for keys will not be overwritten, instead provided values
   152  // will be appended to the list of existing values.
   153  func BuildHeaders(ctx context.Context, keyvals ...string) http.Header {
   154  	return http.Header(insertMetadata(ctx, keyvals...))
   155  }
   156  
   157  func insertMetadata(ctx context.Context, keyvals ...string) metadata.MD {
   158  	if len(keyvals)%2 != 0 {
   159  		panic(fmt.Sprintf("gax: an even number of key value pairs must be provided, got %d", len(keyvals)))
   160  	}
   161  	out, ok := metadata.FromOutgoingContext(ctx)
   162  	if !ok {
   163  		out = metadata.MD(make(map[string][]string))
   164  	}
   165  	headers := callctx.HeadersFromContext(ctx)
   166  	for k, v := range headers {
   167  		out[k] = append(out[k], v...)
   168  	}
   169  	for i := 0; i < len(keyvals); i = i + 2 {
   170  		out[keyvals[i]] = append(out[keyvals[i]], keyvals[i+1])
   171  	}
   172  	return out
   173  }
   174  

View as plain text