parse.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. package nginx
  2. import (
  3. "bufio"
  4. "github.com/emirpasic/gods/stacks/linkedliststack"
  5. "github.com/pkg/errors"
  6. "os"
  7. "strings"
  8. "unicode"
  9. )
  10. const (
  11. Server = "server"
  12. Location = "location"
  13. Upstream = "upstream"
  14. CommentStart = "#"
  15. Empty = ""
  16. )
  17. func matchParentheses(stack *linkedliststack.Stack, v int32) {
  18. if v == '{' {
  19. stack.Push(v)
  20. } else if v == '}' {
  21. // stack is not empty and the top is == '{'
  22. if top, ok := stack.Peek(); ok && top == '{' {
  23. stack.Pop()
  24. } else {
  25. // fail
  26. stack.Push(v)
  27. }
  28. }
  29. }
  30. func parseDirective(scanner *bufio.Scanner) (d NgxDirective) {
  31. text := strings.TrimSpace(scanner.Text())
  32. // escape empty line or comment line
  33. if len(text) < 1 {
  34. return
  35. }
  36. if text[0] == '#' {
  37. d.Directive = "#"
  38. d.Params = strings.TrimLeft(text, "#")
  39. return
  40. }
  41. sep := len(text) - 1
  42. for k, v := range text {
  43. if unicode.IsSpace(v) {
  44. sep = k
  45. break
  46. }
  47. }
  48. d.Directive = text[0:sep]
  49. d.Params = text[sep:]
  50. stack := linkedliststack.New()
  51. if d.Directive == Server || d.Directive == Upstream || d.Directive == Location {
  52. // { } in one line
  53. // location = /.well-known/carddav { return 301 /remote.php/dav/; }
  54. if strings.Contains(d.Params, "{") {
  55. for _, v := range d.Params {
  56. matchParentheses(stack, v)
  57. }
  58. if stack.Empty() {
  59. return
  60. }
  61. }
  62. // location ^~ /.well-known {
  63. // location ^~ /.well-known
  64. // {
  65. // location ^~ /.well-known
  66. //
  67. // {
  68. // { } not in one line
  69. for scanner.Scan() {
  70. text = strings.TrimSpace(scanner.Text())
  71. // escape empty line
  72. if text == "" {
  73. continue
  74. }
  75. d.Params += "\n" + scanner.Text()
  76. for _, v := range text {
  77. matchParentheses(stack, v)
  78. if stack.Empty() {
  79. break
  80. }
  81. }
  82. if stack.Empty() {
  83. break
  84. }
  85. }
  86. }
  87. d.Params = strings.TrimSpace(d.Params)
  88. return
  89. }
  90. func ParseNgxConfig(filename string) (c *NgxConfig, err error) {
  91. file, err := os.Open(filename)
  92. if err != nil {
  93. return nil, errors.Wrap(err, "error open file in ParseNgxConfig")
  94. }
  95. defer file.Close()
  96. scanner := bufio.NewScanner(file)
  97. c = NewNgxConfig(filename)
  98. for scanner.Scan() {
  99. d := parseDirective(scanner)
  100. paramsScanner := bufio.NewScanner(strings.NewReader(d.Params))
  101. switch d.Directive {
  102. case Server:
  103. c.parseServer(paramsScanner)
  104. case Upstream:
  105. c.parseUpstream(paramsScanner)
  106. case CommentStart:
  107. c.commentQueue.Enqueue(d.Params)
  108. }
  109. }
  110. if err = scanner.Err(); err != nil {
  111. return nil, errors.Wrap(err, "error scanner in ParseNgxConfig")
  112. }
  113. return c, nil
  114. }