Migrated all http mechanics to a central handler, changed static

content to all be front-loaded and thread safe
This commit is contained in:
Christopher Ramey 2018-11-18 05:28:02 +00:00 committed by cdramey
parent 4d3a84b6b3
commit 90e419f5a4
4 changed files with 128 additions and 80 deletions

16
main.go
View File

@ -7,7 +7,6 @@ import (
"net/http"
"os"
"qurl/pages"
"qurl/static"
"qurl/storage"
"runtime"
)
@ -54,22 +53,15 @@ func main() {
return
}
mux := http.NewServeMux()
mux.Handle("/index.html", &static.StaticContent{Content: "index.html"})
mux.Handle("/favicon.ico", &static.StaticContent{Content: "favicon.ico"})
mux.Handle("/qurl.css", &static.StaticContent{Content: "qurl.css"})
submit := &pages.SubmitHandler{Storage: stor}
err = submit.Init()
root := &pages.RootHandler{Storage: stor}
err = root.Init()
if err != nil {
fmt.Fprintf(os.Stderr, "Submit init error: %s\n", err.Error())
fmt.Fprintf(os.Stderr, "Handler Init Error: %s\n", err.Error())
return
}
mux.Handle("/submit.html", submit)
fmt.Fprintf(os.Stdout, "qurl listening .. \n")
err = http.Serve(listen, mux)
err = http.Serve(listen, root)
if err != nil {
fmt.Fprintf(os.Stderr, "Serve error: %s\n", err.Error())
}

75
pages/root.go Normal file
View File

@ -0,0 +1,75 @@
package pages
import (
"fmt"
"html/template"
"net/http"
"qurl/static"
"qurl/storage"
)
type RootHandler struct {
Storage storage.Storage
index *static.StaticContent
css *static.StaticContent
favi *static.StaticContent
submit *template.Template
}
func (ctx *RootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fname := r.URL.Path
switch fname {
case "/index.html":
fallthrough
case "/":
ctx.index.ServeHTTP(w, r)
case "/submit.html":
ctx.ServeSubmit(w, r)
case "/qurl.css":
ctx.css.ServeHTTP(w, r)
case "/favicon.ico":
ctx.favi.ServeHTTP(w, r)
default:
fmt.Printf("Path: %s\n", fname)
}
}
func (ctx *RootHandler) Init() error {
// Initialize the static content object for the index page
index := &static.StaticContent{Content: "index.html"}
err := index.Init()
if err != nil {
return err
}
ctx.index = index
// Initialize the static content object for the css
css := &static.StaticContent{Content: "qurl.css"}
err = css.Init()
if err != nil {
return err
}
ctx.css = css
// Initialize the static content object for the css
favi := &static.StaticContent{Content: "favicon.ico"}
err = favi.Init()
if err != nil {
return err
}
ctx.favi = favi
// Initialize submit page template
ctx.submit = template.New("submit.html")
_, err = ctx.submit.Parse(string(static.Assets["submit.html"]))
if err != nil {
return err
}
return nil
}

View File

@ -3,36 +3,18 @@ package pages
import (
"bytes"
"fmt"
"html/template"
"net/http"
"net"
"net/http"
"qurl/qurl"
"qurl/static"
"qurl/storage"
"time"
)
type SubmitHandler struct {
Storage storage.Storage
template *template.Template
}
type submitPage struct {
Message string
URL string
}
func (ctx *SubmitHandler) Init() error {
ctx.template = template.New("submit.html")
_, err := ctx.template.Parse(string(static.Assets["submit.html"]))
if err != nil {
return err
}
return nil
}
func (ctx *SubmitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (ctx *RootHandler) ServeSubmit(w http.ResponseWriter, r *http.Request) {
var pg submitPage
u := r.FormValue("url")
@ -79,7 +61,7 @@ func (ctx *SubmitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
var buf bytes.Buffer
err := ctx.template.Execute(&buf, pg)
err := ctx.submit.Execute(&buf, pg)
if err != nil {
http.Error(w, fmt.Sprintf("Template execute error: %s", err.Error()),
http.StatusInternalServerError)

View File

@ -12,14 +12,58 @@ import (
)
type StaticContent struct {
ContentType string
Content string
ContentType string
ETag string
GZIPContent []byte
GZIPETag string
DisableGZIP bool
}
func (ctx *StaticContent) Init() error {
// Do we have a content type? If not, initialize it
if ctx.ContentType == "" {
ext := path.Ext(ctx.Content)
ctx.ContentType = mime.TypeByExtension(ext)
// Fallback to default mime type
if ctx.ContentType == "" {
ctx.ContentType = "application/octet-stream"
}
}
// Do we have an etag? If not, generate one
if ctx.ETag == "" {
ctx.ETag = fmt.Sprintf("%x", md5.Sum(Assets[ctx.Content]))
}
// If gzip is allowed and we have no gzip etag, generate
// gzip content and etag
if !ctx.DisableGZIP && ctx.GZIPETag == "" {
var buf bytes.Buffer
gz, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
defer gz.Close()
if _, err := gz.Write(Assets[ctx.Content]); err != nil {
return err
}
if err := gz.Flush(); err != nil {
return err
}
// Check if GZIP actually resulted in a smaller file
if buf.Len() < len(Assets[ctx.Content]) {
ctx.GZIPContent = buf.Bytes()
ctx.GZIPETag = fmt.Sprintf("%x", md5.Sum(Assets[ctx.Content]))
} else {
// If gzip turns out to be ineffective, disable it
ctx.DisableGZIP = true
}
}
return nil
}
func (ctx *StaticContent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// By default, don't use gzip
useGZIP := false
@ -29,45 +73,11 @@ func (ctx *StaticContent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
useGZIP = strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
}
// If gzip is enabled, and there's no gzip etag,
// generate the gzip'd content plus the etag
if useGZIP && len(ctx.GZIPETag) == 0 {
var buf bytes.Buffer
gz, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
defer gz.Close()
if _, err := gz.Write(Assets[ctx.Content]); err != nil {
http.Error(w, fmt.Sprintf("GZIP write error: %s", err.Error()),
http.StatusInternalServerError)
return
}
if err := gz.Flush(); err != nil {
http.Error(w, fmt.Sprintf("GZIP flush error: %s", err.Error()),
http.StatusInternalServerError)
return
}
// Check if GZIP actually resulted in a smaller file
if buf.Len() < len(Assets[ctx.Content]) {
ctx.GZIPContent = buf.Bytes()
ctx.GZIPETag = fmt.Sprintf("%x", md5.Sum(Assets[ctx.Content]))
} else {
// If gzip turns out to be ineffective, disable it
ctx.DisableGZIP = true
useGZIP = false
}
}
// Use the correct etag
var localETag string
if useGZIP {
localETag = ctx.GZIPETag
} else {
// Generate an ETag for content if necessary
if ctx.ETag == "" {
ctx.ETag = fmt.Sprintf("%x", md5.Sum(Assets[ctx.Content]))
}
localETag = ctx.ETag
}
@ -79,17 +89,6 @@ func (ctx *StaticContent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
w.Header().Set("ETag", localETag)
// Check the content type, if we don't already
// have one, make one
if ctx.ContentType == "" {
ext := path.Ext(ctx.Content)
ctx.ContentType = mime.TypeByExtension(ext)
// Fallback to default mime type
if ctx.ContentType == "" {
ctx.ContentType = "application/octet-stream"
}
}
w.Header().Set("Content-Type", ctx.ContentType)
// Finally, write our content