github.com/mholt/caddy-l4@v0.0.0-20241104153248-ec8fae209322/layer4/app.go (about) 1 // Copyright 2020 Matthew Holt 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package layer4 16 17 import ( 18 "fmt" 19 "net" 20 21 "github.com/caddyserver/caddy/v2" 22 "go.uber.org/zap" 23 ) 24 25 func init() { 26 caddy.RegisterModule(&App{}) 27 } 28 29 // App is a Caddy app that operates closest to layer 4 of the OSI model. 30 type App struct { 31 // Servers are the servers to create. The key of each server must be 32 // a unique name identifying the server for your own convenience; 33 // the order of servers does not matter. 34 Servers map[string]*Server `json:"servers,omitempty"` 35 36 listeners []net.Listener 37 packetConns []net.PacketConn 38 logger *zap.Logger 39 ctx caddy.Context 40 } 41 42 // CaddyModule returns the Caddy module information. 43 func (*App) CaddyModule() caddy.ModuleInfo { 44 return caddy.ModuleInfo{ 45 ID: "layer4", 46 New: func() caddy.Module { return new(App) }, 47 } 48 } 49 50 // Provision sets up the app. 51 func (a *App) Provision(ctx caddy.Context) error { 52 a.ctx = ctx 53 a.logger = ctx.Logger() 54 55 for srvName, srv := range a.Servers { 56 err := srv.Provision(ctx, a.logger) 57 if err != nil { 58 return fmt.Errorf("server '%s': %v", srvName, err) 59 } 60 } 61 62 return nil 63 } 64 65 // Start starts the app. 66 func (a *App) Start() error { 67 for _, s := range a.Servers { 68 for _, addr := range s.listenAddrs { 69 listeners, err := addr.ListenAll(a.ctx, net.ListenConfig{}) 70 if err != nil { 71 return err 72 } 73 for _, lnAny := range listeners { 74 var lnAddr string 75 switch ln := lnAny.(type) { 76 case net.Listener: 77 a.listeners = append(a.listeners, ln) 78 lnAddr = caddy.JoinNetworkAddress(ln.Addr().Network(), ln.Addr().String(), "") 79 go func(s *Server, ln net.Listener) { 80 _ = s.serve(ln) 81 }(s, ln) 82 case net.PacketConn: 83 a.packetConns = append(a.packetConns, ln) 84 lnAddr = caddy.JoinNetworkAddress(ln.LocalAddr().Network(), ln.LocalAddr().String(), "") 85 go func(s *Server, pc net.PacketConn) { 86 _ = s.servePacket(pc) 87 }(s, ln) 88 } 89 s.logger.Debug("listening", zap.String("address", lnAddr)) 90 } 91 } 92 } 93 return nil 94 } 95 96 // Stop stops the servers and closes all listeners. 97 func (a *App) Stop() error { 98 for _, pc := range a.packetConns { 99 err := pc.Close() 100 if err != nil { 101 a.logger.Error("closing packet listener", 102 zap.String("network", pc.LocalAddr().Network()), 103 zap.String("address", pc.LocalAddr().String()), 104 zap.Error(err)) 105 } 106 } 107 for _, ln := range a.listeners { 108 err := ln.Close() 109 if err != nil { 110 a.logger.Error("closing listener", 111 zap.String("network", ln.Addr().Network()), 112 zap.String("address", ln.Addr().String()), 113 zap.Error(err)) 114 } 115 } 116 return nil 117 } 118 119 // Interface guard 120 var _ caddy.App = (*App)(nil)