git.iko.soy/typeable/oauth2-proxy
on v3.2.0 help sign in
153 lines 4.2 kB history raw
1package main
3import (
4 "crypto/tls"
5 "log"
6 "net"
7 "net/http"
8 "strings"
9 "time"
12// Server represents an HTTP server
13type Server struct {
14 Handler http.Handler
15 Opts *Options
18// ListenAndServe will serve traffic on HTTP or HTTPS depending on TLS options
19func (s *Server) ListenAndServe() {
20 if s.Opts.TLSKeyFile != "" || s.Opts.TLSCertFile != "" {
21 s.ServeHTTPS()
22 } else {
23 s.ServeHTTP()
24 }
27// Used with gcpHealthcheck()
28const userAgentHeader = "User-Agent"
29const googleHealthCheckUserAgent = "GoogleHC/1.0"
30const rootPath = "/"
32// gcpHealthcheck handles healthcheck queries from GCP.
33func 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 }
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 {
58 w.WriteHeader(http.StatusOK)
59 return
60 }
62 h.ServeHTTP(w, r)
63 })
66// ServeHTTP constructs a net.Listener and starts handling HTTP requests
67func (s *Server) ServeHTTP() {
68 HTTPAddress := s.Opts.HTTPAddress
69 var scheme string
71 i := strings.Index(HTTPAddress, "://")
72 if i > -1 {
73 scheme = HTTPAddress[0:i]
74 }
76 var networkType string
77 switch scheme {
78 case "", "http":
79 networkType = "tcp"
80 default:
81 networkType = scheme
82 }
84 slice := strings.SplitN(HTTPAddress, "//", 2)
85 listenAddr := slice[len(slice)-1]
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)
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 }
99 log.Printf("HTTP: closing %s", listener.Addr())
102// ServeHTTPS constructs a net.Listener and starts handling HTTPS requests
103func (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 }
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 }
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())
126 tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
127 srv := &http.Server{Handler: s.Handler}
128 err = srv.Serve(tlsListener)
130 if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
131 log.Printf("ERROR: https.Serve() - %s", err)
132 }
134 log.Printf("HTTPS: closing %s", tlsListener.Addr())
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.
141type tcpKeepAliveListener struct {
142 *net.TCPListener
145func (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