caddyserver 包含了一个强大的adapter 架构设计,我们可以方便的进行 caddyserver 扩展
nginx 扩展的处理核心也是基于adapter 模块扩展的,通过解析nginx.conf 文件,然后转换为json 格式内容
参考处理
func init() {
// 注册Adapter
caddyconfig.RegisterAdapter("nginx", Adapter{})
}
const ErrUnrecognized = "unrecognized or unsupported nginx directive"
const ErrNamedLocation = "named locations marked by @ are unnsupported"
const ErrExpiresAtTime = "usage of `expires @time` is not supported"
// Adapter adapts NGINX config to Caddy JSON.
type Adapter struct{}
// 解析nginx 配置并 转换为json 格式
// Adapt converts the NGINX config in body to Caddy JSON.
func (Adapter) Adapt(body []byte, options map[string]interface{}) ([]byte, []caddyconfig.Warning, error) {
filename := "nginx.conf"
if v, ok := options["filename"].(string); ok {
filename = v
filename, _ = filepath.Abs(filename)
}
// 解析nginx 的关键token
tokens := tokenize(body, filename)
// 解析nginx 指令
dirs, err := parse(tokens)
if err != nil {
return nil, nil, fmt.Errorf("parsing: %v", err)
}
ss := setupState{
servers: make(map[string]*caddyhttp.Server),
}
warnings, err := ss.mainContext(dirs)
if err != nil {
return nil, nil, err
}
httpApp := caddyhttp.App{
Servers: ss.servers,
}
ss.mainConfig.AppsRaw = map[string]json.RawMessage{
"http": caddyconfig.JSON(httpApp, &warnings),
}
result, err := json.Marshal(ss.mainConfig)
return result, warnings, err
}
nginx 解析处理
parse.go 以及lexer.go 中处理上还是值得学习的,部分应该也参考了部分社区的golang nginx 解析工具
转换caddy 配置处理,实际解析都会处理为标准的caddy.Config
func (ss *setupState) mainContext(dirs []Directive) ([]caddyconfig.Warning, error) {
var warnings []caddyconfig.Warning
for _, dir := range dirs {
var warns []caddyconfig.Warning
var err error
switch dir.Name() {
case "http":
warns, err = ss.httpContext(dir.Block)
default:
warns = []caddyconfig.Warning{
{
File: dir.File,
Line: dir.Line,
Directive: dir.Name(),
Message: ErrUnrecognized,
},
}
}
warnings = append(warnings, warns...)
if err != nil {
return warnings, err
}
}
return warnings, nil
}
func (ss *setupState) httpContext(dirs []Directive) ([]caddyconfig.Warning, error) {
var warnings []caddyconfig.Warning
for _, dir := range dirs {
var warns []caddyconfig.Warning
var err error
switch dir.Name() {
case "index":
for k, d := range dirs {
if d.Name() == "server" {
dirs[k].Block = append(d.Block, dir)
}
}
case "server":
warns, err = ss.serverContext(dir.Block)
case "upstream":
up, w, err := ss.upstreamContext(dir.Block)
warns = append(warns, w...)
if err != nil {
return warns, err
}
if ss.upstreams == nil {
ss.upstreams = make(map[string]Upstream)
}
ss.upstreams[dir.Param(1)] = up
default:
warns = []caddyconfig.Warning{
{
File: dir.File,
Line: dir.Line,
Directive: dir.Name(),
Message: ErrUnrecognized,
},
}
}
warnings = append(warnings, warns...)
if err != nil {
return warnings, err
}
}
return warnings, nil
}
server 处理的 (server.go 中)会转换为标准caddy 配置的监听,路由等处理,详细的可以参考
说明
caddyserver nginx adaper 核心还是将nignx 配置转换为json 格式,之后caddy 会按照标准 adaper 模式进行加载,注意实际使用的时候需要自己构建
可以使用xcaddy 构建工具
参考资料
nginxadapter.go
https://github.com/caddyserver/nginx-adapter
https://caddyserver.com/docs/json/apps/http/
https://caddyserver.com/docs/config-adapters
https://caddyserver.com/docs/extending-caddy/config-adapters
https://caddyserver.com/docs/architecture
https://github.com/recoye/config
https://github.com/yangchenxing/go-nginx-conf-parser
https://www.nginx.com/resources/wiki/start/topics/examples/full/
https://github.com/caddyserver/xcaddy