grace/gracehttp/testserver/testserver.go
2013-08-20 13:28:03 -07:00

157 lines
4.4 KiB
Go

// Command testserver implements a test case.
package main
import (
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"strings"
"sync"
"time"
"github.com/daaku/go.grace/gracehttp"
)
type response struct {
Sleep time.Duration
Pid int
Error string `json:",omitempty"`
}
func wait(wg *sync.WaitGroup, url string) {
defer wg.Done()
for {
res, err := http.Get(url)
if err == nil {
// ensure it isn't a response from a previous instance
defer res.Body.Close()
var r response
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
log.Fatal("Error decoding json: %s", err)
}
if r.Pid == os.Getpid() {
return
}
} else {
// we expect connection refused
if !strings.HasSuffix(err.Error(), "connection refused") {
e2 := json.NewEncoder(os.Stderr).Encode(&response{
Error: err.Error(),
Pid: os.Getpid(),
})
if e2 != nil {
log.Fatalf("Error writing error json: %s", e2)
}
}
}
}
}
func httpsServer(addr string) *http.Server {
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
if err != nil {
log.Fatal("error loading cert: %v", err)
}
return &http.Server{
Addr: addr,
Handler: newHandler(),
TLSConfig: &tls.Config{
NextProtos: []string{"http/1.1"},
Certificates: []tls.Certificate{cert},
},
}
}
func main() {
var httpAddr, httpsAddr string
flag.StringVar(&httpAddr, "http", ":48560", "http address to bind to")
flag.StringVar(&httpsAddr, "https", ":48561", "https address to bind to")
flag.Parse()
// we have self signed certs
http.DefaultTransport = &http.Transport{
DisableKeepAlives: true,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
err := flag.Set("gracehttp.log", "false")
if err != nil {
log.Fatalf("Error setting gracehttp.log: %s", err)
}
// print json to stderr once we can successfully connect to all three
// addresses. the ensures we only print the line once we're ready to serve.
go func() {
var wg sync.WaitGroup
wg.Add(2)
go wait(&wg, fmt.Sprintf("http://%s/sleep/?duration=0", httpAddr))
go wait(&wg, fmt.Sprintf("https://%s/sleep/?duration=0", httpsAddr))
wg.Wait()
err = json.NewEncoder(os.Stderr).Encode(&response{Pid: os.Getpid()})
if err != nil {
log.Fatalf("Error writing startup json: %s", err)
}
}()
err = gracehttp.Serve(
&http.Server{Addr: httpAddr, Handler: newHandler()},
httpsServer(httpsAddr),
)
if err != nil {
log.Fatalf("Error in gracehttp.Serve: %s", err)
}
}
func newHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/sleep/", func(w http.ResponseWriter, r *http.Request) {
duration, err := time.ParseDuration(r.FormValue("duration"))
if err != nil {
http.Error(w, err.Error(), 400)
}
time.Sleep(duration)
err = json.NewEncoder(w).Encode(&response{
Sleep: duration,
Pid: os.Getpid(),
})
if err != nil {
log.Fatalf("Error encoding json: %s", err)
}
})
return mux
}
// localhostCert is a PEM-encoded TLS cert with SAN IPs
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
// of ASN.1 time).
// generated from src/pkg/crypto/tls:
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBALyCfqwwip8BvTKgVKGdmjZTU8DD
ndR+WALmFPIRqn89bOU3s30olKiqYEju/SFoEvMyFRT/TWEhXHDaufThqaMCAwEA
AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAr/09uy108p51rheIOSnz4zgduyTl
M+4AmRo8/U1twEZLgfAGG/GZjREv2y4mCEUIM3HebCAqlA5jpRg76Rf8jw==
-----END CERTIFICATE-----`)
// localhostKey is the private key for localhostCert.
var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBALyCfqwwip8BvTKgVKGdmjZTU8DDndR+WALmFPIRqn89bOU3s30o
lKiqYEju/SFoEvMyFRT/TWEhXHDaufThqaMCAwEAAQJAPXuWUxTV8XyAt8VhNQER
LgzJcUKb9JVsoS1nwXgPksXnPDKnL9ax8VERrdNr+nZbj2Q9cDSXBUovfdtehcdP
qQIhAO48ZsPylbTrmtjDEKiHT2Ik04rLotZYS2U873J6I7WlAiEAypDjYxXyafv/
Yo1pm9onwcetQKMW8CS3AjuV9Axzj6cCIEx2Il19fEMG4zny0WPlmbrcKvD/DpJQ
4FHrzsYlIVTpAiAas7S1uAvneqd0l02HlN9OxQKKlbUNXNme+rnOnOGS2wIgS0jW
zl1jvrOSJeP1PpAHohWz6LOhEr8uvltWkN6x3vE=
-----END RSA PRIVATE KEY-----`)