Use standard tab indentation for web.go source files
This is a whitespace change only. Run all the source files through `gofmt`. Previously, all web.go files were indented using four spaces. Convert all the spaces into tabs. Also, remove `Makefile`, which contained the old formatting command.
This commit is contained in:
6
Makefile
6
Makefile
@@ -1,6 +0,0 @@
|
||||
GOFMT=gofmt -s -tabs=false -tabwidth=4
|
||||
|
||||
GOFILES=$(wildcard *.go **/*.go)
|
||||
|
||||
format:
|
||||
${GOFMT} -w ${GOFILES}
|
||||
@@ -1,10 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hoisie/web"
|
||||
"math/rand"
|
||||
"time"
|
||||
"fmt"
|
||||
"github.com/hoisie/web"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
var form = `<form action="say" method="POST"><input name="said"><input type="submit"></form>`
|
||||
@@ -12,22 +12,22 @@ var form = `<form action="say" method="POST"><input name="said"><input type="sub
|
||||
var users = map[string]string{}
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
web.Config.CookieSecret = "7C19QRmwf3mHZ9CPAaPQ0hsWeufKd"
|
||||
web.Get("/", func(ctx *web.Context) string {
|
||||
ctx.Redirect(302, "/said")
|
||||
return ""
|
||||
})
|
||||
web.Get("/said", func() string { return form })
|
||||
web.Post("/say", func(ctx *web.Context) string {
|
||||
uid := fmt.Sprintf("%d\n", rand.Int63())
|
||||
ctx.SetSecureCookie("user", uid, 3600)
|
||||
users[uid] = ctx.Params["said"]
|
||||
return `<a href="/final">Click Here</a>`
|
||||
})
|
||||
web.Get("/final", func(ctx *web.Context) string {
|
||||
uid, _ := ctx.GetSecureCookie("user")
|
||||
return "You said " + users[uid]
|
||||
})
|
||||
web.Run("0.0.0.0:9999")
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
web.Config.CookieSecret = "7C19QRmwf3mHZ9CPAaPQ0hsWeufKd"
|
||||
web.Get("/", func(ctx *web.Context) string {
|
||||
ctx.Redirect(302, "/said")
|
||||
return ""
|
||||
})
|
||||
web.Get("/said", func() string { return form })
|
||||
web.Post("/say", func(ctx *web.Context) string {
|
||||
uid := fmt.Sprintf("%d\n", rand.Int63())
|
||||
ctx.SetSecureCookie("user", uid, 3600)
|
||||
users[uid] = ctx.Params["said"]
|
||||
return `<a href="/final">Click Here</a>`
|
||||
})
|
||||
web.Get("/final", func(ctx *web.Context) string {
|
||||
uid, _ := ctx.GetSecureCookie("user")
|
||||
return "You said " + users[uid]
|
||||
})
|
||||
web.Run("0.0.0.0:9999")
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hoisie/web"
|
||||
"html"
|
||||
"fmt"
|
||||
"github.com/hoisie/web"
|
||||
"html"
|
||||
)
|
||||
|
||||
var cookieName = "cookie"
|
||||
@@ -24,28 +24,28 @@ var form = `
|
||||
`
|
||||
|
||||
func index(ctx *web.Context) string {
|
||||
cookie, _ := ctx.Request.Cookie(cookieName)
|
||||
var top string
|
||||
if cookie == nil {
|
||||
top = fmt.Sprintf(notice, "The cookie has not been set")
|
||||
} else {
|
||||
var val = html.EscapeString(cookie.Value)
|
||||
top = fmt.Sprintf(notice, "The value of the cookie is '"+val+"'.")
|
||||
}
|
||||
return top + form
|
||||
cookie, _ := ctx.Request.Cookie(cookieName)
|
||||
var top string
|
||||
if cookie == nil {
|
||||
top = fmt.Sprintf(notice, "The cookie has not been set")
|
||||
} else {
|
||||
var val = html.EscapeString(cookie.Value)
|
||||
top = fmt.Sprintf(notice, "The value of the cookie is '"+val+"'.")
|
||||
}
|
||||
return top + form
|
||||
}
|
||||
|
||||
func update(ctx *web.Context) {
|
||||
if ctx.Params["submit"] == "Delete" {
|
||||
ctx.SetCookie(web.NewCookie(cookieName, "", -1))
|
||||
} else {
|
||||
ctx.SetCookie(web.NewCookie(cookieName, ctx.Params["cookie"], 0))
|
||||
}
|
||||
ctx.Redirect(301, "/")
|
||||
if ctx.Params["submit"] == "Delete" {
|
||||
ctx.SetCookie(web.NewCookie(cookieName, "", -1))
|
||||
} else {
|
||||
ctx.SetCookie(web.NewCookie(cookieName, ctx.Params["cookie"], 0))
|
||||
}
|
||||
ctx.Redirect(301, "/")
|
||||
}
|
||||
|
||||
func main() {
|
||||
web.Get("/", index)
|
||||
web.Post("/update", update)
|
||||
web.Run("0.0.0.0:9999")
|
||||
web.Get("/", index)
|
||||
web.Post("/update", update)
|
||||
web.Run("0.0.0.0:9999")
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/hoisie/web"
|
||||
"github.com/hoisie/web"
|
||||
)
|
||||
|
||||
func hello(val string) string { return "hello " + val }
|
||||
|
||||
func main() {
|
||||
web.Get("/(.*)", hello)
|
||||
web.Run("0.0.0.0:9999")
|
||||
web.Get("/(.*)", hello)
|
||||
web.Run("0.0.0.0:9999")
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/hoisie/web"
|
||||
"log"
|
||||
"os"
|
||||
"github.com/hoisie/web"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func hello(val string) string { return "hello " + val }
|
||||
|
||||
func main() {
|
||||
f, err := os.Create("server.log")
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
logger := log.New(f, "", log.Ldate|log.Ltime)
|
||||
web.Get("/(.*)", hello)
|
||||
web.SetLogger(logger)
|
||||
web.Run("0.0.0.0:9999")
|
||||
f, err := os.Create("server.log")
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
logger := log.New(f, "", log.Ldate|log.Ltime)
|
||||
web.Get("/(.*)", hello)
|
||||
web.SetLogger(logger)
|
||||
web.Run("0.0.0.0:9999")
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"github.com/hoisie/web"
|
||||
"io"
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"github.com/hoisie/web"
|
||||
"io"
|
||||
)
|
||||
|
||||
func Md5(r io.Reader) string {
|
||||
hash := md5.New()
|
||||
io.Copy(hash, r)
|
||||
return fmt.Sprintf("%x", hash.Sum(nil))
|
||||
hash := md5.New()
|
||||
io.Copy(hash, r)
|
||||
return fmt.Sprintf("%x", hash.Sum(nil))
|
||||
}
|
||||
|
||||
var page = `
|
||||
@@ -38,25 +38,25 @@ var page = `
|
||||
func index() string { return page }
|
||||
|
||||
func multipart(ctx *web.Context) string {
|
||||
ctx.Request.ParseMultipartForm(10 * 1024 * 1024)
|
||||
form := ctx.Request.MultipartForm
|
||||
var output bytes.Buffer
|
||||
output.WriteString("<p>input1: " + form.Value["input1"][0] + "</p>")
|
||||
output.WriteString("<p>input2: " + form.Value["input2"][0] + "</p>")
|
||||
ctx.Request.ParseMultipartForm(10 * 1024 * 1024)
|
||||
form := ctx.Request.MultipartForm
|
||||
var output bytes.Buffer
|
||||
output.WriteString("<p>input1: " + form.Value["input1"][0] + "</p>")
|
||||
output.WriteString("<p>input2: " + form.Value["input2"][0] + "</p>")
|
||||
|
||||
fileHeader := form.File["file"][0]
|
||||
filename := fileHeader.Filename
|
||||
file, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
fileHeader := form.File["file"][0]
|
||||
filename := fileHeader.Filename
|
||||
file, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
output.WriteString("<p>file: " + filename + " " + Md5(file) + "</p>")
|
||||
return output.String()
|
||||
output.WriteString("<p>file: " + filename + " " + Md5(file) + "</p>")
|
||||
return output.String()
|
||||
}
|
||||
|
||||
func main() {
|
||||
web.Get("/", index)
|
||||
web.Post("/multipart", multipart)
|
||||
web.Run("0.0.0.0:9999")
|
||||
web.Get("/", index)
|
||||
web.Post("/multipart", multipart)
|
||||
web.Run("0.0.0.0:9999")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/hoisie/web"
|
||||
"github.com/hoisie/web"
|
||||
)
|
||||
|
||||
func hello1(val string) string { return "hello1 " + val }
|
||||
@@ -9,12 +9,12 @@ func hello1(val string) string { return "hello1 " + val }
|
||||
func hello2(val string) string { return "hello2 " + val }
|
||||
|
||||
func main() {
|
||||
var server1 web.Server
|
||||
var server2 web.Server
|
||||
var server1 web.Server
|
||||
var server2 web.Server
|
||||
|
||||
server1.Get("/(.*)", hello1)
|
||||
go server1.Run("0.0.0.0:9999")
|
||||
server2.Get("/(.*)", hello2)
|
||||
go server2.Run("0.0.0.0:8999")
|
||||
<-make(chan int)
|
||||
server1.Get("/(.*)", hello1)
|
||||
go server1.Run("0.0.0.0:9999")
|
||||
server2.Get("/(.*)", hello2)
|
||||
go server2.Run("0.0.0.0:8999")
|
||||
<-make(chan int)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hoisie/web"
|
||||
"fmt"
|
||||
"github.com/hoisie/web"
|
||||
)
|
||||
|
||||
var page = `
|
||||
@@ -33,11 +33,11 @@ var page = `
|
||||
func index() string { return page }
|
||||
|
||||
func process(ctx *web.Context) string {
|
||||
return fmt.Sprintf("%v\n", ctx.Params)
|
||||
return fmt.Sprintf("%v\n", ctx.Params)
|
||||
}
|
||||
|
||||
func main() {
|
||||
web.Get("/", index)
|
||||
web.Post("/process", process)
|
||||
web.Run("0.0.0.0:9999")
|
||||
web.Get("/", index)
|
||||
web.Post("/process", process)
|
||||
web.Run("0.0.0.0:9999")
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/hoisie/web"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
"github.com/hoisie/web"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func hello(ctx *web.Context, num string) {
|
||||
flusher, _ := ctx.ResponseWriter.(http.Flusher)
|
||||
flusher.Flush()
|
||||
n, _ := strconv.ParseInt(num, 10, 64)
|
||||
for i := int64(0); i < n; i++ {
|
||||
ctx.WriteString("<br>hello world</br>")
|
||||
flusher.Flush()
|
||||
time.Sleep(1e9)
|
||||
}
|
||||
flusher, _ := ctx.ResponseWriter.(http.Flusher)
|
||||
flusher.Flush()
|
||||
n, _ := strconv.ParseInt(num, 10, 64)
|
||||
for i := int64(0); i < n; i++ {
|
||||
ctx.WriteString("<br>hello world</br>")
|
||||
flusher.Flush()
|
||||
time.Sleep(1e9)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
web.Get("/([0-9]+)", hello)
|
||||
web.Run("0.0.0.0:9999")
|
||||
web.Get("/([0-9]+)", hello)
|
||||
web.Run("0.0.0.0:9999")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/hoisie/web"
|
||||
"crypto/tls"
|
||||
"github.com/hoisie/web"
|
||||
)
|
||||
|
||||
// an arbitrary self-signed certificate, generated with
|
||||
@@ -50,19 +50,19 @@ gWrxykqyLToIiAuL+pvC3Jv8IOPIiVFsY032rOqcwSGdVUyhTsG28+7KnR6744tM
|
||||
func hello(val string) string { return "hello " + val }
|
||||
|
||||
func main() {
|
||||
config := tls.Config{
|
||||
Time: nil,
|
||||
}
|
||||
config := tls.Config{
|
||||
Time: nil,
|
||||
}
|
||||
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
var err error
|
||||
config.Certificates[0], err = tls.X509KeyPair([]byte(cert), []byte(pkey))
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
var err error
|
||||
config.Certificates[0], err = tls.X509KeyPair([]byte(cert), []byte(pkey))
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// you must access the server with an HTTPS address, i.e https://localhost:9999/world
|
||||
web.Get("/(.*)", hello)
|
||||
web.RunTLS("0.0.0.0:9999", &config)
|
||||
// you must access the server with an HTTPS address, i.e https://localhost:9999/world
|
||||
web.Get("/(.*)", hello)
|
||||
web.RunTLS("0.0.0.0:9999", &config)
|
||||
}
|
||||
|
||||
34
fcgi.go
34
fcgi.go
@@ -1,27 +1,27 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http/fcgi"
|
||||
"net"
|
||||
"net/http/fcgi"
|
||||
)
|
||||
|
||||
func (s *Server) listenAndServeFcgi(addr string) error {
|
||||
var l net.Listener
|
||||
var err error
|
||||
var l net.Listener
|
||||
var err error
|
||||
|
||||
//if the path begins with a "/", assume it's a unix address
|
||||
if addr[0] == '/' {
|
||||
l, err = net.Listen("unix", addr)
|
||||
} else {
|
||||
l, err = net.Listen("tcp", addr)
|
||||
}
|
||||
//if the path begins with a "/", assume it's a unix address
|
||||
if addr[0] == '/' {
|
||||
l, err = net.Listen("unix", addr)
|
||||
} else {
|
||||
l, err = net.Listen("tcp", addr)
|
||||
}
|
||||
|
||||
//save the listener so it can be closed
|
||||
s.l = l
|
||||
//save the listener so it can be closed
|
||||
s.l = l
|
||||
|
||||
if err != nil {
|
||||
s.Logger.Println("FCGI listen error", err.Error())
|
||||
return err
|
||||
}
|
||||
return fcgi.Serve(s.l, s)
|
||||
if err != nil {
|
||||
s.Logger.Println("FCGI listen error", err.Error())
|
||||
return err
|
||||
}
|
||||
return fcgi.Serve(s.l, s)
|
||||
}
|
||||
|
||||
142
helpers.go
142
helpers.go
@@ -1,59 +1,59 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// internal utility methods
|
||||
func webTime(t time.Time) string {
|
||||
ftime := t.Format(time.RFC1123)
|
||||
if strings.HasSuffix(ftime, "UTC") {
|
||||
ftime = ftime[0:len(ftime)-3] + "GMT"
|
||||
}
|
||||
return ftime
|
||||
ftime := t.Format(time.RFC1123)
|
||||
if strings.HasSuffix(ftime, "UTC") {
|
||||
ftime = ftime[0:len(ftime)-3] + "GMT"
|
||||
}
|
||||
return ftime
|
||||
}
|
||||
|
||||
func dirExists(dir string) bool {
|
||||
d, e := os.Stat(dir)
|
||||
switch {
|
||||
case e != nil:
|
||||
return false
|
||||
case !d.IsDir():
|
||||
return false
|
||||
}
|
||||
d, e := os.Stat(dir)
|
||||
switch {
|
||||
case e != nil:
|
||||
return false
|
||||
case !d.IsDir():
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return true
|
||||
}
|
||||
|
||||
func fileExists(dir string) bool {
|
||||
info, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
info, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return !info.IsDir()
|
||||
return !info.IsDir()
|
||||
}
|
||||
|
||||
// Urlencode is a helper method that converts a map into URL-encoded form data.
|
||||
// It is a useful when constructing HTTP POST requests.
|
||||
func Urlencode(data map[string]string) string {
|
||||
var buf bytes.Buffer
|
||||
for k, v := range data {
|
||||
buf.WriteString(url.QueryEscape(k))
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(url.QueryEscape(v))
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
s := buf.String()
|
||||
return s[0 : len(s)-1]
|
||||
var buf bytes.Buffer
|
||||
for k, v := range data {
|
||||
buf.WriteString(url.QueryEscape(k))
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(url.QueryEscape(v))
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
s := buf.String()
|
||||
return s[0 : len(s)-1]
|
||||
}
|
||||
|
||||
var slugRegex = regexp.MustCompile(`(?i:[^a-z0-9\-_])`)
|
||||
@@ -62,50 +62,50 @@ var slugRegex = regexp.MustCompile(`(?i:[^a-z0-9\-_])`)
|
||||
// It's used to return clean, URL-friendly strings that can be
|
||||
// used in routing.
|
||||
func Slug(s string, sep string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
slug := slugRegex.ReplaceAllString(s, sep)
|
||||
if slug == "" {
|
||||
return ""
|
||||
}
|
||||
quoted := regexp.QuoteMeta(sep)
|
||||
sepRegex := regexp.MustCompile("(" + quoted + "){2,}")
|
||||
slug = sepRegex.ReplaceAllString(slug, sep)
|
||||
sepEnds := regexp.MustCompile("^" + quoted + "|" + quoted + "$")
|
||||
slug = sepEnds.ReplaceAllString(slug, "")
|
||||
return strings.ToLower(slug)
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
slug := slugRegex.ReplaceAllString(s, sep)
|
||||
if slug == "" {
|
||||
return ""
|
||||
}
|
||||
quoted := regexp.QuoteMeta(sep)
|
||||
sepRegex := regexp.MustCompile("(" + quoted + "){2,}")
|
||||
slug = sepRegex.ReplaceAllString(slug, sep)
|
||||
sepEnds := regexp.MustCompile("^" + quoted + "|" + quoted + "$")
|
||||
slug = sepEnds.ReplaceAllString(slug, "")
|
||||
return strings.ToLower(slug)
|
||||
}
|
||||
|
||||
// NewCookie is a helper method that returns a new http.Cookie object.
|
||||
// Duration is specified in seconds. If the duration is zero, the cookie is permanent.
|
||||
// This can be used in conjunction with ctx.SetCookie.
|
||||
func NewCookie(name string, value string, age int64) *http.Cookie {
|
||||
var utctime time.Time
|
||||
if age == 0 {
|
||||
// 2^31 - 1 seconds (roughly 2038)
|
||||
utctime = time.Unix(2147483647, 0)
|
||||
} else {
|
||||
utctime = time.Unix(time.Now().Unix()+age, 0)
|
||||
}
|
||||
return &http.Cookie{Name: name, Value: value, Expires: utctime}
|
||||
var utctime time.Time
|
||||
if age == 0 {
|
||||
// 2^31 - 1 seconds (roughly 2038)
|
||||
utctime = time.Unix(2147483647, 0)
|
||||
} else {
|
||||
utctime = time.Unix(time.Now().Unix()+age, 0)
|
||||
}
|
||||
return &http.Cookie{Name: name, Value: value, Expires: utctime}
|
||||
}
|
||||
|
||||
// GetBasicAuth is a helper method of *Context that returns the decoded
|
||||
// user and password from the *Context's authorization header
|
||||
func (ctx *Context) GetBasicAuth() (string, string, error) {
|
||||
authHeader := ctx.Request.Header["Authorization"][0]
|
||||
authString := strings.Split(string(authHeader), " ")
|
||||
if authString[0] != "Basic" {
|
||||
return "", "", errors.New("Not Basic Authentication")
|
||||
}
|
||||
decodedAuth, err := base64.StdEncoding.DecodeString(authString[1])
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
authSlice := strings.Split(string(decodedAuth), ":")
|
||||
if len(authSlice) != 2 {
|
||||
return "", "", errors.New("Error delimiting authString into username/password. Malformed input: " + authString[1])
|
||||
}
|
||||
return authSlice[0], authSlice[1], nil
|
||||
authHeader := ctx.Request.Header["Authorization"][0]
|
||||
authString := strings.Split(string(authHeader), " ")
|
||||
if authString[0] != "Basic" {
|
||||
return "", "", errors.New("Not Basic Authentication")
|
||||
}
|
||||
decodedAuth, err := base64.StdEncoding.DecodeString(authString[1])
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
authSlice := strings.Split(string(decodedAuth), ":")
|
||||
if len(authSlice) != 2 {
|
||||
return "", "", errors.New("Error delimiting authString into username/password. Malformed input: " + authString[1])
|
||||
}
|
||||
return authSlice[0], authSlice[1], nil
|
||||
}
|
||||
|
||||
254
scgi.go
254
scgi.go
@@ -1,180 +1,180 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cgi"
|
||||
"strconv"
|
||||
"strings"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cgi"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type scgiBody struct {
|
||||
reader io.Reader
|
||||
conn io.ReadWriteCloser
|
||||
closed bool
|
||||
reader io.Reader
|
||||
conn io.ReadWriteCloser
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (b *scgiBody) Read(p []byte) (n int, err error) {
|
||||
if b.closed {
|
||||
return 0, errors.New("SCGI read after close")
|
||||
}
|
||||
return b.reader.Read(p)
|
||||
if b.closed {
|
||||
return 0, errors.New("SCGI read after close")
|
||||
}
|
||||
return b.reader.Read(p)
|
||||
}
|
||||
|
||||
func (b *scgiBody) Close() error {
|
||||
b.closed = true
|
||||
return b.conn.Close()
|
||||
b.closed = true
|
||||
return b.conn.Close()
|
||||
}
|
||||
|
||||
type scgiConn struct {
|
||||
fd io.ReadWriteCloser
|
||||
req *http.Request
|
||||
headers http.Header
|
||||
wroteHeaders bool
|
||||
fd io.ReadWriteCloser
|
||||
req *http.Request
|
||||
headers http.Header
|
||||
wroteHeaders bool
|
||||
}
|
||||
|
||||
func (conn *scgiConn) WriteHeader(status int) {
|
||||
if !conn.wroteHeaders {
|
||||
conn.wroteHeaders = true
|
||||
if !conn.wroteHeaders {
|
||||
conn.wroteHeaders = true
|
||||
|
||||
var buf bytes.Buffer
|
||||
text := statusText[status]
|
||||
var buf bytes.Buffer
|
||||
text := statusText[status]
|
||||
|
||||
fmt.Fprintf(&buf, "HTTP/1.1 %d %s\r\n", status, text)
|
||||
fmt.Fprintf(&buf, "HTTP/1.1 %d %s\r\n", status, text)
|
||||
|
||||
for k, v := range conn.headers {
|
||||
for _, i := range v {
|
||||
buf.WriteString(k + ": " + i + "\r\n")
|
||||
}
|
||||
}
|
||||
for k, v := range conn.headers {
|
||||
for _, i := range v {
|
||||
buf.WriteString(k + ": " + i + "\r\n")
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("\r\n")
|
||||
conn.fd.Write(buf.Bytes())
|
||||
}
|
||||
buf.WriteString("\r\n")
|
||||
conn.fd.Write(buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *scgiConn) Header() http.Header {
|
||||
return conn.headers
|
||||
return conn.headers
|
||||
}
|
||||
|
||||
func (conn *scgiConn) Write(data []byte) (n int, err error) {
|
||||
if !conn.wroteHeaders {
|
||||
conn.WriteHeader(200)
|
||||
}
|
||||
if !conn.wroteHeaders {
|
||||
conn.WriteHeader(200)
|
||||
}
|
||||
|
||||
if conn.req.Method == "HEAD" {
|
||||
return 0, errors.New("Body Not Allowed")
|
||||
}
|
||||
if conn.req.Method == "HEAD" {
|
||||
return 0, errors.New("Body Not Allowed")
|
||||
}
|
||||
|
||||
return conn.fd.Write(data)
|
||||
return conn.fd.Write(data)
|
||||
}
|
||||
|
||||
func (conn *scgiConn) Close() { conn.fd.Close() }
|
||||
|
||||
func (conn *scgiConn) finishRequest() error {
|
||||
var buf bytes.Buffer
|
||||
if !conn.wroteHeaders {
|
||||
conn.wroteHeaders = true
|
||||
var buf bytes.Buffer
|
||||
if !conn.wroteHeaders {
|
||||
conn.wroteHeaders = true
|
||||
|
||||
for k, v := range conn.headers {
|
||||
for _, i := range v {
|
||||
buf.WriteString(k + ": " + i + "\r\n")
|
||||
}
|
||||
}
|
||||
for k, v := range conn.headers {
|
||||
for _, i := range v {
|
||||
buf.WriteString(k + ": " + i + "\r\n")
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("\r\n")
|
||||
conn.fd.Write(buf.Bytes())
|
||||
}
|
||||
return nil
|
||||
buf.WriteString("\r\n")
|
||||
conn.fd.Write(buf.Bytes())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) readScgiRequest(fd io.ReadWriteCloser) (*http.Request, error) {
|
||||
reader := bufio.NewReader(fd)
|
||||
line, err := reader.ReadString(':')
|
||||
if err != nil {
|
||||
s.Logger.Println("Error during SCGI read: ", err.Error())
|
||||
}
|
||||
length, _ := strconv.Atoi(line[0 : len(line)-1])
|
||||
if length > 16384 {
|
||||
s.Logger.Println("Error: max header size is 16k")
|
||||
}
|
||||
headerData := make([]byte, length)
|
||||
_, err = reader.Read(headerData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader := bufio.NewReader(fd)
|
||||
line, err := reader.ReadString(':')
|
||||
if err != nil {
|
||||
s.Logger.Println("Error during SCGI read: ", err.Error())
|
||||
}
|
||||
length, _ := strconv.Atoi(line[0 : len(line)-1])
|
||||
if length > 16384 {
|
||||
s.Logger.Println("Error: max header size is 16k")
|
||||
}
|
||||
headerData := make([]byte, length)
|
||||
_, err = reader.Read(headerData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// discard the trailing comma
|
||||
if b != ',' {
|
||||
return nil, errors.New("SCGI protocol error: missing comma")
|
||||
}
|
||||
headerList := bytes.Split(headerData, []byte{0})
|
||||
headers := map[string]string{}
|
||||
for i := 0; i < len(headerList)-1; i += 2 {
|
||||
headers[string(headerList[i])] = string(headerList[i+1])
|
||||
}
|
||||
httpReq, err := cgi.RequestFromMap(headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if httpReq.ContentLength > 0 {
|
||||
httpReq.Body = &scgiBody{
|
||||
reader: io.LimitReader(reader, httpReq.ContentLength),
|
||||
conn: fd,
|
||||
}
|
||||
} else {
|
||||
httpReq.Body = &scgiBody{reader: reader, conn: fd}
|
||||
}
|
||||
return httpReq, nil
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// discard the trailing comma
|
||||
if b != ',' {
|
||||
return nil, errors.New("SCGI protocol error: missing comma")
|
||||
}
|
||||
headerList := bytes.Split(headerData, []byte{0})
|
||||
headers := map[string]string{}
|
||||
for i := 0; i < len(headerList)-1; i += 2 {
|
||||
headers[string(headerList[i])] = string(headerList[i+1])
|
||||
}
|
||||
httpReq, err := cgi.RequestFromMap(headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if httpReq.ContentLength > 0 {
|
||||
httpReq.Body = &scgiBody{
|
||||
reader: io.LimitReader(reader, httpReq.ContentLength),
|
||||
conn: fd,
|
||||
}
|
||||
} else {
|
||||
httpReq.Body = &scgiBody{reader: reader, conn: fd}
|
||||
}
|
||||
return httpReq, nil
|
||||
}
|
||||
|
||||
func (s *Server) handleScgiRequest(fd io.ReadWriteCloser) {
|
||||
req, err := s.readScgiRequest(fd)
|
||||
if err != nil {
|
||||
s.Logger.Println("SCGI error: %q", err.Error())
|
||||
}
|
||||
sc := scgiConn{fd, req, make(map[string][]string), false}
|
||||
s.routeHandler(req, &sc)
|
||||
sc.finishRequest()
|
||||
fd.Close()
|
||||
req, err := s.readScgiRequest(fd)
|
||||
if err != nil {
|
||||
s.Logger.Println("SCGI error: %q", err.Error())
|
||||
}
|
||||
sc := scgiConn{fd, req, make(map[string][]string), false}
|
||||
s.routeHandler(req, &sc)
|
||||
sc.finishRequest()
|
||||
fd.Close()
|
||||
}
|
||||
|
||||
func (s *Server) listenAndServeScgi(addr string) error {
|
||||
|
||||
var l net.Listener
|
||||
var err error
|
||||
var l net.Listener
|
||||
var err error
|
||||
|
||||
//if the path begins with a "/", assume it's a unix address
|
||||
if strings.HasPrefix(addr, "/") {
|
||||
l, err = net.Listen("unix", addr)
|
||||
} else {
|
||||
l, err = net.Listen("tcp", addr)
|
||||
}
|
||||
//if the path begins with a "/", assume it's a unix address
|
||||
if strings.HasPrefix(addr, "/") {
|
||||
l, err = net.Listen("unix", addr)
|
||||
} else {
|
||||
l, err = net.Listen("tcp", addr)
|
||||
}
|
||||
|
||||
//save the listener so it can be closed
|
||||
s.l = l
|
||||
//save the listener so it can be closed
|
||||
s.l = l
|
||||
|
||||
if err != nil {
|
||||
s.Logger.Println("SCGI listen error", err.Error())
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
s.Logger.Println("SCGI listen error", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
fd, err := l.Accept()
|
||||
if err != nil {
|
||||
s.Logger.Println("SCGI accept error", err.Error())
|
||||
return err
|
||||
}
|
||||
go s.handleScgiRequest(fd)
|
||||
}
|
||||
return nil
|
||||
for {
|
||||
fd, err := l.Accept()
|
||||
if err != nil {
|
||||
s.Logger.Println("SCGI accept error", err.Error())
|
||||
return err
|
||||
}
|
||||
go s.handleScgiRequest(fd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
486
server.go
486
server.go
@@ -21,223 +21,223 @@ import (
|
||||
|
||||
// ServerConfig is configuration for server objects.
|
||||
type ServerConfig struct {
|
||||
StaticDir string
|
||||
Addr string
|
||||
Port int
|
||||
CookieSecret string
|
||||
RecoverPanic bool
|
||||
Profiler bool
|
||||
StaticDir string
|
||||
Addr string
|
||||
Port int
|
||||
CookieSecret string
|
||||
RecoverPanic bool
|
||||
Profiler bool
|
||||
}
|
||||
|
||||
// Server represents a web.go server.
|
||||
type Server struct {
|
||||
Config *ServerConfig
|
||||
routes []route
|
||||
Logger *log.Logger
|
||||
Env map[string]interface{}
|
||||
//save the listener so it can be closed
|
||||
l net.Listener
|
||||
Config *ServerConfig
|
||||
routes []route
|
||||
Logger *log.Logger
|
||||
Env map[string]interface{}
|
||||
//save the listener so it can be closed
|
||||
l net.Listener
|
||||
}
|
||||
|
||||
func NewServer() *Server {
|
||||
return &Server{
|
||||
Config: Config,
|
||||
Logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
|
||||
Env: map[string]interface{}{},
|
||||
}
|
||||
return &Server{
|
||||
Config: Config,
|
||||
Logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
|
||||
Env: map[string]interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) initServer() {
|
||||
if s.Config == nil {
|
||||
s.Config = &ServerConfig{}
|
||||
}
|
||||
if s.Config == nil {
|
||||
s.Config = &ServerConfig{}
|
||||
}
|
||||
|
||||
if s.Logger == nil {
|
||||
s.Logger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
|
||||
}
|
||||
if s.Logger == nil {
|
||||
s.Logger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
|
||||
}
|
||||
}
|
||||
|
||||
type route struct {
|
||||
r string
|
||||
cr *regexp.Regexp
|
||||
method string
|
||||
handler reflect.Value
|
||||
httpHandler http.Handler
|
||||
r string
|
||||
cr *regexp.Regexp
|
||||
method string
|
||||
handler reflect.Value
|
||||
httpHandler http.Handler
|
||||
}
|
||||
|
||||
func (s *Server) addRoute(r string, method string, handler interface{}) {
|
||||
cr, err := regexp.Compile(r)
|
||||
if err != nil {
|
||||
s.Logger.Printf("Error in route regex %q\n", r)
|
||||
return
|
||||
}
|
||||
cr, err := regexp.Compile(r)
|
||||
if err != nil {
|
||||
s.Logger.Printf("Error in route regex %q\n", r)
|
||||
return
|
||||
}
|
||||
|
||||
switch handler.(type) {
|
||||
case http.Handler:
|
||||
s.routes = append(s.routes, route{r: r, cr: cr, method: method, httpHandler: handler.(http.Handler)})
|
||||
case reflect.Value:
|
||||
fv := handler.(reflect.Value)
|
||||
s.routes = append(s.routes, route{r: r, cr: cr, method: method, handler: fv})
|
||||
default:
|
||||
fv := reflect.ValueOf(handler)
|
||||
s.routes = append(s.routes, route{r: r, cr: cr, method: method, handler: fv})
|
||||
}
|
||||
switch handler.(type) {
|
||||
case http.Handler:
|
||||
s.routes = append(s.routes, route{r: r, cr: cr, method: method, httpHandler: handler.(http.Handler)})
|
||||
case reflect.Value:
|
||||
fv := handler.(reflect.Value)
|
||||
s.routes = append(s.routes, route{r: r, cr: cr, method: method, handler: fv})
|
||||
default:
|
||||
fv := reflect.ValueOf(handler)
|
||||
s.routes = append(s.routes, route{r: r, cr: cr, method: method, handler: fv})
|
||||
}
|
||||
}
|
||||
|
||||
// ServeHTTP is the interface method for Go's http server package
|
||||
func (s *Server) ServeHTTP(c http.ResponseWriter, req *http.Request) {
|
||||
s.Process(c, req)
|
||||
s.Process(c, req)
|
||||
}
|
||||
|
||||
// Process invokes the routing system for server s
|
||||
func (s *Server) Process(c http.ResponseWriter, req *http.Request) {
|
||||
route := s.routeHandler(req, c)
|
||||
if route != nil {
|
||||
route.httpHandler.ServeHTTP(c, req)
|
||||
}
|
||||
route := s.routeHandler(req, c)
|
||||
if route != nil {
|
||||
route.httpHandler.ServeHTTP(c, req)
|
||||
}
|
||||
}
|
||||
|
||||
// Get adds a handler for the 'GET' http method for server s.
|
||||
func (s *Server) Get(route string, handler interface{}) {
|
||||
s.addRoute(route, "GET", handler)
|
||||
s.addRoute(route, "GET", handler)
|
||||
}
|
||||
|
||||
// Post adds a handler for the 'POST' http method for server s.
|
||||
func (s *Server) Post(route string, handler interface{}) {
|
||||
s.addRoute(route, "POST", handler)
|
||||
s.addRoute(route, "POST", handler)
|
||||
}
|
||||
|
||||
// Put adds a handler for the 'PUT' http method for server s.
|
||||
func (s *Server) Put(route string, handler interface{}) {
|
||||
s.addRoute(route, "PUT", handler)
|
||||
s.addRoute(route, "PUT", handler)
|
||||
}
|
||||
|
||||
// Delete adds a handler for the 'DELETE' http method for server s.
|
||||
func (s *Server) Delete(route string, handler interface{}) {
|
||||
s.addRoute(route, "DELETE", handler)
|
||||
s.addRoute(route, "DELETE", handler)
|
||||
}
|
||||
|
||||
// Match adds a handler for an arbitrary http method for server s.
|
||||
func (s *Server) Match(method string, route string, handler interface{}) {
|
||||
s.addRoute(route, method, handler)
|
||||
s.addRoute(route, method, handler)
|
||||
}
|
||||
|
||||
//Adds a custom handler. Only for webserver mode. Will have no effect when running as FCGI or SCGI.
|
||||
func (s *Server) Handler(route string, method string, httpHandler http.Handler) {
|
||||
s.addRoute(route, method, httpHandler)
|
||||
s.addRoute(route, method, httpHandler)
|
||||
}
|
||||
|
||||
//Adds a handler for websockets. Only for webserver mode. Will have no effect when running as FCGI or SCGI.
|
||||
func (s *Server) Websocket(route string, httpHandler websocket.Handler) {
|
||||
s.addRoute(route, "GET", httpHandler)
|
||||
s.addRoute(route, "GET", httpHandler)
|
||||
}
|
||||
|
||||
// Run starts the web application and serves HTTP requests for s
|
||||
func (s *Server) Run(addr string) {
|
||||
s.initServer()
|
||||
s.initServer()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
if s.Config.Profiler {
|
||||
mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
|
||||
mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
|
||||
mux.Handle("/debug/pprof/heap", pprof.Handler("heap"))
|
||||
mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
|
||||
}
|
||||
mux.Handle("/", s)
|
||||
mux := http.NewServeMux()
|
||||
if s.Config.Profiler {
|
||||
mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
|
||||
mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
|
||||
mux.Handle("/debug/pprof/heap", pprof.Handler("heap"))
|
||||
mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
|
||||
}
|
||||
mux.Handle("/", s)
|
||||
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe:", err)
|
||||
}
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe:", err)
|
||||
}
|
||||
|
||||
s.Logger.Printf("web.go serving %s\n", l.Addr())
|
||||
s.Logger.Printf("web.go serving %s\n", l.Addr())
|
||||
|
||||
s.l = l
|
||||
err = http.Serve(s.l, mux)
|
||||
s.l.Close()
|
||||
s.l = l
|
||||
err = http.Serve(s.l, mux)
|
||||
s.l.Close()
|
||||
}
|
||||
|
||||
// RunFcgi starts the web application and serves FastCGI requests for s.
|
||||
func (s *Server) RunFcgi(addr string) {
|
||||
s.initServer()
|
||||
s.Logger.Printf("web.go serving fcgi %s\n", addr)
|
||||
s.listenAndServeFcgi(addr)
|
||||
s.initServer()
|
||||
s.Logger.Printf("web.go serving fcgi %s\n", addr)
|
||||
s.listenAndServeFcgi(addr)
|
||||
}
|
||||
|
||||
// RunScgi starts the web application and serves SCGI requests for s.
|
||||
func (s *Server) RunScgi(addr string) {
|
||||
s.initServer()
|
||||
s.Logger.Printf("web.go serving scgi %s\n", addr)
|
||||
s.listenAndServeScgi(addr)
|
||||
s.initServer()
|
||||
s.Logger.Printf("web.go serving scgi %s\n", addr)
|
||||
s.listenAndServeScgi(addr)
|
||||
}
|
||||
|
||||
// RunTLS starts the web application and serves HTTPS requests for s.
|
||||
func (s *Server) RunTLS(addr string, config *tls.Config) error {
|
||||
s.initServer()
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", s)
|
||||
s.initServer()
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", s)
|
||||
|
||||
l, err := tls.Listen("tcp", addr, config)
|
||||
if err != nil {
|
||||
log.Fatal("Listen:", err)
|
||||
return err
|
||||
}
|
||||
s.Logger.Printf("web.go serving %s\n", l.Addr())
|
||||
l, err := tls.Listen("tcp", addr, config)
|
||||
if err != nil {
|
||||
log.Fatal("Listen:", err)
|
||||
return err
|
||||
}
|
||||
s.Logger.Printf("web.go serving %s\n", l.Addr())
|
||||
|
||||
s.l = l
|
||||
return http.Serve(s.l, mux)
|
||||
s.l = l
|
||||
return http.Serve(s.l, mux)
|
||||
}
|
||||
|
||||
// Close stops server s.
|
||||
func (s *Server) Close() {
|
||||
if s.l != nil {
|
||||
s.l.Close()
|
||||
}
|
||||
if s.l != nil {
|
||||
s.l.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// safelyCall invokes `function` in recover block
|
||||
func (s *Server) safelyCall(function reflect.Value, args []reflect.Value) (resp []reflect.Value, e interface{}) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
if !s.Config.RecoverPanic {
|
||||
// go back to panic
|
||||
panic(err)
|
||||
} else {
|
||||
e = err
|
||||
resp = nil
|
||||
s.Logger.Println("Handler crashed with error", err)
|
||||
for i := 1; ; i += 1 {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
s.Logger.Println(file, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return function.Call(args), nil
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
if !s.Config.RecoverPanic {
|
||||
// go back to panic
|
||||
panic(err)
|
||||
} else {
|
||||
e = err
|
||||
resp = nil
|
||||
s.Logger.Println("Handler crashed with error", err)
|
||||
for i := 1; ; i += 1 {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
s.Logger.Println(file, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return function.Call(args), nil
|
||||
}
|
||||
|
||||
// requiresContext determines whether 'handlerType' contains
|
||||
// an argument to 'web.Ctx' as its first argument
|
||||
func requiresContext(handlerType reflect.Type) bool {
|
||||
//if the method doesn't take arguments, no
|
||||
if handlerType.NumIn() == 0 {
|
||||
return false
|
||||
}
|
||||
//if the method doesn't take arguments, no
|
||||
if handlerType.NumIn() == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
//if the first argument is not a pointer, no
|
||||
a0 := handlerType.In(0)
|
||||
if a0.Kind() != reflect.Ptr {
|
||||
return false
|
||||
}
|
||||
//if the first argument is a context, yes
|
||||
if a0.Elem() == contextType {
|
||||
return true
|
||||
}
|
||||
//if the first argument is not a pointer, no
|
||||
a0 := handlerType.In(0)
|
||||
if a0.Kind() != reflect.Ptr {
|
||||
return false
|
||||
}
|
||||
//if the first argument is a context, yes
|
||||
if a0.Elem() == contextType {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return false
|
||||
}
|
||||
|
||||
// tryServingFile attempts to serve a static file, and returns
|
||||
@@ -247,50 +247,50 @@ func requiresContext(handlerType reflect.Type) bool {
|
||||
// 2) The 'static' directory in the parent directory of the executable.
|
||||
// 3) The 'static' directory in the current working directory
|
||||
func (s *Server) tryServingFile(name string, req *http.Request, w http.ResponseWriter) bool {
|
||||
//try to serve a static file
|
||||
if s.Config.StaticDir != "" {
|
||||
staticFile := path.Join(s.Config.StaticDir, name)
|
||||
if fileExists(staticFile) {
|
||||
http.ServeFile(w, req, staticFile)
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
for _, staticDir := range defaultStaticDirs {
|
||||
staticFile := path.Join(staticDir, name)
|
||||
if fileExists(staticFile) {
|
||||
http.ServeFile(w, req, staticFile)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
//try to serve a static file
|
||||
if s.Config.StaticDir != "" {
|
||||
staticFile := path.Join(s.Config.StaticDir, name)
|
||||
if fileExists(staticFile) {
|
||||
http.ServeFile(w, req, staticFile)
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
for _, staticDir := range defaultStaticDirs {
|
||||
staticFile := path.Join(staticDir, name)
|
||||
if fileExists(staticFile) {
|
||||
http.ServeFile(w, req, staticFile)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Server) logRequest(ctx Context, sTime time.Time) {
|
||||
//log the request
|
||||
var logEntry bytes.Buffer
|
||||
req := ctx.Request
|
||||
requestPath := req.URL.Path
|
||||
//log the request
|
||||
var logEntry bytes.Buffer
|
||||
req := ctx.Request
|
||||
requestPath := req.URL.Path
|
||||
|
||||
duration := time.Now().Sub(sTime)
|
||||
var client string
|
||||
duration := time.Now().Sub(sTime)
|
||||
var client string
|
||||
|
||||
// We suppose RemoteAddr is of the form Ip:Port as specified in the Request
|
||||
// documentation at http://golang.org/pkg/net/http/#Request
|
||||
pos := strings.LastIndex(req.RemoteAddr, ":")
|
||||
if pos > 0 {
|
||||
client = req.RemoteAddr[0:pos]
|
||||
} else {
|
||||
client = req.RemoteAddr
|
||||
}
|
||||
// We suppose RemoteAddr is of the form Ip:Port as specified in the Request
|
||||
// documentation at http://golang.org/pkg/net/http/#Request
|
||||
pos := strings.LastIndex(req.RemoteAddr, ":")
|
||||
if pos > 0 {
|
||||
client = req.RemoteAddr[0:pos]
|
||||
} else {
|
||||
client = req.RemoteAddr
|
||||
}
|
||||
|
||||
fmt.Fprintf(&logEntry, "%s - \033[32;1m %s %s\033[0m - %v", client, req.Method, requestPath, duration)
|
||||
fmt.Fprintf(&logEntry, "%s - \033[32;1m %s %s\033[0m - %v", client, req.Method, requestPath, duration)
|
||||
|
||||
if len(ctx.Params) > 0 {
|
||||
fmt.Fprintf(&logEntry, " - \033[37;1mParams: %v\033[0m\n", ctx.Params)
|
||||
}
|
||||
if len(ctx.Params) > 0 {
|
||||
fmt.Fprintf(&logEntry, " - \033[37;1mParams: %v\033[0m\n", ctx.Params)
|
||||
}
|
||||
|
||||
ctx.Server.Logger.Print(logEntry.String())
|
||||
ctx.Server.Logger.Print(logEntry.String())
|
||||
|
||||
}
|
||||
|
||||
@@ -301,105 +301,105 @@ func (s *Server) logRequest(ctx Context, sTime time.Time) {
|
||||
// route. The caller is then responsible for calling the httpHandler associated
|
||||
// with the returned route.
|
||||
func (s *Server) routeHandler(req *http.Request, w http.ResponseWriter) (unused *route) {
|
||||
requestPath := req.URL.Path
|
||||
ctx := Context{req, map[string]string{}, s, w}
|
||||
requestPath := req.URL.Path
|
||||
ctx := Context{req, map[string]string{}, s, w}
|
||||
|
||||
//set some default headers
|
||||
ctx.SetHeader("Server", "web.go", true)
|
||||
tm := time.Now().UTC()
|
||||
//set some default headers
|
||||
ctx.SetHeader("Server", "web.go", true)
|
||||
tm := time.Now().UTC()
|
||||
|
||||
//ignore errors from ParseForm because it's usually harmless.
|
||||
req.ParseForm()
|
||||
if len(req.Form) > 0 {
|
||||
for k, v := range req.Form {
|
||||
ctx.Params[k] = v[0]
|
||||
}
|
||||
}
|
||||
//ignore errors from ParseForm because it's usually harmless.
|
||||
req.ParseForm()
|
||||
if len(req.Form) > 0 {
|
||||
for k, v := range req.Form {
|
||||
ctx.Params[k] = v[0]
|
||||
}
|
||||
}
|
||||
|
||||
defer s.logRequest(ctx, tm)
|
||||
defer s.logRequest(ctx, tm)
|
||||
|
||||
ctx.SetHeader("Date", webTime(tm), true)
|
||||
ctx.SetHeader("Date", webTime(tm), true)
|
||||
|
||||
if req.Method == "GET" || req.Method == "HEAD" {
|
||||
if s.tryServingFile(requestPath, req, w) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if req.Method == "GET" || req.Method == "HEAD" {
|
||||
if s.tryServingFile(requestPath, req, w) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//Set the default content-type
|
||||
ctx.SetHeader("Content-Type", "text/html; charset=utf-8", true)
|
||||
//Set the default content-type
|
||||
ctx.SetHeader("Content-Type", "text/html; charset=utf-8", true)
|
||||
|
||||
for i := 0; i < len(s.routes); i++ {
|
||||
route := s.routes[i]
|
||||
cr := route.cr
|
||||
//if the methods don't match, skip this handler (except HEAD can be used in place of GET)
|
||||
if req.Method != route.method && !(req.Method == "HEAD" && route.method == "GET") {
|
||||
continue
|
||||
}
|
||||
for i := 0; i < len(s.routes); i++ {
|
||||
route := s.routes[i]
|
||||
cr := route.cr
|
||||
//if the methods don't match, skip this handler (except HEAD can be used in place of GET)
|
||||
if req.Method != route.method && !(req.Method == "HEAD" && route.method == "GET") {
|
||||
continue
|
||||
}
|
||||
|
||||
if !cr.MatchString(requestPath) {
|
||||
continue
|
||||
}
|
||||
match := cr.FindStringSubmatch(requestPath)
|
||||
if !cr.MatchString(requestPath) {
|
||||
continue
|
||||
}
|
||||
match := cr.FindStringSubmatch(requestPath)
|
||||
|
||||
if len(match[0]) != len(requestPath) {
|
||||
continue
|
||||
}
|
||||
if len(match[0]) != len(requestPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
if route.httpHandler != nil {
|
||||
unused = &route
|
||||
// We can not handle custom http handlers here, give back to the caller.
|
||||
return
|
||||
}
|
||||
if route.httpHandler != nil {
|
||||
unused = &route
|
||||
// We can not handle custom http handlers here, give back to the caller.
|
||||
return
|
||||
}
|
||||
|
||||
var args []reflect.Value
|
||||
handlerType := route.handler.Type()
|
||||
if requiresContext(handlerType) {
|
||||
args = append(args, reflect.ValueOf(&ctx))
|
||||
}
|
||||
for _, arg := range match[1:] {
|
||||
args = append(args, reflect.ValueOf(arg))
|
||||
}
|
||||
var args []reflect.Value
|
||||
handlerType := route.handler.Type()
|
||||
if requiresContext(handlerType) {
|
||||
args = append(args, reflect.ValueOf(&ctx))
|
||||
}
|
||||
for _, arg := range match[1:] {
|
||||
args = append(args, reflect.ValueOf(arg))
|
||||
}
|
||||
|
||||
ret, err := s.safelyCall(route.handler, args)
|
||||
if err != nil {
|
||||
//there was an error or panic while calling the handler
|
||||
ctx.Abort(500, "Server Error")
|
||||
}
|
||||
if len(ret) == 0 {
|
||||
return
|
||||
}
|
||||
ret, err := s.safelyCall(route.handler, args)
|
||||
if err != nil {
|
||||
//there was an error or panic while calling the handler
|
||||
ctx.Abort(500, "Server Error")
|
||||
}
|
||||
if len(ret) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
sval := ret[0]
|
||||
sval := ret[0]
|
||||
|
||||
var content []byte
|
||||
var content []byte
|
||||
|
||||
if sval.Kind() == reflect.String {
|
||||
content = []byte(sval.String())
|
||||
} else if sval.Kind() == reflect.Slice && sval.Type().Elem().Kind() == reflect.Uint8 {
|
||||
content = sval.Interface().([]byte)
|
||||
}
|
||||
ctx.SetHeader("Content-Length", strconv.Itoa(len(content)), true)
|
||||
_, err = ctx.ResponseWriter.Write(content)
|
||||
if err != nil {
|
||||
ctx.Server.Logger.Println("Error during write: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if sval.Kind() == reflect.String {
|
||||
content = []byte(sval.String())
|
||||
} else if sval.Kind() == reflect.Slice && sval.Type().Elem().Kind() == reflect.Uint8 {
|
||||
content = sval.Interface().([]byte)
|
||||
}
|
||||
ctx.SetHeader("Content-Length", strconv.Itoa(len(content)), true)
|
||||
_, err = ctx.ResponseWriter.Write(content)
|
||||
if err != nil {
|
||||
ctx.Server.Logger.Println("Error during write: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// try serving index.html or index.htm
|
||||
if req.Method == "GET" || req.Method == "HEAD" {
|
||||
if s.tryServingFile(path.Join(requestPath, "index.html"), req, w) {
|
||||
return
|
||||
} else if s.tryServingFile(path.Join(requestPath, "index.htm"), req, w) {
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.Abort(404, "Page not found")
|
||||
return
|
||||
// try serving index.html or index.htm
|
||||
if req.Method == "GET" || req.Method == "HEAD" {
|
||||
if s.tryServingFile(path.Join(requestPath, "index.html"), req, w) {
|
||||
return
|
||||
} else if s.tryServingFile(path.Join(requestPath, "index.htm"), req, w) {
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.Abort(404, "Page not found")
|
||||
return
|
||||
}
|
||||
|
||||
// SetLogger sets the logger for server s
|
||||
func (s *Server) SetLogger(logger *log.Logger) {
|
||||
s.Logger = logger
|
||||
s.Logger = logger
|
||||
}
|
||||
|
||||
80
status.go
80
status.go
@@ -7,48 +7,48 @@ package web
|
||||
import "net/http"
|
||||
|
||||
var statusText = map[int]string{
|
||||
http.StatusContinue: "Continue",
|
||||
http.StatusSwitchingProtocols: "Switching Protocols",
|
||||
http.StatusContinue: "Continue",
|
||||
http.StatusSwitchingProtocols: "Switching Protocols",
|
||||
|
||||
http.StatusOK: "OK",
|
||||
http.StatusCreated: "Created",
|
||||
http.StatusAccepted: "Accepted",
|
||||
http.StatusNonAuthoritativeInfo: "Non-Authoritative Information",
|
||||
http.StatusNoContent: "No Content",
|
||||
http.StatusResetContent: "Reset Content",
|
||||
http.StatusPartialContent: "Partial Content",
|
||||
http.StatusOK: "OK",
|
||||
http.StatusCreated: "Created",
|
||||
http.StatusAccepted: "Accepted",
|
||||
http.StatusNonAuthoritativeInfo: "Non-Authoritative Information",
|
||||
http.StatusNoContent: "No Content",
|
||||
http.StatusResetContent: "Reset Content",
|
||||
http.StatusPartialContent: "Partial Content",
|
||||
|
||||
http.StatusMultipleChoices: "Multiple Choices",
|
||||
http.StatusMovedPermanently: "Moved Permanently",
|
||||
http.StatusFound: "Found",
|
||||
http.StatusSeeOther: "See Other",
|
||||
http.StatusNotModified: "Not Modified",
|
||||
http.StatusUseProxy: "Use Proxy",
|
||||
http.StatusTemporaryRedirect: "Temporary Redirect",
|
||||
http.StatusMultipleChoices: "Multiple Choices",
|
||||
http.StatusMovedPermanently: "Moved Permanently",
|
||||
http.StatusFound: "Found",
|
||||
http.StatusSeeOther: "See Other",
|
||||
http.StatusNotModified: "Not Modified",
|
||||
http.StatusUseProxy: "Use Proxy",
|
||||
http.StatusTemporaryRedirect: "Temporary Redirect",
|
||||
|
||||
http.StatusBadRequest: "Bad Request",
|
||||
http.StatusUnauthorized: "Unauthorized",
|
||||
http.StatusPaymentRequired: "Payment Required",
|
||||
http.StatusForbidden: "Forbidden",
|
||||
http.StatusNotFound: "Not Found",
|
||||
http.StatusMethodNotAllowed: "Method Not Allowed",
|
||||
http.StatusNotAcceptable: "Not Acceptable",
|
||||
http.StatusProxyAuthRequired: "Proxy Authentication Required",
|
||||
http.StatusRequestTimeout: "Request Timeout",
|
||||
http.StatusConflict: "Conflict",
|
||||
http.StatusGone: "Gone",
|
||||
http.StatusLengthRequired: "Length Required",
|
||||
http.StatusPreconditionFailed: "Precondition Failed",
|
||||
http.StatusRequestEntityTooLarge: "Request Entity Too Large",
|
||||
http.StatusRequestURITooLong: "Request URI Too Long",
|
||||
http.StatusUnsupportedMediaType: "Unsupported Media Type",
|
||||
http.StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable",
|
||||
http.StatusExpectationFailed: "Expectation Failed",
|
||||
http.StatusBadRequest: "Bad Request",
|
||||
http.StatusUnauthorized: "Unauthorized",
|
||||
http.StatusPaymentRequired: "Payment Required",
|
||||
http.StatusForbidden: "Forbidden",
|
||||
http.StatusNotFound: "Not Found",
|
||||
http.StatusMethodNotAllowed: "Method Not Allowed",
|
||||
http.StatusNotAcceptable: "Not Acceptable",
|
||||
http.StatusProxyAuthRequired: "Proxy Authentication Required",
|
||||
http.StatusRequestTimeout: "Request Timeout",
|
||||
http.StatusConflict: "Conflict",
|
||||
http.StatusGone: "Gone",
|
||||
http.StatusLengthRequired: "Length Required",
|
||||
http.StatusPreconditionFailed: "Precondition Failed",
|
||||
http.StatusRequestEntityTooLarge: "Request Entity Too Large",
|
||||
http.StatusRequestURITooLong: "Request URI Too Long",
|
||||
http.StatusUnsupportedMediaType: "Unsupported Media Type",
|
||||
http.StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable",
|
||||
http.StatusExpectationFailed: "Expectation Failed",
|
||||
|
||||
http.StatusInternalServerError: "Internal Server Error",
|
||||
http.StatusNotImplemented: "Not Implemented",
|
||||
http.StatusBadGateway: "Bad Gateway",
|
||||
http.StatusServiceUnavailable: "Service Unavailable",
|
||||
http.StatusGatewayTimeout: "Gateway Timeout",
|
||||
http.StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
|
||||
http.StatusInternalServerError: "Internal Server Error",
|
||||
http.StatusNotImplemented: "Not Implemented",
|
||||
http.StatusBadGateway: "Bad Gateway",
|
||||
http.StatusServiceUnavailable: "Service Unavailable",
|
||||
http.StatusGatewayTimeout: "Gateway Timeout",
|
||||
http.StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
|
||||
}
|
||||
|
||||
242
web.go
242
web.go
@@ -3,23 +3,23 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"golang.org/x/net/websocket"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"golang.org/x/net/websocket"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Context object is created for every incoming HTTP request, and is
|
||||
@@ -27,15 +27,15 @@ import (
|
||||
// about the request, including the http.Request object, the GET and POST params,
|
||||
// and acts as a Writer for the response.
|
||||
type Context struct {
|
||||
Request *http.Request
|
||||
Params map[string]string
|
||||
Server *Server
|
||||
http.ResponseWriter
|
||||
Request *http.Request
|
||||
Params map[string]string
|
||||
Server *Server
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
// WriteString writes string data into the response object.
|
||||
func (ctx *Context) WriteString(content string) {
|
||||
ctx.ResponseWriter.Write([]byte(content))
|
||||
ctx.ResponseWriter.Write([]byte(content))
|
||||
}
|
||||
|
||||
// Abort is a helper method that sends an HTTP header and an optional
|
||||
@@ -43,36 +43,36 @@ func (ctx *Context) WriteString(content string) {
|
||||
// Once it has been called, any return value from the handler will
|
||||
// not be written to the response.
|
||||
func (ctx *Context) Abort(status int, body string) {
|
||||
ctx.ResponseWriter.WriteHeader(status)
|
||||
ctx.ResponseWriter.Write([]byte(body))
|
||||
ctx.ResponseWriter.WriteHeader(status)
|
||||
ctx.ResponseWriter.Write([]byte(body))
|
||||
}
|
||||
|
||||
// Redirect is a helper method for 3xx redirects.
|
||||
func (ctx *Context) Redirect(status int, url_ string) {
|
||||
ctx.ResponseWriter.Header().Set("Location", url_)
|
||||
ctx.ResponseWriter.WriteHeader(status)
|
||||
ctx.ResponseWriter.Write([]byte("Redirecting to: " + url_))
|
||||
ctx.ResponseWriter.Header().Set("Location", url_)
|
||||
ctx.ResponseWriter.WriteHeader(status)
|
||||
ctx.ResponseWriter.Write([]byte("Redirecting to: " + url_))
|
||||
}
|
||||
|
||||
// Notmodified writes a 304 HTTP response
|
||||
func (ctx *Context) NotModified() {
|
||||
ctx.ResponseWriter.WriteHeader(304)
|
||||
ctx.ResponseWriter.WriteHeader(304)
|
||||
}
|
||||
|
||||
// NotFound writes a 404 HTTP response
|
||||
func (ctx *Context) NotFound(message string) {
|
||||
ctx.ResponseWriter.WriteHeader(404)
|
||||
ctx.ResponseWriter.Write([]byte(message))
|
||||
ctx.ResponseWriter.WriteHeader(404)
|
||||
ctx.ResponseWriter.Write([]byte(message))
|
||||
}
|
||||
|
||||
//Unauthorized writes a 401 HTTP response
|
||||
func (ctx *Context) Unauthorized() {
|
||||
ctx.ResponseWriter.WriteHeader(401)
|
||||
ctx.ResponseWriter.WriteHeader(401)
|
||||
}
|
||||
|
||||
//Forbidden writes a 403 HTTP response
|
||||
func (ctx *Context) Forbidden() {
|
||||
ctx.ResponseWriter.WriteHeader(403)
|
||||
ctx.ResponseWriter.WriteHeader(403)
|
||||
}
|
||||
|
||||
// ContentType sets the Content-Type header for an HTTP response.
|
||||
@@ -81,92 +81,92 @@ func (ctx *Context) Forbidden() {
|
||||
// verbatim. The return value is the content type as it was
|
||||
// set, or an empty string if none was found.
|
||||
func (ctx *Context) ContentType(val string) string {
|
||||
var ctype string
|
||||
if strings.ContainsRune(val, '/') {
|
||||
ctype = val
|
||||
} else {
|
||||
if !strings.HasPrefix(val, ".") {
|
||||
val = "." + val
|
||||
}
|
||||
ctype = mime.TypeByExtension(val)
|
||||
}
|
||||
if ctype != "" {
|
||||
ctx.Header().Set("Content-Type", ctype)
|
||||
}
|
||||
return ctype
|
||||
var ctype string
|
||||
if strings.ContainsRune(val, '/') {
|
||||
ctype = val
|
||||
} else {
|
||||
if !strings.HasPrefix(val, ".") {
|
||||
val = "." + val
|
||||
}
|
||||
ctype = mime.TypeByExtension(val)
|
||||
}
|
||||
if ctype != "" {
|
||||
ctx.Header().Set("Content-Type", ctype)
|
||||
}
|
||||
return ctype
|
||||
}
|
||||
|
||||
// SetHeader sets a response header. If `unique` is true, the current value
|
||||
// of that header will be overwritten . If false, it will be appended.
|
||||
func (ctx *Context) SetHeader(hdr string, val string, unique bool) {
|
||||
if unique {
|
||||
ctx.Header().Set(hdr, val)
|
||||
} else {
|
||||
ctx.Header().Add(hdr, val)
|
||||
}
|
||||
if unique {
|
||||
ctx.Header().Set(hdr, val)
|
||||
} else {
|
||||
ctx.Header().Add(hdr, val)
|
||||
}
|
||||
}
|
||||
|
||||
// SetCookie adds a cookie header to the response.
|
||||
func (ctx *Context) SetCookie(cookie *http.Cookie) {
|
||||
ctx.SetHeader("Set-Cookie", cookie.String(), false)
|
||||
ctx.SetHeader("Set-Cookie", cookie.String(), false)
|
||||
}
|
||||
|
||||
func getCookieSig(key string, val []byte, timestamp string) string {
|
||||
hm := hmac.New(sha1.New, []byte(key))
|
||||
hm := hmac.New(sha1.New, []byte(key))
|
||||
|
||||
hm.Write(val)
|
||||
hm.Write([]byte(timestamp))
|
||||
hm.Write(val)
|
||||
hm.Write([]byte(timestamp))
|
||||
|
||||
return fmt.Sprintf("%02x", hm.Sum(nil))
|
||||
return fmt.Sprintf("%02x", hm.Sum(nil))
|
||||
}
|
||||
|
||||
func (ctx *Context) SetSecureCookie(name string, val string, age int64) {
|
||||
//base64 encode the val
|
||||
if len(ctx.Server.Config.CookieSecret) == 0 {
|
||||
ctx.Server.Logger.Println("Secret Key for secure cookies has not been set. Please assign a cookie secret to web.Config.CookieSecret.")
|
||||
return
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
encoder := base64.NewEncoder(base64.StdEncoding, &buf)
|
||||
encoder.Write([]byte(val))
|
||||
encoder.Close()
|
||||
vs := buf.String()
|
||||
vb := buf.Bytes()
|
||||
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
sig := getCookieSig(ctx.Server.Config.CookieSecret, vb, timestamp)
|
||||
cookie := strings.Join([]string{vs, timestamp, sig}, "|")
|
||||
ctx.SetCookie(NewCookie(name, cookie, age))
|
||||
//base64 encode the val
|
||||
if len(ctx.Server.Config.CookieSecret) == 0 {
|
||||
ctx.Server.Logger.Println("Secret Key for secure cookies has not been set. Please assign a cookie secret to web.Config.CookieSecret.")
|
||||
return
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
encoder := base64.NewEncoder(base64.StdEncoding, &buf)
|
||||
encoder.Write([]byte(val))
|
||||
encoder.Close()
|
||||
vs := buf.String()
|
||||
vb := buf.Bytes()
|
||||
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
sig := getCookieSig(ctx.Server.Config.CookieSecret, vb, timestamp)
|
||||
cookie := strings.Join([]string{vs, timestamp, sig}, "|")
|
||||
ctx.SetCookie(NewCookie(name, cookie, age))
|
||||
}
|
||||
|
||||
func (ctx *Context) GetSecureCookie(name string) (string, bool) {
|
||||
for _, cookie := range ctx.Request.Cookies() {
|
||||
if cookie.Name != name {
|
||||
continue
|
||||
}
|
||||
for _, cookie := range ctx.Request.Cookies() {
|
||||
if cookie.Name != name {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.SplitN(cookie.Value, "|", 3)
|
||||
parts := strings.SplitN(cookie.Value, "|", 3)
|
||||
|
||||
val := parts[0]
|
||||
timestamp := parts[1]
|
||||
sig := parts[2]
|
||||
val := parts[0]
|
||||
timestamp := parts[1]
|
||||
sig := parts[2]
|
||||
|
||||
if getCookieSig(ctx.Server.Config.CookieSecret, []byte(val), timestamp) != sig {
|
||||
return "", false
|
||||
}
|
||||
if getCookieSig(ctx.Server.Config.CookieSecret, []byte(val), timestamp) != sig {
|
||||
return "", false
|
||||
}
|
||||
|
||||
ts, _ := strconv.ParseInt(timestamp, 0, 64)
|
||||
ts, _ := strconv.ParseInt(timestamp, 0, 64)
|
||||
|
||||
if time.Now().Unix()-31*86400 > ts {
|
||||
return "", false
|
||||
}
|
||||
if time.Now().Unix()-31*86400 > ts {
|
||||
return "", false
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString(val)
|
||||
encoder := base64.NewDecoder(base64.StdEncoding, buf)
|
||||
buf := bytes.NewBufferString(val)
|
||||
encoder := base64.NewDecoder(base64.StdEncoding, buf)
|
||||
|
||||
res, _ := ioutil.ReadAll(encoder)
|
||||
return string(res), true
|
||||
}
|
||||
return "", false
|
||||
res, _ := ioutil.ReadAll(encoder)
|
||||
return string(res), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// small optimization: cache the context type instead of repeteadly calling reflect.Typeof
|
||||
@@ -175,96 +175,96 @@ var contextType reflect.Type
|
||||
var defaultStaticDirs []string
|
||||
|
||||
func init() {
|
||||
contextType = reflect.TypeOf(Context{})
|
||||
//find the location of the exe file
|
||||
wd, _ := os.Getwd()
|
||||
arg0 := path.Clean(os.Args[0])
|
||||
var exeFile string
|
||||
if strings.HasPrefix(arg0, "/") {
|
||||
exeFile = arg0
|
||||
} else {
|
||||
//TODO for robustness, search each directory in $PATH
|
||||
exeFile = path.Join(wd, arg0)
|
||||
}
|
||||
parent, _ := path.Split(exeFile)
|
||||
defaultStaticDirs = append(defaultStaticDirs, path.Join(parent, "static"))
|
||||
defaultStaticDirs = append(defaultStaticDirs, path.Join(wd, "static"))
|
||||
return
|
||||
contextType = reflect.TypeOf(Context{})
|
||||
//find the location of the exe file
|
||||
wd, _ := os.Getwd()
|
||||
arg0 := path.Clean(os.Args[0])
|
||||
var exeFile string
|
||||
if strings.HasPrefix(arg0, "/") {
|
||||
exeFile = arg0
|
||||
} else {
|
||||
//TODO for robustness, search each directory in $PATH
|
||||
exeFile = path.Join(wd, arg0)
|
||||
}
|
||||
parent, _ := path.Split(exeFile)
|
||||
defaultStaticDirs = append(defaultStaticDirs, path.Join(parent, "static"))
|
||||
defaultStaticDirs = append(defaultStaticDirs, path.Join(wd, "static"))
|
||||
return
|
||||
}
|
||||
|
||||
// Process invokes the main server's routing system.
|
||||
func Process(c http.ResponseWriter, req *http.Request) {
|
||||
mainServer.Process(c, req)
|
||||
mainServer.Process(c, req)
|
||||
}
|
||||
|
||||
// Run starts the web application and serves HTTP requests for the main server.
|
||||
func Run(addr string) {
|
||||
mainServer.Run(addr)
|
||||
mainServer.Run(addr)
|
||||
}
|
||||
|
||||
// RunTLS starts the web application and serves HTTPS requests for the main server.
|
||||
func RunTLS(addr string, config *tls.Config) {
|
||||
mainServer.RunTLS(addr, config)
|
||||
mainServer.RunTLS(addr, config)
|
||||
}
|
||||
|
||||
// RunScgi starts the web application and serves SCGI requests for the main server.
|
||||
func RunScgi(addr string) {
|
||||
mainServer.RunScgi(addr)
|
||||
mainServer.RunScgi(addr)
|
||||
}
|
||||
|
||||
// RunFcgi starts the web application and serves FastCGI requests for the main server.
|
||||
func RunFcgi(addr string) {
|
||||
mainServer.RunFcgi(addr)
|
||||
mainServer.RunFcgi(addr)
|
||||
}
|
||||
|
||||
// Close stops the main server.
|
||||
func Close() {
|
||||
mainServer.Close()
|
||||
mainServer.Close()
|
||||
}
|
||||
|
||||
// Get adds a handler for the 'GET' http method in the main server.
|
||||
func Get(route string, handler interface{}) {
|
||||
mainServer.Get(route, handler)
|
||||
mainServer.Get(route, handler)
|
||||
}
|
||||
|
||||
// Post adds a handler for the 'POST' http method in the main server.
|
||||
func Post(route string, handler interface{}) {
|
||||
mainServer.addRoute(route, "POST", handler)
|
||||
mainServer.addRoute(route, "POST", handler)
|
||||
}
|
||||
|
||||
// Put adds a handler for the 'PUT' http method in the main server.
|
||||
func Put(route string, handler interface{}) {
|
||||
mainServer.addRoute(route, "PUT", handler)
|
||||
mainServer.addRoute(route, "PUT", handler)
|
||||
}
|
||||
|
||||
// Delete adds a handler for the 'DELETE' http method in the main server.
|
||||
func Delete(route string, handler interface{}) {
|
||||
mainServer.addRoute(route, "DELETE", handler)
|
||||
mainServer.addRoute(route, "DELETE", handler)
|
||||
}
|
||||
|
||||
// Match adds a handler for an arbitrary http method in the main server.
|
||||
func Match(method string, route string, handler interface{}) {
|
||||
mainServer.addRoute(route, method, handler)
|
||||
mainServer.addRoute(route, method, handler)
|
||||
}
|
||||
|
||||
//Adds a custom handler. Only for webserver mode. Will have no effect when running as FCGI or SCGI.
|
||||
func Handler(route string, method string, httpHandler http.Handler) {
|
||||
mainServer.Handler(route, method, httpHandler)
|
||||
mainServer.Handler(route, method, httpHandler)
|
||||
}
|
||||
|
||||
//Adds a handler for websockets. Only for webserver mode. Will have no effect when running as FCGI or SCGI.
|
||||
func Websocket(route string, httpHandler websocket.Handler) {
|
||||
mainServer.Websocket(route, httpHandler)
|
||||
mainServer.Websocket(route, httpHandler)
|
||||
}
|
||||
|
||||
// SetLogger sets the logger for the main server.
|
||||
func SetLogger(logger *log.Logger) {
|
||||
mainServer.Logger = logger
|
||||
mainServer.Logger = logger
|
||||
}
|
||||
|
||||
// Config is the configuration of the main server.
|
||||
var Config = &ServerConfig{
|
||||
RecoverPanic: true,
|
||||
RecoverPanic: true,
|
||||
}
|
||||
|
||||
var mainServer = NewServer()
|
||||
|
||||
866
web_test.go
866
web_test.go
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user