diff --git a/cert_go1.10_test.go b/cert_go1.10_test.go index 56b4564407f..36a813d6aab 100644 --- a/cert_go1.10_test.go +++ b/cert_go1.10_test.go @@ -204,7 +204,7 @@ func TestProxyTransport(t *testing.T) { spec.Proxy.Transport.ProxyURL = proxy.URL }) - client := getTLSClient(nil, nil) + client := getTLSClient(nil, nil, false) client.Transport = &http.Transport{ TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper), } diff --git a/cert_test.go b/cert_test.go index 41fd7310c5c..84c73342803 100644 --- a/cert_test.go +++ b/cert_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "crypto/rand" "crypto/rsa" "crypto/tls" @@ -18,6 +19,8 @@ import ( "testing" "time" + "golang.org/x/net/http2" + "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/certs" "github.com/TykTechnologies/tyk/config" @@ -25,7 +28,7 @@ import ( "github.com/TykTechnologies/tyk/user" ) -func getTLSClient(cert *tls.Certificate, caCert []byte) *http.Client { +func getTLSClient(cert *tls.Certificate, caCert []byte, isHttp2 bool) *http.Client { // Setup HTTPS client tlsConfig := &tls.Config{} @@ -42,7 +45,12 @@ func getTLSClient(cert *tls.Certificate, caCert []byte) *http.Client { tlsConfig.InsecureSkipVerify = true } - transport := &http.Transport{TLSClientConfig: tlsConfig} + var transport http.RoundTripper + if isHttp2 { + transport = &http2.Transport{TLSClientConfig: tlsConfig} + } else { + transport = &http.Transport{TLSClientConfig: tlsConfig} + } return &http.Client{Transport: transport} } @@ -91,7 +99,7 @@ func TestGatewayTLS(t *testing.T) { dir, _ := ioutil.TempDir("", "certs") defer os.RemoveAll(dir) - client := getTLSClient(nil, nil) + client := getTLSClient(nil, nil, false) t.Run("Without certificates", func(t *testing.T) { globalConf := config.Global() @@ -204,9 +212,9 @@ func TestGatewayControlAPIMutualTLS(t *testing.T) { }() clientCertPem, _, _, clientCert := genCertificate(&x509.Certificate{}) - clientWithCert := getTLSClient(&clientCert, serverCertPem) + clientWithCert := getTLSClient(&clientCert, serverCertPem, false) - clientWithoutCert := getTLSClient(nil, nil) + clientWithoutCert := getTLSClient(nil, nil, false) t.Run("Separate domain", func(t *testing.T) { certID, _ := CertificateManager.Add(combinedPEM, "") @@ -276,7 +284,7 @@ func TestAPIMutualTLS(t *testing.T) { t.Run("SNI and domain per API", func(t *testing.T) { t.Run("API without mutual TLS", func(t *testing.T) { - client := getTLSClient(&clientCert, serverCertPem) + client := getTLSClient(&clientCert, serverCertPem, false) buildAndLoadAPI(func(spec *APISpec) { spec.Domain = "localhost" @@ -287,7 +295,7 @@ func TestAPIMutualTLS(t *testing.T) { }) t.Run("MutualTLSCertificate not set", func(t *testing.T) { - client := getTLSClient(nil, nil) + client := getTLSClient(nil, nil, false) buildAndLoadAPI(func(spec *APISpec) { spec.Domain = "localhost" @@ -303,7 +311,7 @@ func TestAPIMutualTLS(t *testing.T) { }) t.Run("Client certificate match", func(t *testing.T) { - client := getTLSClient(&clientCert, serverCertPem) + client := getTLSClient(&clientCert, serverCertPem, false) clientCertID, _ := CertificateManager.Add(clientCertPem, "") buildAndLoadAPI(func(spec *APISpec) { @@ -320,14 +328,14 @@ func TestAPIMutualTLS(t *testing.T) { CertificateManager.Delete(clientCertID) CertificateManager.FlushCache() - client = getTLSClient(&clientCert, serverCertPem) + client = getTLSClient(&clientCert, serverCertPem, false) ts.Run(t, test.TestCase{ Client: client, Domain: "localhost", ErrorMatch: badcertErr, }) }) t.Run("Client certificate differ", func(t *testing.T) { - client := getTLSClient(&clientCert, serverCertPem) + client := getTLSClient(&clientCert, serverCertPem, false) clientCertPem2, _, _, _ := genCertificate(&x509.Certificate{}) clientCertID2, _ := CertificateManager.Add(clientCertPem2, "") @@ -364,7 +372,7 @@ func TestAPIMutualTLS(t *testing.T) { } t.Run("Without certificate", func(t *testing.T) { - clientWithoutCert := getTLSClient(nil, nil) + clientWithoutCert := getTLSClient(nil, nil, false) loadAPIS() @@ -385,7 +393,7 @@ func TestAPIMutualTLS(t *testing.T) { }) t.Run("Client certificate not match", func(t *testing.T) { - client := getTLSClient(&clientCert, serverCertPem) + client := getTLSClient(&clientCert, serverCertPem, false) loadAPIS() @@ -401,7 +409,7 @@ func TestAPIMutualTLS(t *testing.T) { t.Run("Client certificate match", func(t *testing.T) { loadAPIS(clientCertID) - client := getTLSClient(&clientCert, serverCertPem) + client := getTLSClient(&clientCert, serverCertPem, false) ts.Run(t, test.TestCase{ Path: "/with_mutual", @@ -431,7 +439,7 @@ func TestUpstreamMutualTLS(t *testing.T) { defer upstream.Close() t.Run("Without API", func(t *testing.T) { - client := getTLSClient(&clientCert, nil) + client := getTLSClient(&clientCert, nil, false) if _, err := client.Get(upstream.URL); err == nil { t.Error("Should reject without certificate") @@ -495,7 +503,7 @@ func TestKeyWithCertificateTLS(t *testing.T) { spec.Proxy.ListenPath = "/" }) - client := getTLSClient(&clientCert, nil) + client := getTLSClient(&clientCert, nil, false) t.Run("Cert unknown", func(t *testing.T) { ts.Run(t, test.TestCase{Code: 403, Client: client}) @@ -648,3 +656,73 @@ func TestCipherSuites(t *testing.T) { ts.Run(t, test.TestCase{Client: client, Path: "/", ErrorMatch: "tls: handshake failure"}) }) } + +func TestHTTP2(t *testing.T) { + + // Certificates + serverCertPem, serverPrivPem, _, _ := genServerCertificate() + _, _, _, clientCert := genCertificate(&x509.Certificate{}) + + dir, _ := ioutil.TempDir("", "certs") + defer os.RemoveAll(dir) + certFilePath := filepath.Join(dir, "server.crt") + ioutil.WriteFile(certFilePath, serverCertPem, 0666) + + certKeyPath := filepath.Join(dir, "server.key") + ioutil.WriteFile(certKeyPath, serverPrivPem, 0666) + + http2Server, err := startMockHttp2Server(certFilePath, certKeyPath) + if err != nil { + t.Fatal(err) + } + + defer func() { + http2Server.Shutdown(context.Background()) + }() + + // Configuration + globalConf := config.Global() + globalConf.ProxySSLInsecureSkipVerify = true + globalConf.HttpServerOptions.EnableHttp2 = true + globalConf.HttpServerOptions.Certificates = []config.CertData{{ + Name: "localhost", + CertFile: certFilePath, + KeyFile: certKeyPath, + }} + globalConf.HttpServerOptions.UseSSL = true + config.SetGlobal(globalConf) + defer resetTestConfig() + + ts := newTykTestServer() + defer ts.Close() + + buildAndLoadAPI(func(spec *APISpec) { + spec.Proxy.ListenPath = "/" + spec.UseKeylessAccess = true + spec.Proxy.TargetURL = "https://localhost:16501" // HTTP/2 Upstream + }) + + // Client + http2Client := getTLSClient(&clientCert, serverCertPem, true) + + ts.Run(t, test.TestCase{Client: http2Client, Path: "", Code: 200, BodyMatch: "Hello, I am an Http2 Server"}) +} + +func startMockHttp2Server(certFilePath string, keyFilePath string) (*http.Server, error) { + s := &http.Server{ + Addr: ":16501", + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, I am an Http2 Server") + }), + } + err := http2.ConfigureServer(s, nil) + if err != nil { + return nil, err + } + + go func() { + s.ListenAndServeTLS(certFilePath, keyFilePath) + }() + + return s, nil +} diff --git a/config/config.go b/config/config.go index 8d012c5c424..ad0a6163c17 100644 --- a/config/config.go +++ b/config/config.go @@ -143,6 +143,7 @@ type HttpServerOptionsConfig struct { WriteTimeout int `json:"write_timeout"` UseSSL bool `json:"use_ssl"` UseLE_SSL bool `json:"use_ssl_le"` + EnableHttp2 bool `json:"enable_http2"` SSLInsecureSkipVerify bool `json:"ssl_insecure_skip_verify"` EnableWebSockets bool `json:"enable_websockets"` Certificates []CertData `json:"certificates"` diff --git a/dnscache/storage.go b/dnscache/storage.go index c67f6d4dbac..ac8053490c8 100644 --- a/dnscache/storage.go +++ b/dnscache/storage.go @@ -7,7 +7,7 @@ import ( "fmt" "github.com/Sirupsen/logrus" - "github.com/pmylund/go-cache" + cache "github.com/pmylund/go-cache" ) // DnsCacheItem represents single record in cache diff --git a/handler_success.go b/handler_success.go index ccf5f4cffb2..4d3008bfd15 100644 --- a/handler_success.go +++ b/handler_success.go @@ -10,9 +10,8 @@ import ( "strings" "time" - "github.com/pmylund/go-cache" - "github.com/TykTechnologies/tyk/request" + cache "github.com/pmylund/go-cache" "github.com/TykTechnologies/tyk/config" "github.com/TykTechnologies/tyk/user" diff --git a/helpers_test.go b/helpers_test.go index 25f08f80539..7a98b57d185 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -21,10 +21,10 @@ import ( "testing" "time" - "github.com/dgrijalva/jwt-go" + jwt "github.com/dgrijalva/jwt-go" "github.com/gorilla/mux" "github.com/gorilla/websocket" - "github.com/satori/go.uuid" + uuid "github.com/satori/go.uuid" "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/config" diff --git a/host_checker.go b/host_checker.go index 8af72611ced..40ae81e6008 100644 --- a/host_checker.go +++ b/host_checker.go @@ -9,7 +9,7 @@ import ( "time" "github.com/jeffail/tunny" - "github.com/pmylund/go-cache" + cache "github.com/pmylund/go-cache" "github.com/TykTechnologies/tyk/config" ) diff --git a/install/data/tyk.self_contained.conf b/install/data/tyk.self_contained.conf index 061863e96bd..aa67ec41e38 100644 --- a/install/data/tyk.self_contained.conf +++ b/install/data/tyk.self_contained.conf @@ -58,6 +58,7 @@ } }, "http_server_options": { + "enable_http2": true, "enable_websockets": true }, "hostname": "", diff --git a/install/data/tyk.with_dash.conf b/install/data/tyk.with_dash.conf index daa77114969..1201c6866c5 100644 --- a/install/data/tyk.with_dash.conf +++ b/install/data/tyk.with_dash.conf @@ -63,6 +63,7 @@ "disable_cached_session_state": false }, "http_server_options": { + "enable_http2": true, "enable_websockets": true }, "uptime_tests": { diff --git a/main.go b/main.go index f7419a13f30..babd2962a92 100644 --- a/main.go +++ b/main.go @@ -19,12 +19,14 @@ import ( "sync" "time" + "golang.org/x/net/http2" + newrelic "github.com/newrelic/go-agent" "github.com/TykTechnologies/tyk/checkup" "github.com/Sirupsen/logrus" - "github.com/Sirupsen/logrus/hooks/syslog" + logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" logstashHook "github.com/bshuster-repo/logrus-logstash-hook" "github.com/evalphobia/logrus_sentry" "github.com/facebookgo/pidfile" @@ -1145,6 +1147,10 @@ func generateListener(listenPort int) (net.Listener, error) { CipherSuites: getCipherAliases(httpServerOptions.Ciphers), } + if httpServerOptions.EnableHttp2 { + tlsConfig.NextProtos = append(tlsConfig.NextProtos, http2.NextProtoTLS) + } + tlsConfig.GetConfigForClient = getTLSConfigForClient(&tlsConfig, listenPort) return tls.Listen("tcp", targetPort, &tlsConfig) diff --git a/middleware.go b/middleware.go index 9d7c19f0b92..08bb24b7253 100644 --- a/middleware.go +++ b/middleware.go @@ -9,9 +9,9 @@ import ( "github.com/Sirupsen/logrus" "github.com/gocraft/health" "github.com/justinas/alice" - "github.com/newrelic/go-agent" + newrelic "github.com/newrelic/go-agent" "github.com/paulbellamy/ratecounter" - "github.com/pmylund/go-cache" + cache "github.com/pmylund/go-cache" "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/config" diff --git a/mw_js_plugin_test.go b/mw_js_plugin_test.go index 9b2cdc35a85..722261b74aa 100644 --- a/mw_js_plugin_test.go +++ b/mw_js_plugin_test.go @@ -13,7 +13,7 @@ import ( "time" "github.com/Sirupsen/logrus" - "github.com/x-cray/logrus-prefixed-formatter" + prefixed "github.com/x-cray/logrus-prefixed-formatter" "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/config" diff --git a/reverse_proxy.go b/reverse_proxy.go index 1c01f331096..7a6615507ca 100644 --- a/reverse_proxy.go +++ b/reverse_proxy.go @@ -27,7 +27,7 @@ import ( "time" "github.com/Sirupsen/logrus" - "github.com/pmylund/go-cache" + cache "github.com/pmylund/go-cache" "github.com/TykTechnologies/tyk/apidef" "github.com/TykTechnologies/tyk/config"