diff --git a/config/controller.go b/config/controller.go new file mode 100644 index 00000000..9a63d4ad --- /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 c7347aa1..26047ddf 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 b9947dff..0018fe8c 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 4e8b99f6..8e85184a 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 fd443463..231a56f5 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 }