在 caddy.go 中定义着 Server 的接口,同时实现了优雅的退出。我们首先看图了解组织结构
简单看一下 Stopper 的接口
// Stopper is a type that can stop serving. The stop// does not necessarily have to be graceful.type Stopper interface {
// Stop stops the server. It blocks until the// server is completely stopped.
Stop() error
}
GracefulServer 包含 Stopper 的接口实现了优雅退出,这是拦截了 系统 signal 的信号之后执行的结果,意在意外中断的时候保存好需要保存的东西。
它同时包含着 WrapListener 函数。可以看出,他用来做中间件。
// WrapListener wraps a listener with the// listener middlewares configured for this// server, if any.
WrapListener(net.Listener) net.Listener
ServerType
最后看到不同 serverType 生成不同的 server
另外可以看到 这里最重要的 Instance 下面我们进一步查看 Instance 的代码
Instance
instance 是 Server 用来执行操作的实体。首先来看他的结构。它的代码在 主文件夹中的 caddy.go 中
type EventHook func(eventType EventName, eventInfo interface{})error
然后我们关注到如何注册,和图中的 caddy.EmitEvent
注册与分发
注册 EventHook
可以看到使用 eventHooks.LoadOrStore方法,不必赘述
funcRegisterEventHook(name string, hook EventHook){
if name == "" {
panic("event hook must have a name")
}
_, dup := eventHooks.LoadOrStore(name, hook)
if dup {
panic("hook named" + name + "already registered")
}
}
// EmitEvent executes the different hooks passing the EventType as an// argument. This is a blocking function. Hook developers should// use 'go' keyword if they don't want to block Caddy.funcEmitEvent(event EventName, info interface{}) {
eventHooks.Range(func(k, v interface{})bool {
err := v.(EventHook)(event, info)
if err != nil {
log.Printf("error on '%s' hook: %v", k.(string), err)
}
returntrue//注意这里返回的是 true
})
}
这里使用的 Range 函数,实际上是把事件信息给每一个上述提过 map 中的 EventHook 提供参数进行回调执行,按顺序调用,但是如果 传入函数返回 false ,迭代遍历执行就会中断。
// allTokens lexes the entire input, but does not parse it.// It returns all the tokens from the input, unstructured// and in order.funcallTokens(input io.Reader)([]Token, error) {
l := new(lexer)
err := l.load(input)
if err != nil {
returnnil, err
}
var tokens []Token
for l.next() {
tokens = append(tokens, l.token)
}
return tokens, nil
}
// next loads the next token into the lexer.
// A token is delimited by whitespace, unless
// the token starts with a quotes character (")
// in which case the token goes until the closing
// quotes (the enclosing quotes are not included).
// Inside quoted strings, quotes may be escaped
// with a preceding \ character. No other chars
// may be escaped. The rest of the line is skipped
// if a "#" character is read in. Returns true if
// a token was loaded; false otherwise.
func (l *lexer) next() bool {
var val []rune
var comment, quoted, escaped bool
makeToken := func() bool {
l.token.Text = string(val)
return true
}
for {
ch, _, err := l.reader.ReadRune()
if err != nil {
if len(val) > 0 {
return makeToken()
}
if err == io.EOF {
return false
}
panic(err)
}
if quoted {
if !escaped {
if ch == '\\' {
escaped = true
continue
} else if ch == '"' {
quoted = false
return makeToken()
}
}
if ch == '\n' {
l.line++
}
if escaped {
// only escape quotes
if ch != '"' {
val = append(val, '\\')
}
}
val = append(val, ch)
escaped = false
continue
}
if unicode.IsSpace(ch) {
if ch == '\r' {
continue
}
if ch == '\n' {
l.line++
comment = false
}
if len(val) > 0 {
return makeToken()
}
continue
}
if ch == '#' {
comment = true
}
if comment {
continue
}
if len(val) == 0 {
l.token = Token{Line: l.line}
if ch == '"' {
quoted = true
continue
}
}
val = append(val, ch)
}
}
理解了 next 函数,就很容易知道如何分析一块选项的 token 了,不过都是 next() 的包装函数罢了。
funcexecuteDirectives(inst *Instance, filename string,
directives []string, sblocks []caddyfile.ServerBlock, justValidate bool)error {
// map of server block ID to map of directive name to whatever.
storages := make(map[int]map[string]interface{})
// It is crucial that directives are executed in the proper order.// We loop with the directives on the outer loop so we execute// a directive for all server blocks before going to the next directive.// This is important mainly due to the parsing callbacks (below).for _, dir := range directives {
for i, sb := range sblocks {
var once sync.Once
if _, ok := storages[i]; !ok {
storages[i] = make(map[string]interface{})
}
for j, key := range sb.Keys {
// Execute directive if it is in the server blockif tokens, ok := sb.Tokens[dir]; ok {
controller := &Controller{
instance: inst,
Key: key,
Dispenser: caddyfile.NewDispenserTokens(filename, tokens),
OncePerServerBlock: func(f func()error) error {
var err error
once.Do(func() {
err = f()
})
return err
},
ServerBlockIndex: i,
ServerBlockKeyIndex: j,
ServerBlockKeys: sb.Keys,
ServerBlockStorage: storages[i][dir],
}
setup, err := DirectiveAction(inst.serverType, dir)
if err != nil {
return err
}
err = setup(controller)
if err != nil {
return err
}
storages[i][dir] = controller.ServerBlockStorage // persist for this server block
}
}
}
if !justValidate {
// See if there are any callbacks to execute after this directiveif allCallbacks, ok := parsingCallbacks[inst.serverType]; ok {
callbacks := allCallbacks[dir]
for _, callback := range callbacks {
if err := callback(inst.context); err != nil {
return err
}
}
}
}
}
returnnil
}
// DirectiveAction gets the action for directive dir of// server type serverType.funcDirectiveAction(serverType, dir string)(SetupFunc, error) {
if stypePlugins, ok := plugins[serverType]; ok {
if plugin, ok := stypePlugins[dir]; ok {
return plugin.Action, nil
}
}
if genericPlugins, ok := plugins[""]; ok {
if plugin, ok := genericPlugins[dir]; ok {
return plugin.Action, nil
}
}
returnnil, fmt.Errorf("no action found for directive '%s' with server type '%s' (missing a plugin?)",
dir, serverType)
}
// ErrorHandler handles HTTP errors (and errors from other middleware).type ErrorHandler struct {
Next httpserver.Handler
GenericErrorPage string// default error page filename
ErrorPages map[int]string// map of status code to filename
Log *httpserver.Logger
Debug bool// if true, errors are written out to client rather than to a log
}