parse.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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 Caddyfile rather than current working directory (issue #867)
  220. // and then use glob to get list of matching filenames
  221. absFile, err := filepath.Abs(p.Dispenser.filename)
  222. if err != nil {
  223. return p.Errf("Failed to get absolute path of file: %s: %v", p.Dispenser.filename, err)
  224. }
  225. var matches []string
  226. var globPattern string
  227. if !filepath.IsAbs(importPattern) {
  228. globPattern = filepath.Join(filepath.Dir(absFile), importPattern)
  229. } else {
  230. globPattern = importPattern
  231. }
  232. matches, err = filepath.Glob(globPattern)
  233. if err != nil {
  234. return p.Errf("Failed to use import pattern %s: %v", importPattern, err)
  235. }
  236. if len(matches) == 0 {
  237. if strings.Contains(globPattern, "*") {
  238. log.Printf("[WARNING] No files matching import pattern: %s", importPattern)
  239. } else {
  240. return p.Errf("File to import not found: %s", importPattern)
  241. }
  242. }
  243. // collect all the imported tokens
  244. for _, importFile := range matches {
  245. newTokens, err := p.doSingleImport(importFile)
  246. if err != nil {
  247. return err
  248. }
  249. var importLine int
  250. for i, token := range newTokens {
  251. if token.Text == "import" {
  252. importLine = token.Line
  253. continue
  254. }
  255. if token.Line == importLine {
  256. var abs string
  257. if filepath.IsAbs(token.Text) {
  258. abs = token.Text
  259. } else if !filepath.IsAbs(importFile) {
  260. abs = filepath.Join(filepath.Dir(absFile), token.Text)
  261. } else {
  262. abs = filepath.Join(filepath.Dir(importFile), token.Text)
  263. }
  264. newTokens[i] = Token{
  265. Text: abs,
  266. Line: token.Line,
  267. File: token.File,
  268. }
  269. }
  270. }
  271. importedTokens = append(importedTokens, newTokens...)
  272. }
  273. }
  274. // splice the imported tokens in the place of the import statement
  275. // and rewind cursor so Next() will land on first imported token
  276. p.tokens = append(tokensBefore, append(importedTokens, tokensAfter...)...)
  277. p.cursor--
  278. return nil
  279. }
  280. // doSingleImport lexes the individual file at importFile and returns
  281. // its tokens or an error, if any.
  282. func (p *parser) doSingleImport(importFile string) ([]Token, error) {
  283. file, err := os.Open(importFile)
  284. if err != nil {
  285. return nil, p.Errf("Could not import %s: %v", importFile, err)
  286. }
  287. defer file.Close()
  288. if info, err := file.Stat(); err != nil {
  289. return nil, p.Errf("Could not import %s: %v", importFile, err)
  290. } else if info.IsDir() {
  291. return nil, p.Errf("Could not import %s: is a directory", importFile)
  292. }
  293. importedTokens, err := allTokens(file)
  294. if err != nil {
  295. return nil, p.Errf("Could not read tokens while importing %s: %v", importFile, err)
  296. }
  297. // Tack the file path onto these tokens so errors show the imported file's name
  298. // (we use full, absolute path to avoid bugs: issue #1892)
  299. filename, err := filepath.Abs(importFile)
  300. if err != nil {
  301. return nil, p.Errf("Failed to get absolute path of file: %s: %v", p.Dispenser.filename, err)
  302. }
  303. for i := 0; i < len(importedTokens); i++ {
  304. importedTokens[i].File = filename
  305. }
  306. return importedTokens, nil
  307. }
  308. // directive collects tokens until the directive's scope
  309. // closes (either end of line or end of curly brace block).
  310. // It expects the currently-loaded token to be a directive
  311. // (or } that ends a server block). The collected tokens
  312. // are loaded into the current server block for later use
  313. // by directive setup functions.
  314. func (p *parser) directive() error {
  315. dir := p.Val()
  316. nesting := 0
  317. // TODO: More helpful error message ("did you mean..." or "maybe you need to install its server type")
  318. if !p.validDirective(dir) {
  319. return p.Errf("Unknown directive '%s'", dir)
  320. }
  321. // The directive itself is appended as a relevant token
  322. p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])
  323. for p.Next() {
  324. if p.Val() == "{" {
  325. nesting++
  326. } else if p.isNewLine() && nesting == 0 {
  327. p.cursor-- // read too far
  328. break
  329. } else if p.Val() == "}" && nesting > 0 {
  330. nesting--
  331. } else if p.Val() == "}" && nesting == 0 {
  332. return p.Err("Unexpected '}' because no matching opening brace")
  333. }
  334. p.tokens[p.cursor].Text = replaceEnvVars(p.tokens[p.cursor].Text)
  335. p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])
  336. }
  337. if nesting > 0 {
  338. return p.EOFErr()
  339. }
  340. return nil
  341. }
  342. // openCurlyBrace expects the current token to be an
  343. // opening curly brace. This acts like an assertion
  344. // because it returns an error if the token is not
  345. // a opening curly brace. It does NOT advance the token.
  346. func (p *parser) openCurlyBrace() error {
  347. if p.Val() != "{" {
  348. return p.SyntaxErr("{")
  349. }
  350. return nil
  351. }
  352. // closeCurlyBrace expects the current token to be
  353. // a closing curly brace. This acts like an assertion
  354. // because it returns an error if the token is not
  355. // a closing curly brace. It does NOT advance the token.
  356. func (p *parser) closeCurlyBrace() error {
  357. if p.Val() != "}" {
  358. return p.SyntaxErr("}")
  359. }
  360. return nil
  361. }
  362. // validDirective returns true if dir is in p.validDirectives.
  363. func (p *parser) validDirective(dir string) bool {
  364. if p.validDirectives == nil {
  365. return true
  366. }
  367. for _, d := range p.validDirectives {
  368. if d == dir {
  369. return true
  370. }
  371. }
  372. return false
  373. }
  374. // replaceEnvVars replaces environment variables that appear in the token
  375. // and understands both the $UNIX and %WINDOWS% syntaxes.
  376. func replaceEnvVars(s string) string {
  377. s = replaceEnvReferences(s, "{%", "%}")
  378. s = replaceEnvReferences(s, "{$", "}")
  379. return s
  380. }
  381. // replaceEnvReferences performs the actual replacement of env variables
  382. // in s, given the placeholder start and placeholder end strings.
  383. func replaceEnvReferences(s, refStart, refEnd string) string {
  384. index := strings.Index(s, refStart)
  385. for index != -1 {
  386. endIndex := strings.Index(s, refEnd)
  387. if endIndex != -1 {
  388. ref := s[index : endIndex+len(refEnd)]
  389. s = strings.Replace(s, ref, os.Getenv(ref[len(refStart):len(ref)-len(refEnd)]), -1)
  390. } else {
  391. return s
  392. }
  393. index = strings.Index(s, refStart)
  394. }
  395. return s
  396. }
  397. // ServerBlock associates any number of keys (usually addresses
  398. // of some sort) with tokens (grouped by directive name).
  399. type ServerBlock struct {
  400. Keys []string
  401. Tokens map[string][]Token
  402. }
  403. func (p *parser) isSnippet() (bool, string) {
  404. keys := p.block.Keys
  405. // A snippet block is a single key with parens. Nothing else qualifies.
  406. if len(keys) == 1 && strings.HasPrefix(keys[0], "(") && strings.HasSuffix(keys[0], ")") {
  407. return true, strings.TrimSuffix(keys[0][1:], ")")
  408. }
  409. return false, ""
  410. }
  411. // read and store everything in a block for later replay.
  412. func (p *parser) snippetTokens() ([]Token, error) {
  413. // TODO: disallow imports in snippets for simplicity at import time
  414. // snippet must have curlies.
  415. err := p.openCurlyBrace()
  416. if err != nil {
  417. return nil, err
  418. }
  419. count := 1
  420. tokens := []Token{}
  421. for p.Next() {
  422. if p.Val() == "}" {
  423. count--
  424. if count == 0 {
  425. break
  426. }
  427. }
  428. if p.Val() == "{" {
  429. count++
  430. }
  431. tokens = append(tokens, p.tokens[p.cursor])
  432. }
  433. // make sure we're matched up
  434. if count != 0 {
  435. return nil, p.SyntaxErr("}")
  436. }
  437. return tokens, nil
  438. }