// ToJSON converts caddyfile to its JSON representation. func ToJSON(caddyfile []byte) ([]byte, error) { var j Caddyfile serverBlocks, err := parse.ServerBlocks(filename, bytes.NewReader(caddyfile), false) if err != nil { return nil, err } for _, sb := range serverBlocks { block := ServerBlock{Body: make(map[string]interface{})} for _, host := range sb.HostList() { block.Hosts = append(block.Hosts, strings.TrimSuffix(host, ":")) } for dir, tokens := range sb.Tokens { disp := parse.NewDispenserTokens(filename, tokens) disp.Next() // the first token is the directive; skip it block.Body[dir] = constructLine(disp) } j = append(j, block) } result, err := json.Marshal(j) if err != nil { return nil, err } return result, nil }
// ToJSON converts caddyfile to its JSON representation. func ToJSON(caddyfile []byte) ([]byte, error) { var j Caddyfile serverBlocks, err := parse.ServerBlocks(filename, bytes.NewReader(caddyfile), false) if err != nil { return nil, err } for _, sb := range serverBlocks { block := ServerBlock{Body: [][]interface{}{}} // Fill up host list for _, host := range sb.HostList() { block.Hosts = append(block.Hosts, standardizeScheme(host)) } // Extract directives deterministically by sorting them var directives = make([]string, len(sb.Tokens)) for dir := range sb.Tokens { directives = append(directives, dir) } sort.Strings(directives) // Convert each directive's tokens into our JSON structure for _, dir := range directives { disp := parse.NewDispenserTokens(filename, sb.Tokens[dir]) for disp.Next() { block.Body = append(block.Body, constructLine(&disp)) } } // tack this block onto the end of the list j = append(j, block) } result, err := json.Marshal(j) if err != nil { return nil, err } return result, nil }
// loadConfigsUpToIncludingTLS loads the configs from input with name filename and returns them, // the parsed server blocks, the index of the last directive it processed, and an error (if any). func loadConfigsUpToIncludingTLS(filename string, input io.Reader) ([]server.Config, []parse.ServerBlock, int, error) { var configs []server.Config // Each server block represents similar hosts/addresses, since they // were grouped together in the Caddyfile. serverBlocks, err := parse.ServerBlocks(filename, input, true) if err != nil { return nil, nil, 0, err } if len(serverBlocks) == 0 { newInput := DefaultInput() serverBlocks, err = parse.ServerBlocks(newInput.Path(), bytes.NewReader(newInput.Body()), true) if err != nil { return nil, nil, 0, err } } var lastDirectiveIndex int // we set up directives in two parts; this stores where we left off // Iterate each server block and make a config for each one, // executing the directives that were parsed in order up to the tls // directive; this is because we must activate Let's Encrypt. for i, sb := range serverBlocks { onces := makeOnces() storages := makeStorages() for j, addr := range sb.Addresses { config := server.Config{ Host: addr.Host, Port: addr.Port, Scheme: addr.Scheme, Root: Root, Middleware: make(map[string][]middleware.Middleware), ConfigFile: filename, AppName: AppName, AppVersion: AppVersion, } // It is crucial that directives are executed in the proper order. for k, dir := range directiveOrder { // Execute directive if it is in the server block if tokens, ok := sb.Tokens[dir.name]; ok { // Each setup function gets a controller, from which setup functions // get access to the config, tokens, and other state information useful // to set up its own host only. controller := &setup.Controller{ Config: &config, Dispenser: parse.NewDispenserTokens(filename, tokens), OncePerServerBlock: func(f func() error) error { var err error onces[dir.name].Do(func() { err = f() }) return err }, ServerBlockIndex: i, ServerBlockHostIndex: j, ServerBlockHosts: sb.HostList(), ServerBlockStorage: storages[dir.name], } // execute setup function and append middleware handler, if any midware, err := dir.setup(controller) if err != nil { return nil, nil, lastDirectiveIndex, err } if midware != nil { // TODO: For now, we only support the default path scope / config.Middleware["/"] = append(config.Middleware["/"], midware) } storages[dir.name] = controller.ServerBlockStorage // persist for this server block } // Stop after TLS setup, since we need to activate Let's Encrypt before continuing; // it makes some changes to the configs that middlewares might want to know about. if dir.name == "tls" { lastDirectiveIndex = k break } } configs = append(configs, config) } } return configs, serverBlocks, lastDirectiveIndex, nil }
// loadConfigs reads input (named filename) and parses it, returning the // server configurations in the order they appeared in the input. As part // of this, it activates Let's Encrypt for the configs that are produced. // Thus, the returned configs are already optimally configured optimally // for HTTPS. func loadConfigs(filename string, input io.Reader) ([]server.Config, error) { var configs []server.Config // Each server block represents similar hosts/addresses, since they // were grouped together in the Caddyfile. serverBlocks, err := parse.ServerBlocks(filename, input, true) if err != nil { return nil, err } if len(serverBlocks) == 0 { newInput := DefaultInput() serverBlocks, err = parse.ServerBlocks(newInput.Path(), bytes.NewReader(newInput.Body()), true) if err != nil { return nil, err } } var lastDirectiveIndex int // we set up directives in two parts; this stores where we left off // Iterate each server block and make a config for each one, // executing the directives that were parsed in order up to the tls // directive; this is because we must activate Let's Encrypt. for i, sb := range serverBlocks { onces := makeOnces() storages := makeStorages() for j, addr := range sb.Addresses { config := server.Config{ Host: addr.Host, Port: addr.Port, Root: Root, Middleware: make(map[string][]middleware.Middleware), ConfigFile: filename, AppName: AppName, AppVersion: AppVersion, } // It is crucial that directives are executed in the proper order. for k, dir := range directiveOrder { // Execute directive if it is in the server block if tokens, ok := sb.Tokens[dir.name]; ok { // Each setup function gets a controller, from which setup functions // get access to the config, tokens, and other state information useful // to set up its own host only. controller := &setup.Controller{ Config: &config, Dispenser: parse.NewDispenserTokens(filename, tokens), OncePerServerBlock: func(f func() error) error { var err error onces[dir.name].Do(func() { err = f() }) return err }, ServerBlockIndex: i, ServerBlockHostIndex: j, ServerBlockHosts: sb.HostList(), ServerBlockStorage: storages[dir.name], } // execute setup function and append middleware handler, if any midware, err := dir.setup(controller) if err != nil { return nil, err } if midware != nil { // TODO: For now, we only support the default path scope / config.Middleware["/"] = append(config.Middleware["/"], midware) } storages[dir.name] = controller.ServerBlockStorage // persist for this server block } // Stop after TLS setup, since we need to activate Let's Encrypt before continuing; // it makes some changes to the configs that middlewares might want to know about. if dir.name == "tls" { lastDirectiveIndex = k break } } configs = append(configs, config) } } // Now we have all the configs, but they have only been set up to the // point of tls. We need to activate Let's Encrypt before setting up // the rest of the middlewares so they have correct information regarding // TLS configuration, if necessary. (this call is append-only, so our // iterations below shouldn't be affected) if !IsRestart() && !Quiet { fmt.Print("Activating privacy features...") } configs, err = letsencrypt.Activate(configs) if err != nil { if !Quiet { fmt.Println() } return nil, err } if !IsRestart() && !Quiet { fmt.Println(" done.") } // Finish setting up the rest of the directives, now that TLS is // optimally configured. These loops are similar to above except // we don't iterate all the directives from the beginning and we // don't create new configs. configIndex := -1 for i, sb := range serverBlocks { onces := makeOnces() storages := makeStorages() for j := range sb.Addresses { configIndex++ for k := lastDirectiveIndex + 1; k < len(directiveOrder); k++ { dir := directiveOrder[k] if tokens, ok := sb.Tokens[dir.name]; ok { controller := &setup.Controller{ Config: &configs[configIndex], Dispenser: parse.NewDispenserTokens(filename, tokens), OncePerServerBlock: func(f func() error) error { var err error onces[dir.name].Do(func() { err = f() }) return err }, ServerBlockIndex: i, ServerBlockHostIndex: j, ServerBlockHosts: sb.HostList(), ServerBlockStorage: storages[dir.name], } midware, err := dir.setup(controller) if err != nil { return nil, err } if midware != nil { // TODO: For now, we only support the default path scope / configs[configIndex].Middleware["/"] = append(configs[configIndex].Middleware["/"], midware) } storages[dir.name] = controller.ServerBlockStorage // persist for this server block } } } } return configs, nil }