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