From 5ae1790e525609869e24f8201d1c90fb9f16b010 Mon Sep 17 00:00:00 2001
From: Matthew Holt <Matthew.Holt+git@gmail.com>
Date: Sat, 31 Jan 2015 10:15:17 -0700
Subject: [PATCH] Moved controller into its own file; other minor cleanups

---
 config/controller.go      | 43 ++++++++++++++++++++++++
 config/directives.go      |  4 +--
 config/dispenser.go       | 52 +++--------------------------
 config/parsing.go         | 70 +++++++++++++++++++++++++++++++++------
 middleware/headers/new.go |  6 +---
 5 files changed, 110 insertions(+), 65 deletions(-)
 create mode 100644 config/controller.go

diff --git a/config/controller.go b/config/controller.go
new file mode 100644
index 000000000..9a63d4ad6
--- /dev/null
+++ b/config/controller.go
@@ -0,0 +1,43 @@
+package config
+
+// controller is a dispenser of tokens and also
+// facilitates setup with the server by providing
+// access to its configuration. It implements
+// the middleware.Controller interface.
+type controller struct {
+	dispenser
+}
+
+// newController returns a new controller.
+func newController(p *parser) *controller {
+	return &controller{
+		dispenser: dispenser{
+			cursor: -1,
+			parser: p,
+		},
+	}
+}
+
+// Startup registers a function to execute when the server starts.
+func (c *controller) Startup(fn func() error) {
+	c.parser.cfg.Startup = append(c.parser.cfg.Startup, fn)
+}
+
+// Root returns the server root file path.
+func (c *controller) Root() string {
+	if c.parser.cfg.Root == "" {
+		return "."
+	} else {
+		return c.parser.cfg.Root
+	}
+}
+
+// Host returns the hostname the server is bound to.
+func (c *controller) Host() string {
+	return c.parser.cfg.Host
+}
+
+// Port returns the port that the server is listening on.
+func (c *controller) Port() string {
+	return c.parser.cfg.Port
+}
diff --git a/config/directives.go b/config/directives.go
index c7347aa12..26047ddf9 100644
--- a/config/directives.go
+++ b/config/directives.go
@@ -11,8 +11,8 @@ import (
 // a particular directive and populates the config.
 type dirFunc func(*parser) error
 
-// validDirectives is a map of valid directive names to
-// their parsing function.
+// validDirectives is a map of valid, built-in directive names
+// to their parsing function.
 var validDirectives map[string]dirFunc
 
 func init() {
diff --git a/config/dispenser.go b/config/dispenser.go
index b9947dfff..0018fe8c6 100644
--- a/config/dispenser.go
+++ b/config/dispenser.go
@@ -5,54 +5,10 @@ import (
 	"fmt"
 )
 
-// newController returns a new controller.
-func newController(p *parser) *controller {
-	return &controller{
-		dispenser: dispenser{
-			cursor: -1,
-			parser: p,
-		},
-	}
-}
-
-// controller is a dispenser of tokens and also
-// facilitates setup with the server by providing
-// access to its configuration. It implements
-// the middleware.Controller interface.
-type controller struct {
-	dispenser
-}
-
-// Startup registers a function to execute when the server starts.
-func (c *controller) Startup(fn func() error) {
-	c.parser.cfg.Startup = append(c.parser.cfg.Startup, fn)
-}
-
-// Root returns the server root file path.
-func (c *controller) Root() string {
-	if c.parser.cfg.Root == "" {
-		return "."
-	} else {
-		return c.parser.cfg.Root
-	}
-}
-
-// Host returns the hostname the server is bound to.
-func (c *controller) Host() string {
-	return c.parser.cfg.Host
-}
-
-// Port returns the port that the server is listening on.
-func (c *controller) Port() string {
-	return c.parser.cfg.Port
-}
-
 // dispenser is a type that gets exposed to middleware
 // generators so that they can parse tokens to configure
-// their instance.
-// TODO: Rename this; it does more than dispense tokens.
-// It also provides access to the server to touch its
-// configuration.
+// their instance. It basically dispenses tokens but can
+// do so in a structured manner.
 type dispenser struct {
 	parser  *parser
 	cursor  int
@@ -115,7 +71,9 @@ func (d *dispenser) NextLine() bool {
 // if the current token is an open curly brace on the
 // same line. If so, that token is consumed and this
 // function will return true until the closing curly
-// brace is consumed by this method.
+// brace is consumed by this method. Usually, you would
+// use this as the condition of a for loop to parse
+// tokens while being inside the block.
 func (d *dispenser) NextBlock() bool {
 	if d.nesting > 0 {
 		d.Next()
diff --git a/config/parsing.go b/config/parsing.go
index 4e8b99f6b..8e85184aa 100644
--- a/config/parsing.go
+++ b/config/parsing.go
@@ -62,7 +62,7 @@ func (p *parser) addressBlock() error {
 // openCurlyBrace expects the current token to be an
 // opening curly brace. This acts like an assertion
 // because it returns an error if the token is not
-// a opening curly brace.
+// a opening curly brace. It does not advance the token.
 func (p *parser) openCurlyBrace() error {
 	if p.tkn() != "{" {
 		return p.syntaxErr("{")
@@ -73,7 +73,7 @@ func (p *parser) openCurlyBrace() error {
 // closeCurlyBrace expects the current token to be
 // a closing curly brace. This acts like an assertion
 // because it returns an error if the token is not
-// a closing curly brace.
+// a closing curly brace. It does not advance the token.
 func (p *parser) closeCurlyBrace() error {
 	if p.tkn() != "}" {
 		return p.syntaxErr("}")
@@ -84,32 +84,80 @@ func (p *parser) closeCurlyBrace() error {
 // directives parses through all the directives
 // and it expects the current token to be the first
 // directive. It goes until EOF or closing curly
-// brace.
+// brace which ends the address block.
 func (p *parser) directives() error {
 	for p.next() {
 		if p.tkn() == "}" {
 			// end of address scope
 			break
 		}
-		if fn, ok := validDirectives[p.tkn()]; ok {
-			err := fn(p)
+		if p.tkn()[0] == '/' {
+			// Path scope (a.k.a. location context)
+			// TODO: The parser can handle the syntax (obviously), but the
+			// implementation is incomplete. This is intentional,
+			// until we can better decide what kind of feature set we
+			// want to support. Until this is ready, we leave this
+			// syntax undocumented.
+
+			// location := p.tkn()
+
+			if !p.next() {
+				return p.eofErr()
+			}
+
+			err := p.openCurlyBrace()
 			if err != nil {
 				return err
 			}
-		} else if middlewareRegistered(p.tkn()) {
-			err := p.collectTokens()
-			if err != nil {
-				return err
+
+			for p.next() {
+				err := p.closeCurlyBrace()
+				if err == nil { // end of location context
+					break
+				}
+
+				// TODO: How should we give the context to the directives?
+				// Or how do we tell the server that these directives should only
+				// be executed for requests routed to the current path?
+
+				err = p.directive()
+				if err != nil {
+					return err
+				}
 			}
-		} else {
-			return p.err("Syntax", "Unexpected token '"+p.tkn()+"', expecting a valid directive")
+		} else if err := p.directive(); err != nil {
+			return err
 		}
 	}
 	return nil
 }
 
+// directive asserts that the current token is either a built-in
+// directive or a registered middleware directive; otherwise an error
+// will be returned.
+func (p *parser) directive() error {
+	if fn, ok := validDirectives[p.tkn()]; ok {
+		// Built-in (standard) directive
+		err := fn(p)
+		if err != nil {
+			return err
+		}
+	} else if middlewareRegistered(p.tkn()) {
+		// Middleware directive
+		err := p.collectTokens()
+		if err != nil {
+			return err
+		}
+	} else {
+		return p.err("Syntax", "Unexpected token '"+p.tkn()+"', expecting a valid directive")
+	}
+	return nil
+}
+
 // collectTokens consumes tokens until the directive's scope
 // closes (either end of line or end of curly brace block).
+// It creates a controller which is stored in the parser for
+// later use by the middleware.
 func (p *parser) collectTokens() error {
 	directive := p.tkn()
 	line := p.line()
diff --git a/middleware/headers/new.go b/middleware/headers/new.go
index fd4434635..231a56f5f 100644
--- a/middleware/headers/new.go
+++ b/middleware/headers/new.go
@@ -15,10 +15,6 @@ func New(c middleware.Controller) (middleware.Middleware, error) {
 	}
 
 	return func(next http.HandlerFunc) http.HandlerFunc {
-		head := Headers{
-			Next:  next,
-			Rules: rules,
-		}
-		return head.ServeHTTP
+		return Headers{Next: next, Rules: rules}.ServeHTTP
 	}, nil
 }