parse.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. // Copyright 2015 Light Code Labs, LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package caddyfile
  15. import (
  16. "io"
  17. "log"
  18. "os"
  19. "path/filepath"
  20. "strings"
  21. )
  22. // Parse parses the input just enough to group tokens, in
  23. // order, by server block. No further parsing is performed.
  24. // Server blocks are returned in the order in which they appear.
  25. // Directives that do not appear in validDirectives will cause
  26. // an error. If you do not want to check for valid directives,
  27. // pass in nil instead.
  28. func Parse(filename string, input io.Reader, validDirectives []string) ([]ServerBlock, error) {
  29. p := parser{Dispenser: NewDispenser(filename, input), validDirectives: validDirectives}
  30. return p.parseAll()
  31. }
  32. // allTokens lexes the entire input, but does not parse it.
  33. // It returns all the tokens from the input, unstructured
  34. // and in order.
  35. func allTokens(input io.Reader) ([]Token, error) {
  36. l := new(lexer)
  37. err := l.load(input)
  38. if err != nil {
  39. return nil, err
  40. }
  41. var tokens []Token
  42. for l.next() {
  43. tokens = append(tokens, l.token)
  44. }
  45. return tokens, nil
  46. }
  47. type parser struct {
  48. Dispenser
  49. block ServerBlock // current server block being parsed
  50. validDirectives []string // a directive must be valid or it's an error
  51. eof bool // if we encounter a valid EOF in a hard place
  52. definedSnippets map[string][]Token
  53. }
  54. func (p *parser) parseAll() ([]ServerBlock, error) {
  55. var blocks []ServerBlock
  56. for p.Next() {
  57. err := p.parseOne()
  58. if err != nil {
  59. return blocks, err
  60. }
  61. if len(p.block.Keys) > 0 {
  62. blocks = append(blocks, p.block)
  63. }
  64. }
  65. return blocks, nil
  66. }
  67. func (p *parser) parseOne() error {
  68. p.block = ServerBlock{Tokens: make(map[string][]Token)}
  69. return p.begin()
  70. }
  71. func (p *parser) begin() error {
  72. if len(p.tokens) == 0 {
  73. return nil
  74. }
  75. err := p.addresses()
  76. if err != nil {
  77. return err
  78. }
  79. if p.eof {
  80. // this happens if the Caddyfile consists of only
  81. // a line of addresses and nothing else
  82. return nil
  83. }
  84. if ok, name := p.isSnippet(); ok {
  85. if p.definedSnippets == nil {
  86. p.definedSnippets = map[string][]Token{}
  87. }
  88. if _, found := p.definedSnippets[name]; found {
  89. return p.Errf("redeclaration of previously declared snippet %s", name)
  90. }
  91. // consume all tokens til matched close brace
  92. tokens, err := p.snippetTokens()
  93. if err != nil {
  94. return err
  95. }
  96. p.definedSnippets[name] = tokens
  97. // empty block keys so we don't save this block as a real server.
  98. p.block.Keys = nil
  99. return nil
  100. }
  101. return p.blockContents()
  102. }
  103. func (p *parser) addresses() error {
  104. var expectingAnother bool
  105. for {
  106. tkn := replaceEnvVars(p.Val())
  107. // special case: import directive replaces tokens during parse-time
  108. if tkn == "import" && p.isNewLine() {
  109. err := p.doImport()
  110. if err != nil {
  111. return err
  112. }
  113. continue
  114. }
  115. // Open brace definitely indicates end of addresses
  116. if tkn == "{" {
  117. if expectingAnother {
  118. return p.Errf("Expected another address but had '%s' - check for extra comma", tkn)
  119. }
  120. break
  121. }
  122. if tkn != "" { // empty token possible if user typed ""
  123. // Trailing comma indicates another address will follow, which
  124. // may possibly be on the next line
  125. if tkn[len(tkn)-1] == ',' {
  126. tkn = tkn[:len(tkn)-1]
  127. expectingAnother = true
  128. } else {
  129. expectingAnother = false // but we may still see another one on this line
  130. }
  131. p.block.Keys = append(p.block.Keys, tkn)
  132. }
  133. // Advance token and possibly break out of loop or return error
  134. hasNext := p.Next()
  135. if expectingAnother && !hasNext {
  136. return p.EOFErr()
  137. }
  138. if !hasNext {
  139. p.eof = true
  140. break // EOF
  141. }
  142. if !expectingAnother && p.isNewLine() {
  143. break
  144. }
  145. }
  146. return nil
  147. }
  148. func (p *parser) blockContents() error {
  149. errOpenCurlyBrace := p.openCurlyBrace()
  150. if errOpenCurlyBrace != nil {
  151. // single-server configs don't need curly braces
  152. p.cursor--
  153. }
  154. err := p.directives()
  155. if err != nil {
  156. return err
  157. }
  158. // Only look for close curly brace if there was an opening
  159. if errOpenCurlyBrace == nil {
  160. err = p.closeCurlyBrace()
  161. if err != nil {
  162. return err
  163. }
  164. }
  165. return nil
  166. }
  167. // directives parses through all the lines for directives
  168. // and it expects the next token to be the first
  169. // directive. It goes until EOF or closing curly brace
  170. // which ends the server block.
  171. func (p *parser) directives() error {
  172. for p.Next() {
  173. // end of server block
  174. if p.Val() == "}" {
  175. break
  176. }
  177. // special case: import directive replaces tokens during parse-time
  178. if p.Val() == "import" {
  179. err := p.doImport()
  180. if err != nil {
  181. return err
  182. }
  183. p.cursor-- // cursor is advanced when we continue, so roll back one more
  184. continue
  185. }
  186. // normal case: parse a directive on this line
  187. if err := p.directive(); err != nil {
  188. return err
  189. }
  190. }
  191. return nil
  192. }
  193. // doImport swaps out the import directive and its argument
  194. // (a total of 2 tokens) with the tokens in the specified file
  195. // or globbing pattern. When the function returns, the cursor
  196. // is on the token before where the import directive was. In
  197. // other words, call Next() to access the first token that was
  198. // imported.
  199. func (p *parser) doImport() error {
  200. // syntax checks
  201. if !p.NextArg() {
  202. return p.ArgErr()
  203. }
  204. importPattern := replaceEnvVars(p.Val())
  205. if importPattern == "" {
  206. return p.Err("Import requires a non-empty filepath")
  207. }
  208. if p.NextArg() {
  209. return p.Err("Import takes only one argument (glob pattern or file)")
  210. }
  211. // splice out the import directive and its argument (2 tokens total)
  212. tokensBefore := p.tokens[:p.cursor-1]
  213. tokensAfter := p.tokens[p.cursor+1:]
  214. var importedTokens []Token
  215. // first check snippets. That is a simple, non-recursive replacement
  216. if p.definedSnippets != nil && p.definedSnippets[importPattern] != nil {
  217. importedTokens = p.definedSnippets[importPattern]
  218. } else {
  219. // make path relative to the file of the _token_ being processed rather
  220. // than current working directory (issue #867) and then use glob to get
  221. // list of matching filenames
  222. absFile, err := filepath.Abs(p.Dispenser.File())
  223. if err != nil {
  224. return p.Errf("Failed to get absolute path of file: %s: %v", p.Dispenser.filename, err)
  225. }
  226. var matches []string
  227. var globPattern string
  228. if !filepath.IsAbs(importPattern) {
  229. globPattern = filepath.Join(filepath.Dir(absFile), importPattern)
  230. } else {
  231. globPattern = importPattern
  232. }
  233. if strings.Count(globPattern, "*") > 1 || strings.Count(globPattern, "?") > 1 ||
  234. (strings.Contains(globPattern, "[") && strings.Contains(globPattern, "]")) {
  235. // See issue #2096 - a pattern with many glob expansions can hang for too long
  236. return p.Errf("Glob pattern may only contain one wildcard (*), but has others: %s", globPattern)
  237. }
  238. matches, err = filepath.Glob(globPattern)
  239. if err != nil {
  240. return p.Errf("Failed to use import pattern %s: %v", importPattern, err)
  241. }
  242. if len(matches) == 0 {
  243. if strings.ContainsAny(globPattern, "*?[]") {
  244. log.Printf("[WARNING] No files matching import glob pattern: %s", importPattern)
  245. } else {
  246. return p.Errf("File to import not found: %s", importPattern)
  247. }
  248. }
  249. // collect all the imported tokens
  250. for _, importFile := range matches {
  251. newTokens, err := p.doSingleImport(importFile)
  252. if err != nil {
  253. return err
  254. }
  255. importedTokens = append(importedTokens, newTokens...)
  256. }
  257. }
  258. // splice the imported tokens in the place of the import statement
  259. // and rewind cursor so Next() will land on first imported token
  260. p.tokens = append(tokensBefore, append(importedTokens, tokensAfter...)...)
  261. p.cursor--
  262. return nil
  263. }
  264. // doSingleImport lexes the individual file at importFile and returns
  265. // its tokens or an error, if any.
  266. func (p *parser) doSingleImport(importFile string) ([]Token, error) {
  267. file, err := os.Open(importFile)
  268. if err != nil {
  269. return nil, p.Errf("Could not import %s: %v", importFile, err)
  270. }
  271. defer file.Close()
  272. if info, err := file.Stat(); err != nil {
  273. return nil, p.Errf("Could not import %s: %v", importFile, err)
  274. } else if info.IsDir() {
  275. return nil, p.Errf("Could not import %s: is a directory", importFile)
  276. }
  277. importedTokens, err := allTokens(file)
  278. if err != nil {
  279. return nil, p.Errf("Could not read tokens while importing %s: %v", importFile, err)
  280. }
  281. // Tack the file path onto these tokens so errors show the imported file's name
  282. // (we use full, absolute path to avoid bugs: issue #1892)
  283. filename, err := filepath.Abs(importFile)
  284. if err != nil {
  285. return nil, p.Errf("Failed to get absolute path of file: %s: %v", p.Dispenser.filename, err)
  286. }
  287. for i := 0; i < len(importedTokens); i++ {
  288. importedTokens[i].File = filename
  289. }
  290. return importedTokens, nil
  291. }
  292. // directive collects tokens until the directive's scope
  293. // closes (either end of line or end of curly brace block).
  294. // It expects the currently-loaded token to be a directive
  295. // (or } that ends a server block). The collected tokens
  296. // are loaded into the current server block for later use
  297. // by directive setup functions.
  298. func (p *parser) directive() error {
  299. dir := replaceEnvVars(p.Val())
  300. nesting := 0
  301. // TODO: More helpful error message ("did you mean..." or "maybe you need to install its server type")
  302. if !p.validDirective(dir) {
  303. return p.Errf("Unknown directive '%s'", dir)
  304. }
  305. // The directive itself is appended as a relevant token
  306. p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])
  307. for p.Next() {
  308. if p.Val() == "{" {
  309. nesting++
  310. } else if p.isNewLine() && nesting == 0 {
  311. p.cursor-- // read too far
  312. break
  313. } else if p.Val() == "}" && nesting > 0 {
  314. nesting--
  315. } else if p.Val() == "}" && nesting == 0 {
  316. return p.Err("Unexpected '}' because no matching opening brace")
  317. } else if p.Val() == "import" && p.isNewLine() {
  318. if err := p.doImport(); err != nil {
  319. return err
  320. }
  321. p.cursor-- // cursor is advanced when we continue, so roll back one more
  322. continue
  323. }
  324. p.tokens[p.cursor].Text = replaceEnvVars(p.tokens[p.cursor].Text)
  325. p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])
  326. }
  327. if nesting > 0 {
  328. return p.EOFErr()
  329. }
  330. return nil
  331. }
  332. // openCurlyBrace expects the current token to be an
  333. // opening curly brace. This acts like an assertion
  334. // because it returns an error if the token is not
  335. // a opening curly brace. It does NOT advance the token.
  336. func (p *parser) openCurlyBrace() error {
  337. if p.Val() != "{" {
  338. return p.SyntaxErr("{")
  339. }
  340. return nil
  341. }
  342. // closeCurlyBrace expects the current token to be
  343. // a closing curly brace. This acts like an assertion
  344. // because it returns an error if the token is not
  345. // a closing curly brace. It does NOT advance the token.
  346. func (p *parser) closeCurlyBrace() error {
  347. if p.Val() != "}" {
  348. return p.SyntaxErr("}")
  349. }
  350. return nil
  351. }
  352. // validDirective returns true if dir is in p.validDirectives.
  353. func (p *parser) validDirective(dir string) bool {
  354. if p.validDirectives == nil {
  355. return true
  356. }
  357. for _, d := range p.validDirectives {
  358. if d == dir {
  359. return true
  360. }
  361. }
  362. return false
  363. }
  364. // replaceEnvVars replaces environment variables that appear in the token
  365. // and understands both the $UNIX and %WINDOWS% syntaxes.
  366. func replaceEnvVars(s string) string {
  367. s = replaceEnvReferences(s, "{%", "%}")
  368. s = replaceEnvReferences(s, "{$", "}")
  369. return s
  370. }
  371. // replaceEnvReferences performs the actual replacement of env variables
  372. // in s, given the placeholder start and placeholder end strings.
  373. func replaceEnvReferences(s, refStart, refEnd string) string {
  374. index := strings.Index(s, refStart)
  375. for index != -1 {
  376. endIndex := strings.Index(s[index:], refEnd)
  377. if endIndex == -1 {
  378. break
  379. }
  380. endIndex += index
  381. if endIndex > index+len(refStart) {
  382. ref := s[index : endIndex+len(refEnd)]
  383. s = strings.Replace(s, ref, os.Getenv(ref[len(refStart):len(ref)-len(refEnd)]), -1)
  384. } else {
  385. return s
  386. }
  387. index = strings.Index(s, refStart)
  388. }
  389. return s
  390. }
  391. // ServerBlock associates any number of keys (usually addresses
  392. // of some sort) with tokens (grouped by directive name).
  393. type ServerBlock struct {
  394. Keys []string
  395. Tokens map[string][]Token
  396. }
  397. func (p *parser) isSnippet() (bool, string) {
  398. keys := p.block.Keys
  399. // A snippet block is a single key with parens. Nothing else qualifies.
  400. if len(keys) == 1 && strings.HasPrefix(keys[0], "(") && strings.HasSuffix(keys[0], ")") {
  401. return true, strings.TrimSuffix(keys[0][1:], ")")
  402. }
  403. return false, ""
  404. }
  405. // read and store everything in a block for later replay.
  406. func (p *parser) snippetTokens() ([]Token, error) {
  407. // TODO: disallow imports in snippets for simplicity at import time
  408. // snippet must have curlies.
  409. err := p.openCurlyBrace()
  410. if err != nil {
  411. return nil, err
  412. }
  413. count := 1
  414. tokens := []Token{}
  415. for p.Next() {
  416. if p.Val() == "}" {
  417. count--
  418. if count == 0 {
  419. break
  420. }
  421. }
  422. if p.Val() == "{" {
  423. count++
  424. }
  425. tokens = append(tokens, p.tokens[p.cursor])
  426. }
  427. // make sure we're matched up
  428. if count != 0 {
  429. return nil, p.SyntaxErr("}")
  430. }
  431. return tokens, nil
  432. }