github.com/xmidt-org/webpa-common@v1.11.9/xhttp/fanout/requestResponse.go (about) 1 package fanout 2 3 import ( 4 "context" 5 "net/http" 6 "net/textproto" 7 "strings" 8 9 "github.com/gorilla/mux" 10 "github.com/xmidt-org/webpa-common/xhttp" 11 ) 12 13 // FanoutRequestFunc is invoked to build a fanout request. It can transfer information from the original request, 14 // set the body, update the context, etc. This is the analog of go-kit's RequestFunc. 15 type FanoutRequestFunc func(ctx context.Context, original, fanout *http.Request, body []byte) (context.Context, error) 16 17 // ForwardBody creates a FanoutRequestFunc that sends the original request's body to each fanout. 18 // If followRedirects is true, this function also sets fanout.GetBody so that the same body is read for redirects. 19 // 20 // This function also sets the ContentLength and Content-Type header appropriately. 21 func ForwardBody(followRedirects bool) FanoutRequestFunc { 22 return func(ctx context.Context, original, fanout *http.Request, originalBody []byte) (context.Context, error) { 23 fanout.ContentLength = int64(len(originalBody)) 24 fanout.Body = nil 25 fanout.GetBody = nil 26 fanout.Header.Del("Content-Type") 27 28 if len(originalBody) > 0 { 29 fanout.Header.Set("Content-Type", original.Header.Get("Content-Type")) 30 body, getBody := xhttp.NewRewindBytes(originalBody) 31 fanout.Body = body 32 if followRedirects { 33 fanout.GetBody = getBody 34 } 35 } 36 37 return ctx, nil 38 } 39 } 40 41 // ForwardHeaders creates a FanoutRequestFunc that copies headers from the original request onto each fanout request 42 func ForwardHeaders(headers ...string) FanoutRequestFunc { 43 canonicalizedHeaders := make([]string, len(headers)) 44 for i := 0; i < len(headers); i++ { 45 canonicalizedHeaders[i] = textproto.CanonicalMIMEHeaderKey(headers[i]) 46 } 47 48 return func(ctx context.Context, original, fanout *http.Request, _ []byte) (context.Context, error) { 49 for _, key := range canonicalizedHeaders { 50 if values := original.Header[key]; len(values) > 0 { 51 fanout.Header[key] = append(fanout.Header[key], values...) 52 } 53 } 54 55 return ctx, nil 56 } 57 } 58 59 // UsePath sets a constant URI path for every fanout request. Essentially, this replaces the original URL's 60 // Path with the configured value. 61 func UsePath(path string) FanoutRequestFunc { 62 return func(ctx context.Context, original, fanout *http.Request, _ []byte) (context.Context, error) { 63 fanout.URL.Path = path 64 fanout.URL.RawPath = "" 65 return ctx, nil 66 } 67 } 68 69 // ForwardVariableAsHeader returns a request function that copies the value of a gorilla/mux path variable 70 // from the original HTTP request into an HTTP header on each fanout request. 71 // 72 // The fanout request will always have the given header. If no path variable is supplied (or no path variables 73 // are found), the fanout request will have the header associated with an empty string. 74 func ForwardVariableAsHeader(variable, header string) FanoutRequestFunc { 75 return func(ctx context.Context, original, fanout *http.Request, _ []byte) (context.Context, error) { 76 variables := mux.Vars(original) 77 if len(variables) > 0 { 78 fanout.Header.Add(header, variables[variable]) 79 } else { 80 fanout.Header.Add(header, "") 81 } 82 83 return ctx, nil 84 } 85 } 86 87 // FanoutResponseFunc is a strategy applied to the termination fanout response. 88 type FanoutResponseFunc func(ctx context.Context, response http.ResponseWriter, result Result) context.Context 89 90 // ReturnHeaders copies zero or more headers from the fanout response into the top-level HTTP response. 91 func ReturnHeaders(headers ...string) FanoutResponseFunc { 92 canonicalizedHeaders := make([]string, len(headers)) 93 for i := 0; i < len(headers); i++ { 94 canonicalizedHeaders[i] = textproto.CanonicalMIMEHeaderKey(headers[i]) 95 } 96 97 return func(ctx context.Context, response http.ResponseWriter, result Result) context.Context { 98 if result.Response != nil { 99 header := response.Header() 100 for _, key := range canonicalizedHeaders { 101 if values := result.Response.Header[key]; len(values) > 0 { 102 header[key] = append(header[key], values...) 103 } 104 } 105 } 106 107 return ctx 108 } 109 } 110 111 // ReturnHeadersWithPrefix copies zero or more headers from the fanout where the headerPrefix is matched in the response into the top-level HTTP response. 112 func ReturnHeadersWithPrefix(headerPrefixs ...string) FanoutResponseFunc { 113 canonicalizedHeaders := make([]string, len(headerPrefixs)) 114 for i := 0; i < len(headerPrefixs); i++ { 115 canonicalizedHeaders[i] = textproto.CanonicalMIMEHeaderKey(headerPrefixs[i]) 116 } 117 118 return func(ctx context.Context, response http.ResponseWriter, result Result) context.Context { 119 if result.Response != nil { 120 header := response.Header() 121 for _, prefix := range canonicalizedHeaders { 122 for key, results := range result.Response.Header { 123 if strings.HasPrefix(key, prefix) && len(results) > 0 { 124 header[key] = append(header[key], results...) 125 } 126 } 127 128 } 129 } 130 131 return ctx 132 } 133 }