github.com/Kong/go-pdk@v0.11.0/response/response.go (about) 1 /* 2 Client response module. 3 4 The downstream response module contains a set of functions for producing 5 and manipulating responses sent back to the client (“downstream”). 6 Responses can be produced by Kong (e.g. an authentication plugin rejecting 7 a request), or proxied back from an Service’s response body. 8 9 Unlike kong.ServiceResponse, this module allows mutating the response 10 before sending it back to the client. 11 */ 12 package response 13 14 import ( 15 "github.com/Kong/go-pdk/bridge" 16 "github.com/Kong/go-pdk/server/kong_plugin_protocol" 17 "google.golang.org/protobuf/types/known/structpb" 18 ) 19 20 // Holds this module's functions. Accessible as `kong.Response` 21 type Response struct { 22 bridge.PdkBridge 23 } 24 25 // kong.Response.GetStatus() returns the HTTP status code 26 // currently set for the downstream response (as an integer). 27 // 28 // If the request was proxied (as per kong.Response.GetSource()), 29 // the return value will be that of the response from the Service 30 // (identical to kong.ServiceResponse.GetStatus()). 31 // 32 // If the request was not proxied, and the response was produced 33 // by Kong itself (i.e. via kong.Response.Exit()), the return value 34 // will be returned as-is. 35 func (r Response) GetStatus() (int, error) { 36 return r.AskInt(`kong.response.get_status`, nil) 37 } 38 39 // kong.Response.GetHeader() returns the value of the specified 40 // response header, as would be seen by the client once received. 41 // 42 // The list of headers returned by this function can consist of 43 // both response headers from the proxied Service and headers 44 // added by Kong (e.g. via kong.Response.AddHeader()). 45 // 46 // The return value is either a string, or can be nil if a header 47 // with name was not found in the response. If a header with the 48 // same name is present multiple times in the request, this function 49 // will return the value of the first occurrence of this header. 50 // 51 // Header names are case-insensitive and dashes (-) can be written 52 // as underscores (_); that is, the header X-Custom-Header 53 // can also be retrieved as x_custom_header. 54 func (r Response) GetHeader(name string) (string, error) { 55 return r.AskString(`kong.response.get_header`, bridge.WrapString(name)) 56 } 57 58 // kong.Response.GetHeaders() returns a map holding the response headers. 59 // Keys are header names. Values are either a string with the header value, 60 // or an array of strings if a header was sent multiple times. 61 // Header names in this table are case-insensitive and are normalized 62 // to lowercase, and dashes (-) can be written as underscores (_); 63 // that is, the header X-Custom-Header can also be retrieved as x_custom_header. 64 // 65 // A response initially has no headers until a plugin short-circuits 66 // the proxying by producing one (e.g. an authentication plugin rejecting 67 // a request), or the request has been proxied, and one of the latter 68 // execution phases is currently running. 69 // 70 // Unlike kong.ServiceResponse.GetHeaders(), this function returns 71 // all headers as the client would see them upon reception, 72 // including headers added by Kong itself. 73 // 74 // The max_args argument specifies the maximum number of returned headers. 75 // Must be greater than 1 and not greater than 1000, or -1 to specify the 76 // default limit of 100 arguments. 77 func (r Response) GetHeaders(max_headers int) (res map[string][]string, err error) { 78 if max_headers == -1 { 79 max_headers = 100 80 } 81 82 arg := kong_plugin_protocol.Int{V: int32(max_headers)} 83 out := new(structpb.Struct) 84 err = r.Ask(`kong.response.get_headers`, &arg, out) 85 if err != nil { 86 return nil, err 87 } 88 89 return bridge.UnwrapHeaders(out), nil 90 } 91 92 // kong.Response.GetSource() helps determining where the current response 93 // originated from. Kong being a reverse proxy, it can short-circuit 94 // a request and produce a response of its own, or the response can 95 // come from the proxied Service. 96 // 97 // Returns a string with three possible values: 98 // 99 // - “exit” is returned when, at some point during the processing of the request, 100 // there has been a call to kong.response.exit(). In other words, when the request 101 // was short-circuited by a plugin or by Kong itself (e.g. invalid credentials). 102 // 103 // - “error” is returned when an error has happened while processing the request 104 // - for example, a timeout while connecting to the upstream service. 105 // 106 // - “service” is returned when the response was originated by 107 // successfully contacting the proxied Service. 108 func (r Response) GetSource() (string, error) { 109 return r.AskString(`kong.response.get_source`, nil) 110 } 111 112 // kong.Response.SetStatus() allows changing the downstream response 113 // HTTP status code before sending it to the client. 114 // 115 // This function should be used in the header_filter phase, 116 // as Kong is preparing headers to be sent back to the client. 117 func (r Response) SetStatus(status int) error { 118 arg := kong_plugin_protocol.Int{V: int32(status)} 119 err := r.Ask(`kong.response.set_status`, &arg, nil) 120 return err 121 } 122 123 // kong.Response.SetHeader() sets a response header with the given value. 124 // This function overrides any existing header with the same name. 125 // 126 // This function should be used in the header_filter phase, 127 // as Kong is preparing headers to be sent back to the client. 128 func (r Response) SetHeader(k string, v string) error { 129 arg := kong_plugin_protocol.KV{ 130 K: k, 131 V: structpb.NewStringValue(v), 132 } 133 err := r.Ask(`kong.response.set_header`, &arg, nil) 134 return err 135 } 136 137 // kong.Response.AddHeader() adds a response header with the given value. 138 // Unlike kong.Response.SetHeader(), this function does not remove 139 // any existing header with the same name. Instead, another header 140 // with the same name will be added to the response. If no header 141 // with this name already exists on the response, then it is added 142 // with the given value, similarly to kong.Response.SetHeader(). 143 // 144 // This function should be used in the header_filter phase, 145 // as Kong is preparing headers to be sent back to the client. 146 func (r Response) AddHeader(k string, v string) error { 147 arg := kong_plugin_protocol.KV{ 148 K: k, 149 V: structpb.NewStringValue(v), 150 } 151 err := r.Ask(`kong.response.add_header`, &arg, nil) 152 return err 153 } 154 155 // kong.Response.ClearHeader() removes all occurrences of the specified header 156 // in the response sent to the client. 157 // 158 // This function should be used in the header_filter phase, 159 // as Kong is preparing headers to be sent back to the client. 160 func (r Response) ClearHeader(k string) error { 161 err := r.Ask(`kong.response.clear_header`, bridge.WrapString(k), nil) 162 return err 163 } 164 165 // kong.Response.SetHeaders() sets the headers for the response. 166 // Unlike kong.Response.SetHeader(), the headers argument must be a map 167 // in which each key is a string (corresponding to a header’s name), 168 // and each value is an array of strings. To clear a previously 169 // set header, you can set it's value to an empty array. 170 // 171 // This function should be used in the header_filter phase, 172 // as Kong is preparing headers to be sent back to the client. 173 // 174 // The resulting headers are produced in lexicographical order. 175 // The order of entries with the same name (when values are given 176 // as an array) is retained. 177 // 178 // This function overrides any existing header bearing the same name 179 // as those specified in the headers argument. Other headers remain unchanged. 180 func (r Response) SetHeaders(headers map[string][]string) error { 181 arg, err := bridge.WrapHeaders(headers) 182 if err != nil { 183 return err 184 } 185 186 err = r.Ask(`kong.response.set_headers`, arg, nil) 187 return err 188 } 189 190 // kong.Response.Exit() interrupts the current processing and produces a response. 191 // It is typical to see plugins using it to produce a response before Kong 192 // has a chance to proxy the request (e.g. an authentication plugin rejecting 193 // a request, or a caching plugin serving a cached response). 194 // 195 // This function closes the channel back to Kong main process, so any use of a 196 // PDK function after this would trigger a run-time panic. It is recommended to 197 // stop all processing and return immediately from the handler. 198 // 199 // Calling `kong.Response.Exit()` will interrupt the execution flow of 200 // plugins in the current phase. Subsequent phases will still be invoked. 201 // E.g. if a plugin called `kong.Response.Exit()` in the `access` phase, no 202 // other plugin will be executed in that phase, but the `header_filter`, 203 // `body_filter`, and `log` phases will still be executed, along with their 204 // plugins. Plugins should thus be programmed defensively against cases when 205 // a request was **not** proxied to the Service, but instead was produced by 206 // Kong itself. 207 // 208 // The first argument `status` will set the status code of the response that 209 // will be seen by the client. 210 // 211 // The second, `body` argument will set the response body. No special processing 212 // will be done, and the body will be sent as-is. It is the caller's responsibility 213 // to set the appropriate Content-Type header via the third argument. On gRPC 214 // we cannot send the `body` with this function at the moment at least, so what it does 215 // instead is that it sends "body" in `grpc-message` header instead. 216 // 217 // The third, `headers` argument can be a table specifying response headers to send. 218 // If non nil, its behavior is similar to `kong.response.set_headers()`. 219 // 220 // Unless manually specified, this method will automatically set the 221 // Content-Length header in the produced response for convenience. 222 223 func (r Response) Exit(status int, body []byte, headers map[string][]string) { 224 h, _ := bridge.WrapHeaders(headers) 225 arg := kong_plugin_protocol.ExitArgs{ 226 Status: int32(status), 227 Body: body, 228 Headers: h, 229 } 230 _ = r.Ask(`kong.response.exit`, &arg, nil) 231 r.Close() 232 } 233 234 // kong.Response.ExitStatus() terminates current processing just like kong.Response.Exit() 235 // without setting the body or headers. 236 func (r Response) ExitStatus(status int) { 237 arg := kong_plugin_protocol.ExitArgs{ 238 Status: int32(status), 239 } 240 _ = r.Ask(`kong.response.exit`, &arg, nil) 241 r.Close() 242 }