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 }