package main import ( "crypto/tls" "net" "net/http" "strings" "time" "github.com/pusher/oauth2_proxy/logger" ) // Server represents an HTTP server type Server struct { Handler http.Handler Opts *Options } // ListenAndServe will serve traffic on HTTP or HTTPS depending on TLS options func (s *Server) ListenAndServe() { if s.Opts.TLSKeyFile != "" || s.Opts.TLSCertFile != "" { s.ServeHTTPS() } else { s.ServeHTTP() } } // ServeHTTP constructs a net.Listener and starts handling HTTP requests func (s *Server) ServeHTTP() { HTTPAddress := s.Opts.HTTPAddress var scheme string i := strings.Index(HTTPAddress, "://") if i > -1 { scheme = HTTPAddress[0:i] } var networkType string switch scheme { case "", "http": networkType = "tcp" default: networkType = scheme } slice := strings.SplitN(HTTPAddress, "//", 2) listenAddr := slice[len(slice)-1] listener, err := net.Listen(networkType, listenAddr) if err != nil { logger.Fatalf("FATAL: listen (%s, %s) failed - %s", networkType, listenAddr, err) } logger.Printf("HTTP: listening on %s", listenAddr) server := &http.Server{Handler: s.Handler} err = server.Serve(listener) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { logger.Printf("ERROR: http.Serve() - %s", err) } logger.Printf("HTTP: closing %s", listener.Addr()) } // ServeHTTPS constructs a net.Listener and starts handling HTTPS requests func (s *Server) ServeHTTPS() { addr := s.Opts.HTTPSAddress config := &tls.Config{ MinVersion: tls.VersionTLS12, MaxVersion: tls.VersionTLS12, } if config.NextProtos == nil { config.NextProtos = []string{"http/1.1"} } var err error config.Certificates = make([]tls.Certificate, 1) config.Certificates[0], err = tls.LoadX509KeyPair(s.Opts.TLSCertFile, s.Opts.TLSKeyFile) if err != nil { logger.Fatalf("FATAL: loading tls config (%s, %s) failed - %s", s.Opts.TLSCertFile, s.Opts.TLSKeyFile, err) } ln, err := net.Listen("tcp", addr) if err != nil { logger.Fatalf("FATAL: listen (%s) failed - %s", addr, err) } logger.Printf("HTTPS: listening on %s", ln.Addr()) tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config) srv := &http.Server{Handler: s.Handler} err = srv.Serve(tlsListener) if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { logger.Printf("ERROR: https.Serve() - %s", err) } logger.Printf("HTTPS: closing %s", tlsListener.Addr()) } // tcpKeepAliveListener sets TCP keep-alive timeouts on accepted // connections. It's used by ListenAndServe and ListenAndServeTLS so // dead TCP connections (e.g. closing laptop mid-download) eventually // go away. type tcpKeepAliveListener struct { *net.TCPListener } func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { tc, err := ln.AcceptTCP() if err != nil { return } tc.SetKeepAlive(true) tc.SetKeepAlivePeriod(3 * time.Minute) return tc, nil }