middleware_header_comment.go raw

   1  package http
   2  
   3  import (
   4  	"context"
   5  	"fmt"
   6  	"net/http"
   7  
   8  	"github.com/aws/smithy-go/middleware"
   9  )
  10  
  11  // WithHeaderComment instruments a middleware stack to append an HTTP field
  12  // comment to the given header as specified in RFC 9110
  13  // (https://www.rfc-editor.org/rfc/rfc9110#name-comments).
  14  //
  15  // The header is case-insensitive. If the provided header exists when the
  16  // middleware runs, the content will be inserted as-is enclosed in parentheses.
  17  //
  18  // Note that per the HTTP specification, comments are only allowed in fields
  19  // containing "comment" as part of their field value definition, but this API
  20  // will NOT verify whether the provided header is one of them.
  21  //
  22  // WithHeaderComment MAY be applied more than once to a middleware stack and/or
  23  // more than once per header.
  24  func WithHeaderComment(header, content string) func(*middleware.Stack) error {
  25  	return func(s *middleware.Stack) error {
  26  		m, err := getOrAddHeaderComment(s)
  27  		if err != nil {
  28  			return fmt.Errorf("get or add header comment: %v", err)
  29  		}
  30  
  31  		m.values.Add(header, content)
  32  		return nil
  33  	}
  34  }
  35  
  36  type headerCommentMiddleware struct {
  37  	values http.Header // hijack case-insensitive access APIs
  38  }
  39  
  40  func (*headerCommentMiddleware) ID() string {
  41  	return "headerComment"
  42  }
  43  
  44  func (m *headerCommentMiddleware) HandleBuild(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (
  45  	out middleware.BuildOutput, metadata middleware.Metadata, err error,
  46  ) {
  47  	r, ok := in.Request.(*Request)
  48  	if !ok {
  49  		return out, metadata, fmt.Errorf("unknown transport type %T", in.Request)
  50  	}
  51  
  52  	for h, contents := range m.values {
  53  		for _, c := range contents {
  54  			if existing := r.Header.Get(h); existing != "" {
  55  				r.Header.Set(h, fmt.Sprintf("%s (%s)", existing, c))
  56  			}
  57  		}
  58  	}
  59  
  60  	return next.HandleBuild(ctx, in)
  61  }
  62  
  63  func getOrAddHeaderComment(s *middleware.Stack) (*headerCommentMiddleware, error) {
  64  	id := (*headerCommentMiddleware)(nil).ID()
  65  	m, ok := s.Build.Get(id)
  66  	if !ok {
  67  		m := &headerCommentMiddleware{values: http.Header{}}
  68  		if err := s.Build.Add(m, middleware.After); err != nil {
  69  			return nil, fmt.Errorf("add build: %v", err)
  70  		}
  71  
  72  		return m, nil
  73  	}
  74  
  75  	hc, ok := m.(*headerCommentMiddleware)
  76  	if !ok {
  77  		return nil, fmt.Errorf("existing middleware w/ id %s is not *headerCommentMiddleware", id)
  78  	}
  79  
  80  	return hc, nil
  81  }
  82