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  }