http2/h2demo: fix the HTTP/1-vs-HTTP/2 demo after HSTS breakage

We were previously demoing HTTP/1 vs HTTP/2 loading performance using
http:// scheme (which uses HTTP/1 implicitly) on the http2.golang.org
domain for HTTP/1, and https://http2.golang.org for HTTP/2.

But then golang.org got into the HSTS preload list, forcing all
*.golang.org to only be HTTPS.

So now, rather than find a new base domain name, to compare against
HTTP/1 we instead use https://http1.golang.org/ and then use the SNI
name to determine whether we advertise "h2".

Also, some cleanup:

* remove launch.go; it's no longer used since we moved to kubernetes
* use a multi-stage Dockerfile rather than the hacky workarounds
  that used to be necessary to simulate multiple stages
* modernize the kubernetes deployment stuff to match how we do
  it elsewhere ("gcloud docker" is long deprecated too)
* update from Go 1.9 to Go 1.11 for the prod base

Fixes golang/go#30033

Change-Id: I9f6b1f496d4005e5a08bf990843d440005a5b3e8
Reviewed-on: https://go-review.googlesource.com/c/160857
Reviewed-by: Andrew Bonventre <andybons@golang.org>
diff --git a/http2/h2demo/Dockerfile b/http2/h2demo/Dockerfile
index 9238673..91f73ff 100644
--- a/http2/h2demo/Dockerfile
+++ b/http2/h2demo/Dockerfile
@@ -2,10 +2,162 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
-FROM scratch
+FROM golang:1.11 AS build
 LABEL maintainer "golang-dev@googlegroups.com"
 
