github.com/segakazzz/buffalo@v0.16.22-0.20210119082501-1f52048d3feb/server.go (about) 1 package buffalo 2 3 import ( 4 "context" 5 "net/http" 6 "os" 7 "strings" 8 "syscall" 9 10 "github.com/gobuffalo/buffalo/servers" 11 "github.com/gobuffalo/events" 12 "github.com/gobuffalo/packd" 13 "github.com/markbates/refresh/refresh/web" 14 "github.com/markbates/sigtx" 15 "github.com/segakazzz/buffalo/internal/takeon/github.com/markbates/errx" 16 ) 17 18 // Serve the application at the specified address/port and listen for OS 19 // interrupt and kill signals and will attempt to stop the application 20 // gracefully. This will also start the Worker process, unless WorkerOff is enabled. 21 func (a *App) Serve(srvs ...servers.Server) error { 22 a.Logger.Infof("Starting application at http://%s", a.Options.Addr) 23 24 payload := events.Payload{ 25 "app": a, 26 } 27 if err := events.EmitPayload(EvtAppStart, payload); err != nil { 28 return err 29 } 30 31 if len(srvs) == 0 { 32 if strings.HasPrefix(a.Options.Addr, "unix:") { 33 tcp, err := servers.UnixSocket(a.Options.Addr[5:]) 34 if err != nil { 35 return err 36 } 37 srvs = append(srvs, tcp) 38 } else { 39 srvs = append(srvs, servers.New()) 40 } 41 } 42 43 ctx, cancel := sigtx.WithCancel(a.Context, syscall.SIGTERM, os.Interrupt) 44 defer cancel() 45 46 go func() { 47 // gracefully shut down the application when the context is cancelled 48 <-ctx.Done() 49 a.Logger.Info("Shutting down application") 50 51 events.EmitError(EvtAppStop, ctx.Err(), payload) 52 53 if err := a.Stop(ctx.Err()); err != nil { 54 events.EmitError(EvtAppStopErr, err, payload) 55 a.Logger.Error(err) 56 } 57 58 if !a.WorkerOff { 59 // stop the workers 60 a.Logger.Info("Shutting down worker") 61 events.EmitPayload(EvtWorkerStop, payload) 62 if err := a.Worker.Stop(); err != nil { 63 events.EmitError(EvtWorkerStopErr, err, payload) 64 a.Logger.Error(err) 65 } 66 } 67 68 for _, s := range srvs { 69 if err := s.Shutdown(ctx); err != nil { 70 a.Logger.Error(err) 71 } 72 } 73 74 }() 75 76 // if configured to do so, start the workers 77 if !a.WorkerOff { 78 go func() { 79 events.EmitPayload(EvtWorkerStart, payload) 80 if err := a.Worker.Start(ctx); err != nil { 81 a.Stop(err) 82 } 83 }() 84 } 85 86 for _, s := range srvs { 87 s.SetAddr(a.Addr) 88 go func(s servers.Server) { 89 if err := s.Start(ctx, a); err != nil { 90 a.Stop(err) 91 } 92 }(s) 93 } 94 95 <-ctx.Done() 96 97 return a.Context.Err() 98 } 99 100 // Stop the application and attempt to gracefully shutdown 101 func (a *App) Stop(err error) error { 102 a.cancel() 103 if err != nil && errx.Unwrap(err) != context.Canceled { 104 a.Logger.Error(err) 105 return err 106 } 107 return nil 108 } 109 110 func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { 111 if r.Body != nil { 112 // convert the request's body to a packd.File which can be read N times 113 f, err := packd.NewFile("", r.Body) 114 if err == nil { 115 r.Body = f 116 } 117 } 118 ws := &Response{ 119 ResponseWriter: w, 120 } 121 if a.MethodOverride != nil { 122 a.MethodOverride(w, r) 123 } 124 if ok := a.processPreHandlers(ws, r); !ok { 125 return 126 } 127 128 r.URL.Path = a.normalizePath(r.URL.Path) 129 130 var h http.Handler = a.router 131 if a.Env == "development" { 132 h = web.ErrorChecker(h) 133 } 134 h.ServeHTTP(ws, r) 135 } 136 137 func (a *App) processPreHandlers(res http.ResponseWriter, req *http.Request) bool { 138 sh := func(h http.Handler) bool { 139 h.ServeHTTP(res, req) 140 if br, ok := res.(*Response); ok { 141 if br.Status > 0 || br.Size > 0 { 142 return false 143 } 144 } 145 return true 146 } 147 148 for _, ph := range a.PreHandlers { 149 if ok := sh(ph); !ok { 150 return false 151 } 152 } 153 154 last := http.Handler(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {})) 155 for _, ph := range a.PreWares { 156 last = ph(last) 157 if ok := sh(last); !ok { 158 return false 159 } 160 } 161 return true 162 } 163 164 func (a *App) normalizePath(path string) string { 165 if strings.HasSuffix(path, "/") { 166 return path 167 } 168 for _, p := range a.filepaths { 169 if p == "/" { 170 continue 171 } 172 if strings.HasPrefix(path, p) { 173 return path 174 } 175 } 176 return path + "/" 177 }