github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/core/corehttp/corehttp.go (about)

     1  /*
     2  Package corehttp provides utilities for the webui, gateways, and other
     3  high-level HTTP interfaces to IPFS.
     4  */
     5  package corehttp
     6  
     7  import (
     8  	"fmt"
     9  	"net"
    10  	"net/http"
    11  	"time"
    12  
    13  	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
    14  	manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
    15  	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
    16  	core "github.com/ipfs/go-ipfs/core"
    17  	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
    18  )
    19  
    20  var log = eventlog.Logger("core/server")
    21  
    22  // ServeOption registers any HTTP handlers it provides on the given mux.
    23  // It returns the mux to expose to future options, which may be a new mux if it
    24  // is interested in mediating requests to future options, or the same mux
    25  // initially passed in if not.
    26  type ServeOption func(*core.IpfsNode, net.Listener, *http.ServeMux) (*http.ServeMux, error)
    27  
    28  // makeHandler turns a list of ServeOptions into a http.Handler that implements
    29  // all of the given options, in order.
    30  func makeHandler(n *core.IpfsNode, l net.Listener, options ...ServeOption) (http.Handler, error) {
    31  	topMux := http.NewServeMux()
    32  	mux := topMux
    33  	for _, option := range options {
    34  		var err error
    35  		mux, err = option(n, l, mux)
    36  		if err != nil {
    37  			return nil, err
    38  		}
    39  	}
    40  	return topMux, nil
    41  }
    42  
    43  // ListenAndServe runs an HTTP server listening at |listeningMultiAddr| with
    44  // the given serve options. The address must be provided in multiaddr format.
    45  //
    46  // TODO intelligently parse address strings in other formats so long as they
    47  // unambiguously map to a valid multiaddr. e.g. for convenience, ":8080" should
    48  // map to "/ip4/0.0.0.0/tcp/8080".
    49  func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...ServeOption) error {
    50  	addr, err := ma.NewMultiaddr(listeningMultiAddr)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	list, err := manet.Listen(addr)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	// we might have listened to /tcp/0 - lets see what we are listing on
    61  	addr = list.Multiaddr()
    62  	fmt.Printf("API server listening on %s\n", addr)
    63  
    64  	return Serve(n, list.NetListener(), options...)
    65  }
    66  
    67  func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error {
    68  	handler, err := makeHandler(node, lis, options...)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	addr, err := manet.FromNetAddr(lis.Addr())
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	// if the server exits beforehand
    79  	var serverError error
    80  	serverExited := make(chan struct{})
    81  
    82  	node.Process().Go(func(p goprocess.Process) {
    83  		serverError = http.Serve(lis, handler)
    84  		close(serverExited)
    85  	})
    86  
    87  	// wait for server to exit.
    88  	select {
    89  	case <-serverExited:
    90  
    91  	// if node being closed before server exits, close server
    92  	case <-node.Process().Closing():
    93  		log.Infof("server at %s terminating...", addr)
    94  
    95  		lis.Close()
    96  
    97  	outer:
    98  		for {
    99  			// wait until server exits
   100  			select {
   101  			case <-serverExited:
   102  				// if the server exited as we are closing, we really dont care about errors
   103  				serverError = nil
   104  				break outer
   105  			case <-time.After(5 * time.Second):
   106  				log.Infof("waiting for server at %s to terminate...", addr)
   107  			}
   108  		}
   109  	}
   110  
   111  	log.Infof("server at %s terminated", addr)
   112  	return serverError
   113  }