-COPY ca-certificates.crt /etc/ssl/certs/
-COPY h2demo /
-ENTRYPOINT ["/h2demo", "-prod"]
+# BEGIN deps (run `make update-deps` to update)
+
+# Repo cloud.google.com/go at b5eca92 (2018-10-23)
+ENV REV=b5eca92245a08e245bc29c4880c9779ea4aeaa9a
+RUN go get -d cloud.google.com/go/compute/metadata `#and 7 other pkgs` &&\
+    (cd /go/src/cloud.google.com/go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo github.com/golang/protobuf at b4deda0 (2018-04-30)
+ENV REV=b4deda0973fb4c70b50d226b1af49f3da59f5265
+RUN go get -d github.com/golang/protobuf/proto `#and 6 other pkgs` &&\
+    (cd /go/src/github.com/golang/protobuf && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo github.com/googleapis/gax-go at 317e000 (2017-09-15)
+ENV REV=317e0006254c44a0ac427cc52a0e083ff0b9622f
+RUN go get -d github.com/googleapis/gax-go &&\
+    (cd /go/src/github.com/googleapis/gax-go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo go.opencensus.io at ebd8d31 (2018-05-16)
+ENV REV=ebd8d31470fedf6c27d0e3056653ddff642509b8
+RUN go get -d go.opencensus.io/internal `#and 11 other pkgs` &&\
+    (cd /go/src/go.opencensus.io && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo go4.org at fba789b (2018-01-03)
+ENV REV=fba789b7e39ba524b9e60c45c37a50fae63a2a09
+RUN go get -d go4.org/syncutil/singleflight &&\
+    (cd /go/src/go4.org && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo golang.org/x/build at 193361e (2019-02-01)
+ENV REV=193361e263a8c4175cbb6769bb2a59d8c4f8183e
+RUN go get -d golang.org/x/build/autocertcache &&\
+    (cd /go/src/golang.org/x/build && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo golang.org/x/crypto at c7b33c3 (2019-01-28)
+ENV REV=c7b33c32a30bae9ba07d37eb4d86f1f8b0f644fb
+RUN go get -d golang.org/x/crypto/acme `#and 2 other pkgs` &&\
+    (cd /go/src/golang.org/x/crypto && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo golang.org/x/oauth2 at c6d9d57 (2019-01-18)
+ENV REV=c6d9d5723bcdfb8ec25b90ea254cffe6025386d6
+RUN go get -d golang.org/x/oauth2 `#and 5 other pkgs` &&\
+    (cd /go/src/golang.org/x/oauth2 && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo golang.org/x/sys at 4d1cda0 (2018-12-13)
+ENV REV=4d1cda033e0619309c606fc686de3adcf599539e
+RUN go get -d golang.org/x/sys/unix &&\
+    (cd /go/src/golang.org/x/sys && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo golang.org/x/text at 6f44c5a (2018-10-30)
+ENV REV=6f44c5a2ea40ee3593d98cdcc905cc1fdaa660e2
+RUN go get -d golang.org/x/text/secure/bidirule `#and 4 other pkgs` &&\
+    (cd /go/src/golang.org/x/text && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo google.golang.org/api at 41dc4b6 (2018-12-17)
+ENV REV=41dc4b66e69d5dbf20efe4ba67e19d214d147ae3
+RUN go get -d google.golang.org/api/gensupport `#and 10 other pkgs` &&\
+    (cd /go/src/google.golang.org/api && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo google.golang.org/genproto at 86e600f (2018-04-27)
+ENV REV=86e600f69ee4704c6efbf6a2a40a5c10700e76c2
+RUN go get -d google.golang.org/genproto/googleapis/api/annotations `#and 4 other pkgs` &&\
+    (cd /go/src/google.golang.org/genproto && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo google.golang.org/grpc at 07ef407 (2018-08-06)
+ENV REV=07ef407d991f1004e6c3367c8f452ed9a02f17ff
+RUN go get -d google.golang.org/grpc `#and 26 other pkgs` &&\
+    (cd /go/src/google.golang.org/grpc && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Optimization to speed up iterative development, not necessary for correctness:
+RUN go install cloud.google.com/go/compute/metadata \
+	cloud.google.com/go/iam \
+	cloud.google.com/go/internal \
+	cloud.google.com/go/internal/optional \
+	cloud.google.com/go/internal/trace \
+	cloud.google.com/go/internal/version \
+	cloud.google.com/go/storage \
+	github.com/golang/protobuf/proto \
+	github.com/golang/protobuf/protoc-gen-go/descriptor \
+	github.com/golang/protobuf/ptypes \
+	github.com/golang/protobuf/ptypes/any \
+	github.com/golang/protobuf/ptypes/duration \
+	github.com/golang/protobuf/ptypes/timestamp \
+	github.com/googleapis/gax-go \
+	go.opencensus.io/internal \
+	go.opencensus.io/internal/tagencoding \
+	go.opencensus.io/plugin/ochttp \
+	go.opencensus.io/plugin/ochttp/propagation/b3 \
+	go.opencensus.io/stats \
+	go.opencensus.io/stats/internal \
+	go.opencensus.io/stats/view \
+	go.opencensus.io/tag \
+	go.opencensus.io/trace \
+	go.opencensus.io/trace/internal \
+	go.opencensus.io/trace/propagation \
+	go4.org/syncutil/singleflight \
+	golang.org/x/build/autocertcache \
+	golang.org/x/crypto/acme \
+	golang.org/x/crypto/acme/autocert \
+	golang.org/x/oauth2 \
+	golang.org/x/oauth2/google \
+	golang.org/x/oauth2/internal \
+	golang.org/x/oauth2/jws \
+	golang.org/x/oauth2/jwt \
+	golang.org/x/sys/unix \
+	golang.org/x/text/secure/bidirule \
+	golang.org/x/text/transform \
+	golang.org/x/text/unicode/bidi \
+	golang.org/x/text/unicode/norm \
+	google.golang.org/api/gensupport \
+	google.golang.org/api/googleapi \
+	google.golang.org/api/googleapi/internal/uritemplates \
+	google.golang.org/api/googleapi/transport \
+	google.golang.org/api/internal \
+	google.golang.org/api/iterator \
+	google.golang.org/api/option \
+	google.golang.org/api/storage/v1 \
+	google.golang.org/api/transport/http \
+	google.golang.org/api/transport/http/internal/propagation \
+	google.golang.org/genproto/googleapis/api/annotations \
+	google.golang.org/genproto/googleapis/iam/v1 \
+	google.golang.org/genproto/googleapis/rpc/code \
+	google.golang.org/genproto/googleapis/rpc/status \
+	google.golang.org/grpc \
+	google.golang.org/grpc/balancer \
+	google.golang.org/grpc/balancer/base \
+	google.golang.org/grpc/balancer/roundrobin \
+	google.golang.org/grpc/codes \
+	google.golang.org/grpc/connectivity \
+	google.golang.org/grpc/credentials \
+	google.golang.org/grpc/encoding \
+	google.golang.org/grpc/encoding/proto \
+	google.golang.org/grpc/grpclog \
+	google.golang.org/grpc/internal \
+	google.golang.org/grpc/internal/backoff \
+	google.golang.org/grpc/internal/channelz \
+	google.golang.org/grpc/internal/envconfig \
+	google.golang.org/grpc/internal/grpcrand \
+	google.golang.org/grpc/internal/transport \
+	google.golang.org/grpc/keepalive \
+	google.golang.org/grpc/metadata \
+	google.golang.org/grpc/naming \
+	google.golang.org/grpc/peer \
+	google.golang.org/grpc/resolver \
+	google.golang.org/grpc/resolver/dns \
+	google.golang.org/grpc/resolver/passthrough \
+	google.golang.org/grpc/stats \
+	google.golang.org/grpc/status \
+	google.golang.org/grpc/tap
+# END deps
+
+COPY . /go/src/golang.org/x/net/
+
+RUN go install -tags "h2demo netgo" golang.org/x/net/http2/h2demo
+
+FROM golang:1.11
+COPY --from=build /go/bin/h2demo /h2demo
 
diff --git a/http2/h2demo/Dockerfile.0 b/http2/h2demo/Dockerfile.0
deleted file mode 100644
index fd8435d..0000000
--- a/http2/h2demo/Dockerfile.0
+++ /dev/null
@@ -1,134 +0,0 @@
-# Copyright 2018 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-FROM golang:1.9
-LABEL maintainer "golang-dev@googlegroups.com"
-
-ENV CGO_ENABLED=0
-
-# BEGIN deps (run `make update-deps` to update)
-
-# Repo cloud.google.com/go at 1d0c2da (2018-01-30)
-ENV REV=1d0c2da40456a9b47f5376165f275424acc15c09
-RUN go get -d cloud.google.com/go/compute/metadata `#and 6 other pkgs` &&\
-    (cd /go/src/cloud.google.com/go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo github.com/golang/protobuf at 9255415 (2018-01-25)
-ENV REV=925541529c1fa6821df4e44ce2723319eb2be768
-RUN go get -d github.com/golang/protobuf/proto `#and 6 other pkgs` &&\
-    (cd /go/src/github.com/golang/protobuf && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo github.com/googleapis/gax-go at 317e000 (2017-09-15)
-ENV REV=317e0006254c44a0ac427cc52a0e083ff0b9622f
-RUN go get -d github.com/googleapis/gax-go &&\
-    (cd /go/src/github.com/googleapis/gax-go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo go4.org at 034d17a (2017-05-25)
-ENV REV=034d17a462f7b2dcd1a4a73553ec5357ff6e6c6e
-RUN go get -d go4.org/syncutil/singleflight &&\
-    (cd /go/src/go4.org && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo golang.org/x/build at 8aa9ee0 (2018-02-01)
-ENV REV=8aa9ee0e557fd49c14113e5ba106e13a5b455460
-RUN go get -d golang.org/x/build/autocertcache &&\
-    (cd /go/src/golang.org/x/build && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo golang.org/x/crypto at 1875d0a (2018-01-27)
-ENV REV=1875d0a70c90e57f11972aefd42276df65e895b9
-RUN go get -d golang.org/x/crypto/acme `#and 2 other pkgs` &&\
-    (cd /go/src/golang.org/x/crypto && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo golang.org/x/oauth2 at 30785a2 (2018-01-04)
-ENV REV=30785a2c434e431ef7c507b54617d6a951d5f2b4
-RUN go get -d golang.org/x/oauth2 `#and 5 other pkgs` &&\
-    (cd /go/src/golang.org/x/oauth2 && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo golang.org/x/text at e19ae14 (2017-12-27)
-ENV REV=e19ae1496984b1c655b8044a65c0300a3c878dd3
-RUN go get -d golang.org/x/text/secure/bidirule `#and 4 other pkgs` &&\
-    (cd /go/src/golang.org/x/text && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo google.golang.org/api at 7d0e2d3 (2018-01-30)
-ENV REV=7d0e2d350555821bef5a5b8aecf0d12cc1def633
-RUN go get -d google.golang.org/api/gensupport `#and 9 other pkgs` &&\
-    (cd /go/src/google.golang.org/api && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo google.golang.org/genproto at 4eb30f4 (2018-01-25)
-ENV REV=4eb30f4778eed4c258ba66527a0d4f9ec8a36c45
-RUN go get -d google.golang.org/genproto/googleapis/api/annotations `#and 3 other pkgs` &&\
-    (cd /go/src/google.golang.org/genproto && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo google.golang.org/grpc at 0bd008f (2018-01-25)
-ENV REV=0bd008f5fadb62d228f12b18d016709e8139a7af
-RUN go get -d google.golang.org/grpc `#and 23 other pkgs` &&\
-    (cd /go/src/google.golang.org/grpc && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Optimization to speed up iterative development, not necessary for correctness:
-RUN go install cloud.google.com/go/compute/metadata \
-	cloud.google.com/go/iam \
-	cloud.google.com/go/internal \
-	cloud.google.com/go/internal/optional \
-	cloud.google.com/go/internal/version \
-	cloud.google.com/go/storage \
-	github.com/golang/protobuf/proto \
-	github.com/golang/protobuf/protoc-gen-go/descriptor \
-	github.com/golang/protobuf/ptypes \
-	github.com/golang/protobuf/ptypes/any \
-	github.com/golang/protobuf/ptypes/duration \
-	github.com/golang/protobuf/ptypes/timestamp \
-	github.com/googleapis/gax-go \
-	go4.org/syncutil/singleflight \
-	golang.org/x/build/autocertcache \
-	golang.org/x/crypto/acme \
-	golang.org/x/crypto/acme/autocert \
-	golang.org/x/oauth2 \
-	golang.org/x/oauth2/google \
-	golang.org/x/oauth2/internal \
-	golang.org/x/oauth2/jws \
-	golang.org/x/oauth2/jwt \
-	golang.org/x/text/secure/bidirule \
-	golang.org/x/text/transform \
-	golang.org/x/text/unicode/bidi \
-	golang.org/x/text/unicode/norm \
-	google.golang.org/api/gensupport \
-	google.golang.org/api/googleapi \
-	google.golang.org/api/googleapi/internal/uritemplates \
-	google.golang.org/api/googleapi/transport \
-	google.golang.org/api/internal \
-	google.golang.org/api/iterator \
-	google.golang.org/api/option \
-	google.golang.org/api/storage/v1 \
-	google.golang.org/api/transport/http \
-	google.golang.org/genproto/googleapis/api/annotations \
-	google.golang.org/genproto/googleapis/iam/v1 \
-	google.golang.org/genproto/googleapis/rpc/status \
-	google.golang.org/grpc \
-	google.golang.org/grpc/balancer \
-	google.golang.org/grpc/balancer/base \
-	google.golang.org/grpc/balancer/roundrobin \
-	google.golang.org/grpc/codes \
-	google.golang.org/grpc/connectivity \
-	google.golang.org/grpc/credentials \
-	google.golang.org/grpc/encoding \
-	google.golang.org/grpc/encoding/proto \
-	google.golang.org/grpc/grpclb/grpc_lb_v1/messages \
-	google.golang.org/grpc/grpclog \
-	google.golang.org/grpc/internal \
-	google.golang.org/grpc/keepalive \
-	google.golang.org/grpc/metadata \
-	google.golang.org/grpc/naming \
-	google.golang.org/grpc/peer \
-	google.golang.org/grpc/resolver \
-	google.golang.org/grpc/resolver/dns \
-	google.golang.org/grpc/resolver/passthrough \
-	google.golang.org/grpc/stats \
-	google.golang.org/grpc/status \
-	google.golang.org/grpc/tap \
-	google.golang.org/grpc/transport
-# END deps
-
-COPY . /go/src/golang.org/x/net/
-
-RUN go install -tags "h2demo netgo" -ldflags "-linkmode=external -extldflags '-static -pthread'" golang.org/x/net/http2/h2demo
-
diff --git a/http2/h2demo/Makefile b/http2/h2demo/Makefile
index 306d198..388d8de 100644
--- a/http2/h2demo/Makefile
+++ b/http2/h2demo/Makefile
@@ -4,52 +4,34 @@
 
 MUTABLE_VERSION ?= latest
 VERSION ?= $(shell git rev-parse --short HEAD)
-
-IMAGE_STAGING := gcr.io/go-dashboard-dev/h2demo
-IMAGE_PROD := gcr.io/symbolic-datum-552/h2demo
-
-DOCKER_IMAGE_build0=build0/h2demo:latest
-DOCKER_CTR_build0=h2demo-build0
-
-build0: *.go Dockerfile.0
-	docker build --force-rm -f Dockerfile.0 --tag=$(DOCKER_IMAGE_build0) ../..
-
-h2demo: build0
-	docker create --name $(DOCKER_CTR_build0) $(DOCKER_IMAGE_build0)
-	docker cp $(DOCKER_CTR_build0):/go/bin/$@ $@
-	docker rm $(DOCKER_CTR_build0)
-
-ca-certificates.crt:
-	docker create --name $(DOCKER_CTR_build0) $(DOCKER_IMAGE_build0)
-	docker cp $(DOCKER_CTR_build0):/etc/ssl/certs/$@ $@
-	docker rm $(DOCKER_CTR_build0)
+IMAGE_NAME=h2demo
 
 update-deps:
 	go install golang.org/x/build/cmd/gitlock
-	gitlock --update=Dockerfile.0 --ignore=golang.org/x/net --tags=h2demo golang.org/x/net/http2/h2demo
+	gitlock --update=Dockerfile --ignore=golang.org/x/net --tags=h2demo golang.org/x/net/http2/h2demo
 
-docker-prod: Dockerfile h2demo ca-certificates.crt
-	docker build --force-rm --tag=$(IMAGE_PROD):$(VERSION) .
-	docker tag $(IMAGE_PROD):$(VERSION) $(IMAGE_PROD):$(MUTABLE_VERSION)
-docker-staging: Dockerfile h2demo ca-certificates.crt
-	docker build --force-rm --tag=$(IMAGE_STAGING):$(VERSION) .
-	docker tag $(IMAGE_STAGING):$(VERSION) $(IMAGE_STAGING):$(MUTABLE_VERSION)
+docker: Dockerfile
+	go install golang.org/x/build/cmd/xb
+	xb docker build -t golang/$(IMAGE_NAME) -f Dockerfile ../..
 
-push-prod: docker-prod
-	gcloud docker -- push $(IMAGE_PROD):$(MUTABLE_VERSION)
-	gcloud docker -- push $(IMAGE_PROD):$(VERSION)
-push-staging: docker-staging
-	gcloud docker -- push $(IMAGE_STAGING):$(MUTABLE_VERSION)
-	gcloud docker -- push $(IMAGE_STAGING):$(VERSION)
+push-staging: docker
+	go install golang.org/x/build/cmd/xb
+	xb --staging docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):$(VERSION)
+	xb --staging docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):latest
+	xb --staging docker push REPO/$(IMAGE_NAME):$(VERSION)
+	xb --staging docker push REPO/$(IMAGE_NAME):latest
 
+push-prod: docker
+	go install golang.org/x/build/cmd/xb
+	xb --prod docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):$(VERSION)
+	xb --prod docker tag golang/$(IMAGE_NAME) REPO/$(IMAGE_NAME):latest
+	xb --prod docker push REPO/$(IMAGE_NAME):$(VERSION)
+	xb --prod docker push REPO/$(IMAGE_NAME):latest
+
+# TODO(bradfitz): add REPO subsitution in xb for the kubectl command.
 deploy-prod: push-prod
-	kubectl set image deployment/h2demo-deployment h2demo=$(IMAGE_PROD):$(VERSION)
+	xb --prod kubectl set image deployment/h2demo-deployment h2demo=gcr.io/symbolic-datum-552/h2demo:$(VERSION)
 deploy-staging: push-staging
-	kubectl set image deployment/h2demo-deployment h2demo=$(IMAGE_STAGING):$(VERSION)
-
-.PHONY: clean
-clean:
-	$(RM) h2demo
-	$(RM) ca-certificates.crt
+	xb --staging kubectl set image deployment/h2demo-deployment h2demo=gcr.io/go-dashboard-dev/h2demo:$(VERSION)
 
 FORCE:
diff --git a/http2/h2demo/h2demo.go b/http2/h2demo/h2demo.go
index 59d9f5f..196a2a4 100644
--- a/http2/h2demo/h2demo.go
+++ b/http2/h2demo/h2demo.go
@@ -46,6 +46,10 @@
 )
 
 func homeOldHTTP(w http.ResponseWriter, r *http.Request) {
+	if r.Host == "http1.golang.org" {
+		http.Redirect(w, r, "https://http2.golang.org/", http.StatusFound)
+		return
+	}
 	io.WriteString(w, `<html>
 <body>
 <h1>Go + HTTP/2</h1>
@@ -307,13 +311,13 @@
 		}
 		time.Sleep(100 * time.Millisecond) // fake network latency + parsing time
 		if err := pushTmpl.Execute(w, struct {
-			CacheBust int64
-			HTTPSHost string
-			HTTPHost  string
+			CacheBust   int64
+			HTTPSHost   string
+			HTTP1Prefix string
 		}{
-			CacheBust: cacheBust,
-			HTTPSHost: httpsHost(),
-			HTTPHost:  httpHost(),
+			CacheBust:   cacheBust,
+			HTTPSHost:   httpsHost(),
+			HTTP1Prefix: http1Prefix(),
 		}); err != nil {
 			log.Printf("Executing server push template: %v", err)
 		}
@@ -382,9 +386,9 @@
 		fmt.Fprintf(w, "A grid of %d tiled images is below. Compare:<p>", xt*yt)
 		for _, ms := range []int{0, 30, 200, 1000} {
 			d := time.Duration(ms) * nanosPerMilli
-			fmt.Fprintf(w, "[<a href='https://%s/gophertiles?latency=%d'>HTTP/2, %v latency</a>] [<a href='http://%s/gophertiles?latency=%d'>HTTP/1, %v latency</a>]<br>\n",
+			fmt.Fprintf(w, "[<a href='https://%s/gophertiles?latency=%d'>HTTP/2, %v latency</a>] [<a href='%s/gophertiles?latency=%d'>HTTP/1, %v latency</a>]<br>\n",
 				httpsHost(), ms, d,
-				httpHost(), ms, d,
+				http1Prefix(), ms, d,
 			)
 		}
 		io.WriteString(w, "<p>\n")
@@ -420,6 +424,13 @@
 	}
 }
 
+func http1Prefix() string {
+	if *prod {
+		return "https://http1.golang.org"
+	}
+	return "http://" + httpHost()
+}
+
 func httpHost() string {
 	if *hostHTTP != "" {
 		return *hostHTTP
@@ -435,6 +446,14 @@
 	srv := &http.Server{
 		TLSConfig: &tls.Config{
 			GetCertificate: autocertManager.GetCertificate,
+			GetConfigForClient: func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
+				if hello.ServerName == "http1.golang.org" {
+					return &tls.Config{
+						GetCertificate: autocertManager.GetCertificate,
+					}, nil
+				}
+				return nil, nil // fallback to other methods
+			},
 		},
 	}
 	http2.ConfigureServer(srv, &http2.Server{
@@ -464,7 +483,7 @@
 }
 
 func serveProd() error {
-	log.Printf("running in production mode")
+	log.Printf("running in production mode.")
 
 	storageClient, err := storage.NewClient(context.Background())
 	if err != nil {
@@ -472,7 +491,7 @@
 	}
 	autocertManager := &autocert.Manager{
 		Prompt:     autocert.AcceptTOS,
-		HostPolicy: autocert.HostWhitelist("http2.golang.org"),
+		HostPolicy: autocert.HostWhitelist("http1.golang.org", "http2.golang.org"),
 		Cache:      autocertcache.NewGoogleCloudStorageCache(storageClient, "golang-h2demo-autocert"),
 	}
 
diff --git a/http2/h2demo/launch.go b/http2/h2demo/launch.go
deleted file mode 100644
index df0866a..0000000
--- a/http2/h2demo/launch.go
+++ /dev/null
@@ -1,302 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-package main
-
-import (
-	"bufio"
-	"bytes"
-	"encoding/json"
-	"flag"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"log"
-	"net/http"
-	"os"
-	"strings"
-	"time"
-
-	"golang.org/x/oauth2"
-	"golang.org/x/oauth2/google"
-	compute "google.golang.org/api/compute/v1"
-)
-
-var (
-	proj     = flag.String("project", "symbolic-datum-552", "name of Project")
-	zone     = flag.String("zone", "us-central1-a", "GCE zone")
-	mach     = flag.String("machinetype", "n1-standard-1", "Machine type")
-	instName = flag.String("instance_name", "http2-demo", "Name of VM instance.")
-	sshPub   = flag.String("ssh_public_key", "", "ssh public key file to authorize. Can modify later in Google's web UI anyway.")
-	staticIP = flag.String("static_ip", "130.211.116.44", "Static IP to use. If empty, automatic.")
-
-	writeObject  = flag.String("write_object", "", "If non-empty, a VM isn't created and the flag value is Google Cloud Storage bucket/object to write. The contents from stdin.")
-	publicObject = flag.Bool("write_object_is_public", false, "Whether the object created by --write_object should be public.")
-)
-
-func readFile(v string) string {
-	slurp, err := ioutil.ReadFile(v)
-	if err != nil {
-		log.Fatalf("Error reading %s: %v", v, err)
-	}
-	return strings.TrimSpace(string(slurp))
-}
-
-var config = &oauth2.Config{
-	// The client-id and secret should be for an "Installed Application" when using
-	// the CLI. Later we'll use a web application with a callback.
-	ClientID:     readFile("client-id.dat"),
-	ClientSecret: readFile("client-secret.dat"),
-	Endpoint:     google.Endpoint,
-	Scopes: []string{
-		compute.DevstorageFullControlScope,
-		compute.ComputeScope,
-		"https://www.googleapis.com/auth/sqlservice",
-		"https://www.googleapis.com/auth/sqlservice.admin",
-	},
-	RedirectURL: "urn:ietf:wg:oauth:2.0:oob",
-}
-
-const baseConfig = `#cloud-config
-coreos:
-  units:
-    - name: h2demo.service
-      command: start
-      content: |
-        [Unit]
-        Description=HTTP2 Demo
-        
-        [Service]
-        ExecStartPre=/bin/bash -c 'mkdir -p /opt/bin && curl -s -o /opt/bin/h2demo http://storage.googleapis.com/http2-demo-server-tls/h2demo && chmod +x /opt/bin/h2demo'
-        ExecStart=/opt/bin/h2demo --prod
-        RestartSec=5s
-        Restart=always
-        Type=simple
-        
-        [Install]
-        WantedBy=multi-user.target
-`
-
-func main() {
-	flag.Parse()
-	if *proj == "" {
-		log.Fatalf("Missing --project flag")
-	}
-	prefix := "https://www.googleapis.com/compute/v1/projects/" + *proj
-	machType := prefix + "/zones/" + *zone + "/machineTypes/" + *mach
-
-	const tokenFileName = "token.dat"
-	tokenFile := tokenCacheFile(tokenFileName)
-	tokenSource := oauth2.ReuseTokenSource(nil, tokenFile)
-	token, err := tokenSource.Token()
-	if err != nil {
-		if *writeObject != "" {
-			log.Fatalf("Can't use --write_object without a valid token.dat file already cached.")
-		}
-		log.Printf("Error getting token from %s: %v", tokenFileName, err)
-		log.Printf("Get auth code from %v", config.AuthCodeURL("my-state"))
-		fmt.Print("\nEnter auth code: ")
-		sc := bufio.NewScanner(os.Stdin)
-		sc.Scan()
-		authCode := strings.TrimSpace(sc.Text())
-		token, err = config.Exchange(oauth2.NoContext, authCode)
-		if err != nil {
-			log.Fatalf("Error exchanging auth code for a token: %v", err)
-		}
-		if err := tokenFile.WriteToken(token); err != nil {
-			log.Fatalf("Error writing to %s: %v", tokenFileName, err)
-		}
-		tokenSource = oauth2.ReuseTokenSource(token, nil)
-	}
-
-	oauthClient := oauth2.NewClient(oauth2.NoContext, tokenSource)
-
-	if *writeObject != "" {
-		writeCloudStorageObject(oauthClient)
-		return
-	}
-
-	computeService, _ := compute.New(oauthClient)
-
-	natIP := *staticIP
-	if natIP == "" {
-		// Try to find it by name.
-		aggAddrList, err := computeService.Addresses.AggregatedList(*proj).Do()
-		if err != nil {
-			log.Fatal(err)
-		}
-		// http://godoc.org/code.google.com/p/google-api-go-client/compute/v1#AddressAggregatedList
-	IPLoop:
-		for _, asl := range aggAddrList.Items {
-			for _, addr := range asl.Addresses {
-				if addr.Name == *instName+"-ip" && addr.Status == "RESERVED" {
-					natIP = addr.Address
-					break IPLoop
-				}
-			}
-		}
-	}
-
-	cloudConfig := baseConfig
-	if *sshPub != "" {
-		key := strings.TrimSpace(readFile(*sshPub))
-		cloudConfig += fmt.Sprintf("\nssh_authorized_keys:\n    - %s\n", key)
-	}
-	if os.Getenv("USER") == "bradfitz" {
-		cloudConfig += fmt.Sprintf("\nssh_authorized_keys:\n    - %s\n", "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAwks9dwWKlRC+73gRbvYtVg0vdCwDSuIlyt4z6xa/YU/jTDynM4R4W10hm2tPjy8iR1k8XhDv4/qdxe6m07NjG/By1tkmGpm1mGwho4Pr5kbAAy/Qg+NLCSdAYnnE00FQEcFOC15GFVMOW2AzDGKisReohwH9eIzHPzdYQNPRWXE= bradfitz@papag.bradfitz.com")
-	}
-	const maxCloudConfig = 32 << 10 // per compute API docs
-	if len(cloudConfig) > maxCloudConfig {
-		log.Fatalf("cloud config length of %d bytes is over %d byte limit", len(cloudConfig), maxCloudConfig)
-	}
-
-	instance := &compute.Instance{
-		Name:        *instName,
-		Description: "Go Builder",
-		MachineType: machType,
-		Disks:       []*compute.AttachedDisk{instanceDisk(computeService)},
-		Tags: &compute.Tags{
-			Items: []string{"http-server", "https-server"},
-		},
-		Metadata: &compute.Metadata{
-			Items: []*compute.MetadataItems{
-				{
-					Key:   "user-data",
-					Value: &cloudConfig,
-				},
-			},
-		},
-		NetworkInterfaces: []*compute.NetworkInterface{
-			{
-				AccessConfigs: []*compute.AccessConfig{
-					{
-						Type:  "ONE_TO_ONE_NAT",
-						Name:  "External NAT",
-						NatIP: natIP,
-					},
-				},
-				Network: prefix + "/global/networks/default",
-			},
-		},
-		ServiceAccounts: []*compute.ServiceAccount{
-			{
-				Email: "default",
-				Scopes: []string{
-					compute.DevstorageFullControlScope,
-					compute.ComputeScope,
-				},
-			},
-		},
-	}
-
-	log.Printf("Creating instance...")
-	op, err := computeService.Instances.Insert(*proj, *zone, instance).Do()
-	if err != nil {
-		log.Fatalf("Failed to create instance: %v", err)
-	}
-	opName := op.Name
-	log.Printf("Created. Waiting on operation %v", opName)
-OpLoop:
-	for {
-		time.Sleep(2 * time.Second)
-		op, err := computeService.ZoneOperations.Get(*proj, *zone, opName).Do()
-		if err != nil {
-			log.Fatalf("Failed to get op %s: %v", opName, err)
-		}
-		switch op.Status {
-		case "PENDING", "RUNNING":
-			log.Printf("Waiting on operation %v", opName)
-			continue
-		case "DONE":
-			if op.Error != nil {
-				for _, operr := range op.Error.Errors {
-					log.Printf("Error: %+v", operr)
-				}
-				log.Fatalf("Failed to start.")
-			}
-			log.Printf("Success. %+v", op)
-			break OpLoop
-		default:
-			log.Fatalf("Unknown status %q: %+v", op.Status, op)
-		}
-	}
-
-	inst, err := computeService.Instances.Get(*proj, *zone, *instName).Do()
-	if err != nil {
-		log.Fatalf("Error getting instance after creation: %v", err)
-	}
-	ij, _ := json.MarshalIndent(inst, "", "    ")
-	log.Printf("Instance: %s", ij)
-}
-
-func instanceDisk(svc *compute.Service) *compute.AttachedDisk {
-	const imageURL = "https://www.googleapis.com/compute/v1/projects/coreos-cloud/global/images/coreos-stable-444-5-0-v20141016"
-	diskName := *instName + "-disk"
-
-	return &compute.AttachedDisk{
-		AutoDelete: true,
-		Boot:       true,
-		Type:       "PERSISTENT",
-		InitializeParams: &compute.AttachedDiskInitializeParams{
-			DiskName:    diskName,
-			SourceImage: imageURL,
-			DiskSizeGb:  50,
-		},
-	}
-}
-
-func writeCloudStorageObject(httpClient *http.Client) {
-	content := os.Stdin
-	const maxSlurp = 1 << 20
-	var buf bytes.Buffer
-	n, err := io.CopyN(&buf, content, maxSlurp)
-	if err != nil && err != io.EOF {
-		log.Fatalf("Error reading from stdin: %v, %v", n, err)
-	}
-	contentType := http.DetectContentType(buf.Bytes())
-
-	req, err := http.NewRequest("PUT", "https://storage.googleapis.com/"+*writeObject, io.MultiReader(&buf, content))
-	if err != nil {
-		log.Fatal(err)
-	}
-	req.Header.Set("x-goog-api-version", "2")
-	if *publicObject {
-		req.Header.Set("x-goog-acl", "public-read")
-	}
-	req.Header.Set("Content-Type", contentType)
-	res, err := httpClient.Do(req)
-	if err != nil {
-		log.Fatal(err)
-	}
-	if res.StatusCode != 200 {
-		res.Write(os.Stderr)
-		log.Fatalf("Failed.")
-	}
-	log.Printf("Success.")
-	os.Exit(0)
-}
-
-type tokenCacheFile string
-
-func (f tokenCacheFile) Token() (*oauth2.Token, error) {
-	slurp, err := ioutil.ReadFile(string(f))
-	if err != nil {
-		return nil, err
-	}
-	t := new(oauth2.Token)
-	if err := json.Unmarshal(slurp, t); err != nil {
-		return nil, err
-	}
-	return t, nil
-}
-
-func (f tokenCacheFile) WriteToken(t *oauth2.Token) error {
-	jt, err := json.Marshal(t)
-	if err != nil {
-		return err
-	}
-	return ioutil.WriteFile(string(f), jt, 0600)
-}
diff --git a/http2/h2demo/tmpl.go b/http2/h2demo/tmpl.go
index 504d6a7..09fc729 100644
--- a/http2/h2demo/tmpl.go
+++ b/http2/h2demo/tmpl.go
@@ -43,7 +43,7 @@
 <div style="padding:20px">
 
 
-<a href="https://{{.HTTPSHost}}/serverpush">HTTP/2 with Server Push</a> | <a href="http://{{.HTTPHost}}/serverpush">HTTP only</a>
+<a href="https://{{.HTTPSHost}}/serverpush">HTTP/2 with Server Push</a> | <a href="{{.HTTP1Prefix}}/serverpush">HTTP only</a>
 <div id="loadtimes"></div>
 
 </div>