github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/api/server.go (about) 1 package api 2 3 import ( 4 "fmt" 5 "io" 6 "net" 7 "net/http" 8 "os" 9 "os/signal" 10 "strings" 11 "sync" 12 13 "github.com/NebulousLabs/Sia/build" 14 "github.com/NebulousLabs/Sia/modules" 15 ) 16 17 // A Server is essentially a collection of modules and an API server to talk 18 // to them all. 19 type Server struct { 20 cs modules.ConsensusSet 21 explorer modules.Explorer 22 gateway modules.Gateway 23 host modules.Host 24 miner modules.Miner 25 renter modules.Renter 26 tpool modules.TransactionPool 27 wallet modules.Wallet 28 29 apiServer *http.Server 30 listener net.Listener 31 requiredUserAgent string 32 33 // wg is used to block Close() from returning until Serve() has finished. A 34 // WaitGroup is used instead of a chan struct{} so that Close() can be called 35 // without necessarily calling Serve() first. 36 wg sync.WaitGroup 37 } 38 39 // NewServer creates a new API server from the provided modules. 40 func NewServer(APIaddr string, requiredUserAgent string, cs modules.ConsensusSet, e modules.Explorer, g modules.Gateway, h modules.Host, m modules.Miner, r modules.Renter, tp modules.TransactionPool, w modules.Wallet) (*Server, error) { 41 l, err := net.Listen("tcp", APIaddr) 42 if err != nil { 43 return nil, err 44 } 45 46 srv := &Server{ 47 cs: cs, 48 explorer: e, 49 gateway: g, 50 host: h, 51 miner: m, 52 renter: r, 53 tpool: tp, 54 wallet: w, 55 56 listener: l, 57 requiredUserAgent: requiredUserAgent, 58 } 59 60 // Register API handlers 61 srv.initAPI() 62 63 return srv, nil 64 } 65 66 // Serve listens for and handles API calls. It is a blocking function. 67 func (srv *Server) Serve() error { 68 // Block the Close() method until Serve() has finished. 69 srv.wg.Add(1) 70 defer srv.wg.Done() 71 72 // stop the server if a kill signal is caught 73 sigChan := make(chan os.Signal, 1) 74 signal.Notify(sigChan, os.Interrupt, os.Kill) 75 defer signal.Stop(sigChan) 76 stop := make(chan struct{}) 77 defer close(stop) 78 go func() { 79 select { 80 case <-sigChan: 81 fmt.Println("\rCaught stop signal, quitting...") 82 srv.Close() 83 case <-stop: 84 // Don't leave a dangling goroutine. 85 } 86 }() 87 88 // The server will run until an error is encountered or the listener is 89 // closed, via either the Close method or the signal handling above. 90 // Closing the listener will result in the benign error handled below. 91 err := srv.apiServer.Serve(srv.listener) 92 if err != nil && !strings.HasSuffix(err.Error(), "use of closed network connection") { 93 return err 94 } 95 return nil 96 } 97 98 // Close closes the Server's listener, causing the HTTP server to shut down. 99 func (srv *Server) Close() error { 100 var errs []error 101 102 // Close the listener, which will cause Server.Serve() to return. 103 if err := srv.listener.Close(); err != nil { 104 errs = append(errs, fmt.Errorf("listener.Close failed: %v", err)) 105 } 106 107 // Wait for Server.Serve() to exit. We wait so that it's guaranteed that the 108 // server has completely closed after Close() returns. This is particularly 109 // useful during testing so that we don't exit a test before Serve() finishes. 110 srv.wg.Wait() 111 112 // Safely close each module. 113 // TODO: close transaction pool 114 115 // wallet has special closing mechanics 116 if srv.wallet != nil { 117 // TODO: close wallet and lock the wallet in the wallet's Close method. 118 if srv.wallet.Unlocked() { 119 if err := srv.wallet.Lock(); err != nil { 120 errs = append(errs, fmt.Errorf("wallet.Lock failed: %v", err)) 121 } 122 } 123 } 124 125 mods := []struct { 126 name string 127 c io.Closer 128 }{ 129 {"host", srv.host}, 130 {"renter", srv.renter}, 131 {"explorer", srv.explorer}, 132 {"miner", srv.miner}, 133 {"consensus", srv.cs}, 134 {"gateway", srv.gateway}, 135 } 136 for _, mod := range mods { 137 if mod.c != nil { 138 if err := mod.c.Close(); err != nil { 139 errs = append(errs, fmt.Errorf("%v.Close failed: %v", mod.name, err)) 140 } 141 } 142 } 143 144 return build.JoinErrors(errs, "\n") 145 }