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  }