diff options
Diffstat (limited to 'internal/httptest/httptest.go')
-rw-r--r-- | internal/httptest/httptest.go | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/internal/httptest/httptest.go b/internal/httptest/httptest.go new file mode 100644 index 0000000..85b1719 --- /dev/null +++ b/internal/httptest/httptest.go @@ -0,0 +1,104 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package httptest provides a method for testing a TLS server a la net/http/httptest. +package httptest + +import ( + "bytes" + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "math/big" + "net" + "net/http" + "net/http/httptest" + "time" +) + +// NewTLSServer returns an httptest server, with an http client that has been configured to +// send all requests to the returned server. The TLS certs are generated for the given domain. +// If you need a transport, Client().Transport is correctly configured. +func NewTLSServer(domain string, handler http.Handler) (*httptest.Server, error) { + s := httptest.NewUnstartedServer(handler) + + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + NotBefore: time.Now().Add(-1 * time.Hour), + NotAfter: time.Now().Add(time.Hour), + IPAddresses: []net.IP{ + net.IPv4(127, 0, 0, 1), + net.IPv6loopback, + }, + DNSNames: []string{domain}, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: true, + } + + priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + if err != nil { + return nil, err + } + + b, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return nil, err + } + + pc := &bytes.Buffer{} + if err := pem.Encode(pc, &pem.Block{Type: "CERTIFICATE", Bytes: b}); err != nil { + return nil, err + } + + ek, err := x509.MarshalECPrivateKey(priv) + if err != nil { + return nil, err + } + + pk := &bytes.Buffer{} + if err := pem.Encode(pk, &pem.Block{Type: "EC PRIVATE KEY", Bytes: ek}); err != nil { + return nil, err + } + + c, err := tls.X509KeyPair(pc.Bytes(), pk.Bytes()) + if err != nil { + return nil, err + } + s.TLS = &tls.Config{ + Certificates: []tls.Certificate{c}, + } + s.StartTLS() + + certpool := x509.NewCertPool() + certpool.AddCert(s.Certificate()) + + t := &http.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: certpool, + }, + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + return net.Dial(s.Listener.Addr().Network(), s.Listener.Addr().String()) + }, + } + s.Client().Transport = t + + return s, nil +} |