| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 | 
							- /*
 
-  * Public Domain Software
 
-  *
 
-  * I (Matthias Ladkau) am the author of the source code in this file.
 
-  * I have placed the source code in this file in the public domain.
 
-  *
 
-  * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
 
-  */
 
- /*
 
- Package httputil contains a HTTP/HTTPS Server which can be stopped via signals
 
- or a Shutdown() call.
 
- */
 
- package httputil
 
- import (
 
- 	"crypto/tls"
 
- 	"errors"
 
- 	"fmt"
 
- 	"net"
 
- 	"net/http"
 
- 	"os"
 
- 	"os/signal"
 
- 	"strings"
 
- 	"sync"
 
- 	"syscall"
 
- 	"time"
 
- )
 
- /*
 
- HTTPServer data structure
 
- */
 
- type HTTPServer struct {
 
- 	signalling chan os.Signal    // Channel for receiving signals
 
- 	LastError  error             // Last recorded error
 
- 	Running    bool              // Flag if the server is running
 
- 	listener   signalTCPListener // TCP listener of the server
 
- }
 
- /*
 
- Shutdown sends a shutdown signal.
 
- */
 
- func (hs *HTTPServer) Shutdown() {
 
- 	if hs.signalling != nil {
 
- 		hs.signalling <- syscall.SIGINT
 
- 	}
 
- }
 
- /*
 
- RunHTTPServer starts a HTTP Server which can be stopped via ^C (Control-C).
 
- It is assumed that all routes have been added prior to this call.
 
- laddr should be the local address which should be given to net.Listen.
 
- wgStatus is an optional wait group which will be notified once the server is listening
 
- and once the server has shutdown.
 
- This function will not return unless the server is shutdown.
 
- */
 
- func (hs *HTTPServer) RunHTTPServer(laddr string, wgStatus *sync.WaitGroup) error {
 
- 	hs.Running = false
 
- 	// Create normal TCP listener
 
- 	originalListener, err := net.Listen("tcp", laddr)
 
- 	if err != nil {
 
- 		hs.LastError = err
 
- 		if wgStatus != nil {
 
- 			wgStatus.Done()
 
- 		}
 
- 		return err
 
- 	}
 
- 	// Wrap listener in a signal aware listener
 
- 	sl := newSignalTCPListener(originalListener, originalListener.(*net.TCPListener), wgStatus)
 
- 	return hs.runServer(sl, wgStatus)
 
- }
 
- /*
 
- RunHTTPSServer starts a HTTPS Server which can be stopped via ^C (Control-C).
 
- It is assumed that all routes have been added prior to this call.
 
- keypath should be set to a path containing the TLS certificate and key.
 
- certFile should be the file containing the TLS certificate.
 
- keyFile should be the file containing the private key for the TLS connection.
 
- laddr should be the local address which should be given to net.Listen.
 
- wgStatus is an optional wait group which will be notified once the server is listening
 
- and once the server has shutdown.
 
- This function will not return unless the server is shutdown.
 
- */
 
- func (hs *HTTPServer) RunHTTPSServer(keypath string, certFile string, keyFile string,
 
- 	laddr string, wgStatus *sync.WaitGroup) error {
 
- 	// Check parameters
 
- 	if keypath != "" && !strings.HasSuffix(keypath, "/") {
 
- 		keypath += "/"
 
- 	}
 
- 	// Load key pair and create a TLS config
 
- 	cert, err := tls.LoadX509KeyPair(keypath+certFile, keypath+keyFile)
 
- 	if err != nil {
 
- 		hs.LastError = err
 
- 		if wgStatus != nil {
 
- 			wgStatus.Done()
 
- 		}
 
- 		return err
 
- 	}
 
- 	hs.Running = false
 
- 	// Create normal TCP listener
 
- 	originalListener, err := net.Listen("tcp", laddr)
 
- 	if err != nil {
 
- 		hs.LastError = err
 
- 		if wgStatus != nil {
 
- 			wgStatus.Done()
 
- 		}
 
- 		return err
 
- 	}
 
- 	// Wrap the listener in a TLS listener
 
- 	config := tls.Config{Certificates: []tls.Certificate{cert}}
 
- 	originalTLSListener := tls.NewListener(originalListener, &config)
 
- 	// Wrap listeners in a signal aware listener
 
- 	sl := newSignalTCPListener(originalTLSListener, originalListener.(*net.TCPListener), wgStatus)
 
- 	return hs.runServer(sl, wgStatus)
 
- }
 
