From 0fc47e8357af5ccd6f800819722229b1a279e5b5 Mon Sep 17 00:00:00 2001
From: Matthew Holt <mholt@users.noreply.github.com>
Date: Fri, 2 Oct 2020 15:23:52 -0600
Subject: [PATCH] map: Apply default if mapped output is nil

---
 caddytest/integration/map_test.go  |  4 ++--
 modules/caddyhttp/map/caddyfile.go |  9 ++++++---
 modules/caddyhttp/map/map.go       | 24 +++++++++++++++---------
 3 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/caddytest/integration/map_test.go b/caddytest/integration/map_test.go
index dc077ee7b..aa535ecdb 100644
--- a/caddytest/integration/map_test.go
+++ b/caddytest/integration/map_test.go
@@ -18,7 +18,7 @@ func TestMap(t *testing.T) {
 	localhost:9080 {
 
 		map {http.request.method} {dest-1} {dest-2} {
-			default unknown
+			default unknown1    unknown2
 			~G.T    get-called
 			POST    post-called foobar
 		}
@@ -30,7 +30,7 @@ func TestMap(t *testing.T) {
 	`, "caddyfile")
 
 	// act and assert
-	tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost get-called ")
+	tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost get-called unknown2")
 	tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called foobar")
 }
 
diff --git a/modules/caddyhttp/map/caddyfile.go b/modules/caddyhttp/map/caddyfile.go
index 67c148bdf..eb0c5ae38 100644
--- a/modules/caddyhttp/map/caddyfile.go
+++ b/modules/caddyhttp/map/caddyfile.go
@@ -70,7 +70,10 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
 
 			// every other line maps one input to one or more outputs
 			in := h.Val()
-			outs := h.RemainingArgs()
+			var outs []interface{}
+			for _, out := range h.RemainingArgs() {
+				outs = append(outs, out)
+			}
 
 			// cannot have more outputs than destinations
 			if len(outs) > len(handler.Destinations) {
@@ -78,9 +81,9 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
 			}
 
 			// for convenience, can have fewer outputs than destinations, but the
-			// underlying handler won't accept that, so we fill in empty values
+			// underlying handler won't accept that, so we fill in nil values
 			for len(outs) < len(handler.Destinations) {
-				outs = append(outs, "")
+				outs = append(outs, nil)
 			}
 
 			// create the mapping
diff --git a/modules/caddyhttp/map/map.go b/modules/caddyhttp/map/map.go
index 29aa19f29..ba90e6f59 100644
--- a/modules/caddyhttp/map/map.go
+++ b/modules/caddyhttp/map/map.go
@@ -16,7 +16,6 @@ package maphandler
 
 import (
 	"fmt"
-	"log"
 	"net/http"
 	"regexp"
 	"strings"
@@ -40,14 +39,15 @@ type Handler struct {
 	// Source is the placeholder from which to get the input value.
 	Source string `json:"source,omitempty"`
 
-	// Destinations are the placeholders in which to store the outputs.
+	// Destinations are the names of placeholders in which to store the outputs.
 	Destinations []string `json:"destinations,omitempty"`
 
 	// Mappings from source values (inputs) to destination values (outputs).
 	// The first matching mapping will be applied.
 	Mappings []Mapping `json:"mappings,omitempty"`
 
-	// If no mappings match, the default value will be applied (optional).
+	// If no mappings match or if the mapped output is null/nil, the associated
+	// default output will be applied (optional).
 	Defaults []string
 }
 
@@ -125,20 +125,26 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
 		// find the first mapping matching the input and return
 		// the requested destination/output value
 		for _, m := range h.Mappings {
-			log.Printf("MAPPING: %+v", m)
 			if m.re != nil {
 				if m.re.MatchString(input) {
-					return m.Outputs[destIdx], true
+					if output := m.Outputs[destIdx]; output == nil {
+						break
+					} else {
+						return output, true
+					}
 				}
 				continue
 			}
 			if input == m.Input {
-				log.Printf("RETURNING: %s", m.Outputs[destIdx])
-				return m.Outputs[destIdx], true
+				if output := m.Outputs[destIdx]; output == nil {
+					break
+				} else {
+					return output, true
+				}
 			}
 		}
 
-		// fall back to default if no match
+		// fall back to default if no match or if matched nil value
 		if len(h.Defaults) > destIdx {
 			return h.Defaults[destIdx], true
 		}
@@ -171,7 +177,7 @@ type Mapping struct {
 
 	// Upon a match with the input, each output is positionally correlated
 	// with each destination of the parent handler.
-	Outputs []string `json:"outputs,omitempty"`
+	Outputs []interface{} `json:"outputs,omitempty"`
 
 	re *regexp.Regexp
 }