nginx-ui/server/tool/nginx/parse.go
2022-07-27 19:57:33 +08:00

144 lines
2.8 KiB
Go

package nginx
import (
"bufio"
"github.com/emirpasic/gods/stacks/linkedliststack"
"github.com/pkg/errors"
"os"
"strings"
"unicode"
)
const (
Server = "server"
Location = "location"
Upstream = "upstream"
CommentStart = "#"
Empty = ""
If = "if"
)
func matchParentheses(stack *linkedliststack.Stack, v int32) {
if v == '{' {
stack.Push(v)
} else if v == '}' {
// stack is not empty and the top is == '{'
if top, ok := stack.Peek(); ok && top == '{' {
stack.Pop()
} else {
// fail
stack.Push(v)
}
}
}
func parseDirective(scanner *bufio.Scanner) (d NgxDirective) {
text := strings.TrimSpace(scanner.Text())
// escape empty line or comment line
if len(text) < 1 {
return
}
if text[0] == '#' {
d.Directive = "#"
d.Params = strings.TrimLeft(text, "#")
return
}
if len(text) > 1 {
sep := len(text) - 1
for k, v := range text {
if unicode.IsSpace(v) {
sep = k
break
}
}
d.Directive = text[0:sep]
d.Params = text[sep:]
} else {
d.Directive = text
return
}
stack := linkedliststack.New()
if d.Directive == Server || d.Directive == Upstream || d.Directive == Location || d.Directive == If {
// { } in one line
// location = /.well-known/carddav { return 301 /remote.php/dav/; }
if strings.Contains(d.Params, "{") {
for _, v := range d.Params {
matchParentheses(stack, v)
}
if stack.Empty() {
return
}
}
// location ^~ /.well-known {
// location ^~ /.well-known
// {
// location ^~ /.well-known
//
// {
// { } not in one line
for scanner.Scan() {
text = strings.TrimSpace(scanner.Text())
// escape empty line
if text == "" {
continue
}
d.Params += "\n" + scanner.Text()
for _, v := range text {
matchParentheses(stack, v)
if stack.Empty() {
break
}
}
if stack.Empty() {
break
}
}
}
d.Params = strings.TrimSpace(d.Params)
return
}
func ParseNgxConfig(filename string) (c *NgxConfig, err error) {
file, err := os.Open(filename)
if err != nil {
return nil, errors.Wrap(err, "error open file in ParseNgxConfig")
}
defer file.Close()
scanner := bufio.NewScanner(file)
c = NewNgxConfig(filename)
for scanner.Scan() {
d := parseDirective(scanner)
paramsScanner := bufio.NewScanner(strings.NewReader(d.Params))
switch d.Directive {
case Server:
c.parseServer(paramsScanner)
case Upstream:
c.parseUpstream(paramsScanner)
case CommentStart:
c.commentQueue.Enqueue(d.Params)
case Empty:
continue
default:
c.Custom += d.Orig() + "\n"
}
}
if err = scanner.Err(); err != nil {
return nil, errors.Wrap(err, "error scanner in ParseNgxConfig")
}
// Attach the rest of the comments to the last server
if len(c.Servers) > 0 {
c.Servers[len(c.Servers)-1].Comments += c.commentQueue.DequeueAllComments()
}
return c, nil
}