- /*
 
- runServer starts the actual server and notifies the wait group.
 
- */
 
- func (hs *HTTPServer) runServer(sl *signalTCPListener, wgStatus *sync.WaitGroup) error {
 
- 	// Use the http server from the standard library
 
- 	server := http.Server{}
 
- 	// Attach SIGINT handler - on unix and windows this is send
 
- 	// when the user presses ^C (Control-C).
 
- 	hs.signalling = make(chan os.Signal)
 
- 	signal.Notify(hs.signalling, syscall.SIGINT)
 
- 	// Put the serve call into a wait group so we can wait until shutdown
 
- 	// completed
 
- 	var wg sync.WaitGroup
 
- 	wg.Add(1)
 
- 	go func() {
 
- 		defer wg.Done()
 
- 		hs.Running = true
 
- 		server.Serve(sl)
 
- 	}()
 
- 	for true {
 
- 		signal := <-hs.signalling
 
- 		if signal == syscall.SIGINT {
 
- 			// Shutdown the server
 
- 			sl.Shutdown()
 
- 			// Wait until the server has shut down
 
- 			wg.Wait()
 
- 			hs.Running = false
 
- 			break
 
- 		}
 
- 	}
 
- 	if wgStatus != nil {
 
- 		wgStatus.Done()
 
- 	}
 
- 	return nil
 
- }
 
- /*
 
- signalTCPListener models a TCPListener which can receive signals.
 
- */
 
- type signalTCPListener struct {
 
- 	net.Listener                  // Wrapped new.Listener
 
- 	tcpListener  *net.TCPListener // TCP listener which accepts connections
 
- 	Signals      chan int         // Channel used for signalling
 
- 	wgStatus     *sync.WaitGroup  // Optional Waitgroup to be notified after start
 
- }
 
- /*
 
- SigShutdown is used to signal a request for shutdown
 
- */
 
- const SigShutdown = 1
 
- /*
 
- ErrSigShutdown indicates that a signal was received
 
- */
 
- var ErrSigShutdown = errors.New("Server was shut down")
 
- /*
 
- newSignalTCPListener wraps a given TCPListener.
 
- */
 
- func newSignalTCPListener(l net.Listener, tl *net.TCPListener, wgStatus *sync.WaitGroup) *signalTCPListener {
 
- 	return &signalTCPListener{l, tl, make(chan int), wgStatus}
 
- }
 
- /*
 
- Accept waits for a new connection. This accept call will check every
 
- second if a signal or other shutdown event was received.
 
- */
 
- func (sl *signalTCPListener) Accept() (net.Conn, error) {
 
- 	for {
 
- 		// Wait up to a second for a new connection
 
- 		sl.tcpListener.SetDeadline(time.Now().Add(time.Second))
 
- 		newConn, err := sl.Listener.Accept()
 
- 		// Notify wgStatus if it was specified
 
- 		if sl.wgStatus != nil {
 
- 			sl.wgStatus.Done()
 
- 			sl.wgStatus = nil
 
- 		}
 
- 		// Check for a received signal
 
- 		select {
 
- 		case sig := <-sl.Signals:
 
- 			// Check which signal was received
 
- 			if sig == SigShutdown {
 
- 				return nil, ErrSigShutdown
 
- 			}
 
- 			panic(fmt.Sprintf("Unknown signal received: %v", sig))
 
- 		default:
 
- 			netErr, ok := err.(net.Error)
 
- 			// If we got a connection or error at this point return it
 
- 			if (err != nil && (!ok || !(netErr.Timeout() && netErr.Temporary()))) || newConn != nil {
 
- 				return newConn, err
 
- 			}
 
- 		}
 
- 	}
 
- }
 
- /*
 
- Shutdown sends a shutdown signal.
 
- */
 
- func (sl *signalTCPListener) Shutdown() {
 
- 	sl.Signals <- SigShutdown
 
- 	close(sl.Signals)
 
- }
 
 
  |