github.com/pusher/oauth2_proxy@v3.2.0+incompatible/http.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto/tls"
     5  	"log"
     6  	"net"
     7  	"net/http"
     8  	"strings"
     9  	"time"
    10  )
    11  
    12  // Server represents an HTTP server
    13  type Server struct {
    14  	Handler http.Handler
    15  	Opts    *Options
    16  }
    17  
    18  // ListenAndServe will serve traffic on HTTP or HTTPS depending on TLS options
    19  func (s *Server) ListenAndServe() {
    20  	if s.Opts.TLSKeyFile != "" || s.Opts.TLSCertFile != "" {
    21  		s.ServeHTTPS()
    22  	} else {
    23  		s.ServeHTTP()
    24  	}
    25  }
    26  
    27  // Used with gcpHealthcheck()
    28  const userAgentHeader = "User-Agent"
    29  const googleHealthCheckUserAgent = "GoogleHC/1.0"
    30  const rootPath = "/"
    31  
    32  // gcpHealthcheck handles healthcheck queries from GCP.
    33  func gcpHealthcheck(h http.Handler) http.Handler {
    34  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    35  		// Check for liveness and readiness:  used for Google App Engine
    36  		if r.URL.EscapedPath() == "/liveness_check" {
    37  			w.WriteHeader(http.StatusOK)
    38  			w.Write([]byte("OK"))
    39  			return
    40  		}
    41  		if r.URL.EscapedPath() == "/readiness_check" {
    42  			w.WriteHeader(http.StatusOK)
    43  			w.Write([]byte("OK"))
    44  			return
    45  		}
    46  
    47  		// Check for GKE ingress healthcheck:  The ingress requires the root
    48  		// path of the target to return a 200 (OK) to indicate the service's good health. This can be quite a challenging demand
    49  		// depending on the application's path structure. This middleware filters out the requests from the health check by
    50  		//
    51  		// 1. checking that the request path is indeed the root path
    52  		// 2. ensuring that the User-Agent is "GoogleHC/1.0", the health checker
    53  		// 3. ensuring the request method is "GET"
    54  		if r.URL.Path == rootPath &&
    55  			r.Header.Get(userAgentHeader) == googleHealthCheckUserAgent &&
    56  			r.Method == http.MethodGet {
    57  
    58  			w.WriteHeader(http.StatusOK)
    59  			return
    60  		}
    61  
    62  		h.ServeHTTP(w, r)
    63  	})
    64  }
    65  
    66  // ServeHTTP constructs a net.Listener and starts handling HTTP requests
    67  func (s *Server) ServeHTTP() {
    68  	HTTPAddress := s.Opts.HTTPAddress
    69  	var scheme string
    70  
    71  	i := strings.Index(HTTPAddress, "://")
    72  	if i > -1 {
    73  		scheme = HTTPAddress[0:i]
    74  	}
    75  
    76  	var networkType string
    77  	switch scheme {
    78  	case "", "http":
    79  		networkType = "tcp"
    80  	default:
    81  		networkType = scheme
    82  	}
    83  
    84  	slice := strings.SplitN(HTTPAddress, "//", 2)
    85  	listenAddr := slice[len(slice)-1]
    86  
    87  	listener, err := net.Listen(networkType, listenAddr)
    88  	if err != nil {
    89  		log.Fatalf("FATAL: listen (%s, %s) failed - %s", networkType, listenAddr, err)
    90  	}
    91  	log.Printf("HTTP: listening on %s", listenAddr)
    92  
    93  	server := &http.Server{Handler: s.Handler}
    94  	err = server.Serve(listener)
    95  	if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
    96  		log.Printf("ERROR: http.Serve() - %s", err)
    97  	}
    98  
    99  	log.Printf("HTTP: closing %s", listener.Addr())
   100  }
   101  
   102  // ServeHTTPS constructs a net.Listener and starts handling HTTPS requests
   103  func (s *Server) ServeHTTPS() {
   104  	addr := s.Opts.HTTPSAddress
   105  	config := &tls.Config{
   106  		MinVersion: tls.VersionTLS12,
   107  		MaxVersion: tls.VersionTLS12,
   108  	}
   109  	if config.NextProtos == nil {
   110  		config.NextProtos = []string{"http/1.1"}
   111  	}
   112  
   113  	var err error
   114  	config.Certificates = make([]tls.Certificate, 1)
   115  	config.Certificates[0], err = tls.LoadX509KeyPair(s.Opts.TLSCertFile, s.Opts.TLSKeyFile)
   116  	if err != nil {
   117  		log.Fatalf("FATAL: loading tls config (%s, %s) failed - %s", s.Opts.TLSCertFile, s.Opts.TLSKeyFile, err)
   118  	}
   119  
   120  	ln, err := net.Listen("tcp", addr)
   121  	if err != nil {
   122  		log.Fatalf("FATAL: listen (%s) failed - %s", addr, err)
   123  	}
   124  	log.Printf("HTTPS: listening on %s", ln.Addr())
   125  
   126  	tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
   127  	srv := &http.Server{Handler: s.Handler}
   128  	err = srv.Serve(tlsListener)
   129  
   130  	if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
   131  		log.Printf("ERROR: https.Serve() - %s", err)
   132  	}
   133  
   134  	log.Printf("HTTPS: closing %s", tlsListener.Addr())
   135  }
   136  
   137  // tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
   138  // connections. It's used by ListenAndServe and ListenAndServeTLS so
   139  // dead TCP connections (e.g. closing laptop mid-download) eventually
   140  // go away.
   141  type tcpKeepAliveListener struct {
   142  	*net.TCPListener
   143  }
   144  
   145  func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
   146  	tc, err := ln.AcceptTCP()
   147  	if err != nil {
   148  		return
   149  	}
   150  	tc.SetKeepAlive(true)
   151  	tc.SetKeepAlivePeriod(3 * time.Minute)
   152  	return tc, nil
   153  }