Skip to content

Commit

Permalink
reverseproxy: Add handle_response blocks to reverse_proxy (#3710)
Browse files Browse the repository at this point in the history
  • Loading branch information
maxatome authored and francislavoie committed May 1, 2021
1 parent 956f011 commit 73a0059
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 5 deletions.
7 changes: 7 additions & 0 deletions caddyconfig/httpcaddyfile/directives.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,13 @@ func (h Helper) NewBindAddresses(addrs []string) []ConfigValue {
return []ConfigValue{{Class: "bind", Value: addrs}}
}

// WithDispenser returns a new instance based on d. All others Helper
// fields are copied, so typically maps are shared with this new instance.
func (h Helper) WithDispenser(d *caddyfile.Dispenser) Helper {
h.Dispenser = d
return h
}

// ParseSegmentAsSubroute parses the segment such that its subdirectives
// are themselves treated as directives, from which a subroute is built
// and returned.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
:8884

reverse_proxy 127.0.0.1:65535 {
handle_response header X-Accel-Redirect {
respond "Header!"
}
handle_response status 401 {
respond "Status!"
}
handle_response {
respond "Any!"
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8884"
],
"routes": [
{
"handle": [
{
"handle_response": [
{
"match": {
"headers": {
"X-Accel-Redirect": []
}
},
"routes": [
{
"handle": [
{
"body": "Header!",
"handler": "static_response"
}
]
}
]
},
{
"match": {
"status_code": [
401
]
},
"routes": [
{
"handle": [
{
"body": "Status!",
"handler": "static_response"
}
]
}
]
},
{
"match": {},
"routes": [
{
"handle": [
{
"body": "Any!",
"handler": "static_response"
}
]
}
]
}
],
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "127.0.0.1:65535"
}
]
}
]
}
]
}
}
}
}
}
53 changes: 49 additions & 4 deletions modules/caddyhttp/reverseproxy/caddyfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ func init() {

func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
rp := new(Handler)
err := rp.UnmarshalCaddyfile(h.Dispenser)
err := rp.ParseCaddyfileReverseProxy(h)
if err != nil {
return nil, err
}
return rp, nil
}

// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
// ParseCaddyfileReverseProxy sets up the handler from Caddyfile tokens. Syntax:
//
// reverse_proxy [<matcher>] [<upstreams...>] {
// # upstreams
Expand Down Expand Up @@ -86,13 +86,20 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
// transport <name> {
// ...
// }
//
// # handle responses
// handle_response [header <field>|status <status>] {
// ...
// }
// }
//
// Proxy upstream addresses should be network dial addresses such
// as `host:port`, or a URL such as `scheme://host:port`. Scheme
// and port may be inferred from other parts of the address/URL; if
// either are missing, defaults to HTTP.
func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
func (h *Handler) ParseCaddyfileReverseProxy(helper httpcaddyfile.Helper) error {
d := helper.Dispenser

// currently, all backends must use the same scheme/protocol (the
// underlying JSON does not yet support per-backend transports)
var commonScheme string
Expand Down Expand Up @@ -617,6 +624,45 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
}
transport = rt

case "handle_response":
var rm caddyhttp.ResponseMatcher
args := d.RemainingArgs()
switch len(args) {
case 2:
switch args[0] {
case "header":
rm = caddyhttp.ResponseMatcher{
Headers: http.Header{args[1]: []string{}},
}
case "status":
statusNum, err := strconv.Atoi(args[1])
if err != nil {
return d.Errf("bad status value '%s': %v", args[1], err)
}
rm = caddyhttp.ResponseMatcher{
StatusCode: []int{statusNum},
}
default:
return d.Err("handle_response only accepts header|status")
}
case 0: // Any status or header
default:
return d.ArgErr()
}
handler, err := httpcaddyfile.ParseSegmentAsSubroute(helper.WithDispenser(d.NewFromNextSegment()))
if err != nil {
return err
}
subroute, ok := handler.(*caddyhttp.Subroute)
if !ok {
return helper.Errf("segment was not parsed as a subroute")
}
h.HandleResponse = append(h.HandleResponse,
caddyhttp.ResponseHandler{
Match: &rm,
Routes: subroute.Routes,
})

default:
return d.Errf("unrecognized subdirective %s", d.Val())
}
Expand Down Expand Up @@ -894,6 +940,5 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {

// Interface guards
var (
_ caddyfile.Unmarshaler = (*Handler)(nil)
_ caddyfile.Unmarshaler = (*HTTPTransport)(nil)
)
2 changes: 1 addition & 1 deletion modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
// using the reverse_proxy directive syntax
// TODO: this can overwrite our fcgiTransport that we encoded and
// set on the rpHandler... even with a non-fastcgi transport!
err = rpHandler.UnmarshalCaddyfile(dispenser)
err = rpHandler.ParseCaddyfileReverseProxy(h.WithDispenser(dispenser))
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 73a0059

Please sign in to comment.