Migrated all http mechanics to a central handler, changed static
content to all be front-loaded and thread safe
This commit is contained in:
parent
4d3a84b6b3
commit
90e419f5a4
16
main.go
16
main.go
@ -7,7 +7,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"qurl/pages"
|
"qurl/pages"
|
||||||
"qurl/static"
|
|
||||||
"qurl/storage"
|
"qurl/storage"
|
||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
@ -54,22 +53,15 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
root := &pages.RootHandler{Storage: stor}
|
||||||
mux.Handle("/index.html", &static.StaticContent{Content: "index.html"})
|
err = root.Init()
|
||||||
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()
|
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mux.Handle("/submit.html", submit)
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stdout, "qurl listening .. \n")
|
fmt.Fprintf(os.Stdout, "qurl listening .. \n")
|
||||||
err = http.Serve(listen, mux)
|
err = http.Serve(listen, root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Serve error: %s\n", err.Error())
|
fmt.Fprintf(os.Stderr, "Serve error: %s\n", err.Error())
|
||||||
}
|
}
|
||||||
|
75
pages/root.go
Normal file
75
pages/root.go
Normal 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
|
||||||
|
}
|
@ -3,36 +3,18 @@ package pages
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"net/http"
|
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"qurl/qurl"
|
"qurl/qurl"
|
||||||
"qurl/static"
|
|
||||||
"qurl/storage"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SubmitHandler struct {
|
|
||||||
Storage storage.Storage
|
|
||||||
template *template.Template
|
|
||||||
}
|
|
||||||
|
|
||||||
type submitPage struct {
|
type submitPage struct {
|
||||||
Message string
|
Message string
|
||||||
URL string
|
URL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SubmitHandler) Init() error {
|
func (ctx *RootHandler) ServeSubmit(w http.ResponseWriter, r *http.Request) {
|
||||||
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) {
|
|
||||||
var pg submitPage
|
var pg submitPage
|
||||||
|
|
||||||
u := r.FormValue("url")
|
u := r.FormValue("url")
|
||||||
@ -79,7 +61,7 @@ func (ctx *SubmitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err := ctx.template.Execute(&buf, pg)
|
err := ctx.submit.Execute(&buf, pg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, fmt.Sprintf("Template execute error: %s", err.Error()),
|
http.Error(w, fmt.Sprintf("Template execute error: %s", err.Error()),
|
||||||
http.StatusInternalServerError)
|
http.StatusInternalServerError)
|
||||||
|
@ -12,14 +12,58 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type StaticContent struct {
|
type StaticContent struct {
|
||||||
ContentType string
|
|
||||||
Content string
|
Content string
|
||||||
|
ContentType string
|
||||||
ETag string
|
ETag string
|
||||||
GZIPContent []byte
|
GZIPContent []byte
|
||||||
GZIPETag string
|
GZIPETag string
|
||||||
DisableGZIP bool
|
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) {
|
func (ctx *StaticContent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// By default, don't use gzip
|
// By default, don't use gzip
|
||||||
useGZIP := false
|
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")
|
useGZIP = strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If gzip is enabled, and there's no gzip etag,
|
// Use the correct 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var localETag string
|
var localETag string
|
||||||
if useGZIP {
|
if useGZIP {
|
||||||
localETag = ctx.GZIPETag
|
localETag = ctx.GZIPETag
|
||||||
} else {
|
} else {
|
||||||
// Generate an ETag for content if necessary
|
|
||||||
if ctx.ETag == "" {
|
|
||||||
ctx.ETag = fmt.Sprintf("%x", md5.Sum(Assets[ctx.Content]))
|
|
||||||
}
|
|
||||||
localETag = ctx.ETag
|
localETag = ctx.ETag
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,17 +89,6 @@ func (ctx *StaticContent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
w.Header().Set("ETag", localETag)
|
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)
|
w.Header().Set("Content-Type", ctx.ContentType)
|
||||||
|
|
||||||
// Finally, write our content
|
// Finally, write our content
|
||||||
|
Loading…
Reference in New Issue
Block a user