Add HTTP/2 push
This commit is contained in:
parent
8d4b707757
commit
37c8e780bc
@ -1,9 +1,6 @@
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.5.4
|
|
||||||
- 1.6.4
|
|
||||||
- 1.7.5
|
|
||||||
- 1.8.1
|
- 1.8.1
|
||||||
- tip
|
- tip
|
||||||
|
|
||||||
|
@ -21,12 +21,7 @@ There is a few different ways of getting it:
|
|||||||
- [Other versions](https://github.com/khlieng/dispatch/releases)
|
- [Other versions](https://github.com/khlieng/dispatch/releases)
|
||||||
|
|
||||||
### 2. Go
|
### 2. Go
|
||||||
This requires a [Go environment](http://golang.org/doc/install).
|
This requires a [Go environment](http://golang.org/doc/install), version 1.8 or greater.
|
||||||
|
|
||||||
If running go 1.5 this environment variable is needed, versions <1.5 are not supported:
|
|
||||||
```bash
|
|
||||||
export GO15VENDOREXPERIMENT=1
|
|
||||||
```
|
|
||||||
|
|
||||||
Fetch, compile and run dispatch:
|
Fetch, compile and run dispatch:
|
||||||
```bash
|
```bash
|
||||||
|
@ -28,6 +28,7 @@ type File struct {
|
|||||||
Path string
|
Path string
|
||||||
Asset string
|
Asset string
|
||||||
GzipAsset []byte
|
GzipAsset []byte
|
||||||
|
Hash string
|
||||||
ContentType string
|
ContentType string
|
||||||
CacheControl string
|
CacheControl string
|
||||||
Compressed bool
|
Compressed bool
|
||||||
@ -69,7 +70,8 @@ func initFileServer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hash := md5.Sum(data)
|
hash := md5.Sum(data)
|
||||||
files[0].Path = "bundle." + base64.RawURLEncoding.EncodeToString(hash[:]) + ".js"
|
files[0].Hash = base64.RawURLEncoding.EncodeToString(hash[:])
|
||||||
|
files[0].Path = "bundle." + files[0].Hash + ".js"
|
||||||
|
|
||||||
br, err := brotli.NewReader(bytes.NewReader(data), nil)
|
br, err := brotli.NewReader(bytes.NewReader(data), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -92,7 +94,8 @@ func initFileServer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hash = md5.Sum(data)
|
hash = md5.Sum(data)
|
||||||
files[1].Path = "bundle." + base64.RawURLEncoding.EncodeToString(hash[:]) + ".css"
|
files[1].Hash = base64.RawURLEncoding.EncodeToString(hash[:])
|
||||||
|
files[1].Path = "bundle." + files[1].Hash + ".css"
|
||||||
|
|
||||||
br.Reset(bytes.NewReader(data))
|
br.Reset(bytes.NewReader(data))
|
||||||
buf = &bytes.Buffer{}
|
buf = &bytes.Buffer{}
|
||||||
@ -196,6 +199,36 @@ func serveIndex(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Header().Set("Strict-Transport-Security", hstsHeader)
|
w.Header().Set("Strict-Transport-Security", hstsHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pusher, ok := w.(http.Pusher); ok {
|
||||||
|
options := &http.PushOptions{
|
||||||
|
Header: http.Header{
|
||||||
|
"Accept-Encoding": r.Header["Accept-Encoding"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cookie, err := r.Cookie("push")
|
||||||
|
if err != nil {
|
||||||
|
err = pusher.Push("/"+files[1].Path, options)
|
||||||
|
err = pusher.Push("/"+files[0].Path, options)
|
||||||
|
setPushCookie(w, r)
|
||||||
|
} else {
|
||||||
|
pushed := false
|
||||||
|
|
||||||
|
if files[1].Hash != cookie.Value[22:] {
|
||||||
|
pusher.Push("/"+files[1].Path, options)
|
||||||
|
pushed = true
|
||||||
|
}
|
||||||
|
if files[0].Hash != cookie.Value[:22] {
|
||||||
|
pusher.Push("/"+files[0].Path, options)
|
||||||
|
pushed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if pushed {
|
||||||
|
setPushCookie(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
|
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
|
||||||
w.Header().Set("Content-Encoding", "gzip")
|
w.Header().Set("Content-Encoding", "gzip")
|
||||||
|
|
||||||
@ -207,6 +240,17 @@ func serveIndex(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setPushCookie(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: "push",
|
||||||
|
Value: files[0].Hash + files[1].Hash,
|
||||||
|
Path: "/",
|
||||||
|
Expires: time.Now().AddDate(1, 0, 0),
|
||||||
|
HttpOnly: true,
|
||||||
|
Secure: r.TLS != nil,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func serveFile(w http.ResponseWriter, r *http.Request, file *File) {
|
func serveFile(w http.ResponseWriter, r *http.Request, file *File) {
|
||||||
info, err := assets.AssetInfo(file.Asset)
|
info, err := assets.AssetInfo(file.Asset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -10,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"golang.org/x/net/http2"
|
|
||||||
|
|
||||||
"github.com/khlieng/dispatch/letsencrypt"
|
"github.com/khlieng/dispatch/letsencrypt"
|
||||||
"github.com/khlieng/dispatch/storage"
|
"github.com/khlieng/dispatch/storage"
|
||||||
@ -59,8 +59,6 @@ func startHTTP() {
|
|||||||
Handler: http.HandlerFunc(serve),
|
Handler: http.HandlerFunc(serve),
|
||||||
}
|
}
|
||||||
|
|
||||||
http2.ConfigureServer(server, nil)
|
|
||||||
|
|
||||||
if certExists() {
|
if certExists() {
|
||||||
log.Println("[HTTPS] Listening on port", portHTTPS)
|
log.Println("[HTTPS] Listening on port", portHTTPS)
|
||||||
server.ListenAndServeTLS(viper.GetString("https.cert"), viper.GetString("https.key"))
|
server.ListenAndServeTLS(viper.GetString("https.cert"), viper.GetString("https.key"))
|
||||||
@ -79,10 +77,12 @@ func startHTTP() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
server.TLSConfig.GetCertificate = letsEncrypt.GetCertificate
|
server.TLSConfig = &tls.Config{
|
||||||
|
GetCertificate: letsEncrypt.GetCertificate,
|
||||||
|
}
|
||||||
|
|
||||||
log.Println("[HTTPS] Listening on port", portHTTPS)
|
log.Println("[HTTPS] Listening on port", portHTTPS)
|
||||||
log.Fatal(listenAndServeTLS(server))
|
log.Fatal(server.ListenAndServeTLS("", ""))
|
||||||
} else {
|
} else {
|
||||||
log.Fatal("Could not locate SSL certificate or private key")
|
log.Fatal("Could not locate SSL certificate or private key")
|
||||||
}
|
}
|
||||||
|
@ -1,43 +1,11 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func listenAndServeTLS(srv *http.Server) error {
|
|
||||||
if srv.TLSConfig.NextProtos == nil {
|
|
||||||
srv.TLSConfig.NextProtos = []string{"http/1.1"}
|
|
||||||
}
|
|
||||||
|
|
||||||
ln, err := net.Listen("tcp", srv.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig)
|
|
||||||
return srv.Serve(tlsListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func certExists() bool {
|
func certExists() bool {
|
||||||
cert := viper.GetString("https.cert")
|
cert := viper.GetString("https.cert")
|
||||||
key := viper.GetString("https.key")
|
key := viper.GetString("https.key")
|
||||||
|
Loading…
Reference in New Issue
Block a user