track favicon stats

experiment with tracking average route performance
temporary error route stub
optimise dumprequest
add DisableAnalytics config setting
fix double hyphens in slugs being mistaken for sql injection
more querygen tests

You wil need to run the updater / patcher for this commit.
This commit is contained in:
Azareal 2020-02-26 20:34:38 +10:00
parent 04fdf4c318
commit ea1037bd63
19 changed files with 841 additions and 705 deletions

View File

@ -662,6 +662,7 @@ func createTables(adapter qgen.Adapter) (err error) {
createTable("viewchunks", "", "",
[]tC{
tC{"count", "int", 0, false, false, "0"},
tC{"avg", "int", 0, false, false, "0"},
tC{"createdAt", "datetime", 0, false, false, ""},
tC{"route", "varchar", 200, false, false, ""}, // todo: set a default empty here
}, nil,

View File

@ -50,7 +50,7 @@ func (co *DefaultAgentViewCounter) insertChunk(count int64, agent int) error {
func (co *DefaultAgentViewCounter) Bump(agent int) {
// TODO: Test this check
c.DebugDetail("co.buckets[", agent, "]: ", co.buckets[agent])
c.DebugDetail("buckets[", agent, "]: ", co.buckets[agent])
if len(co.buckets) <= agent || agent < 0 {
return
}

View File

@ -157,7 +157,7 @@ func (co *DefaultLangViewCounter) Bump(langCode string) (validCode bool) {
}
// TODO: Test this check
c.DebugDetail("co.buckets[", id, "]: ", co.buckets[id])
c.DebugDetail("buckets[", id, "]: ", co.buckets[id])
if len(co.buckets) <= id || id < 0 {
return validCode
}

View File

@ -85,7 +85,7 @@ func (ref *DefaultReferrerTracker) insertChunk(referrer string, count int64) err
if count == 0 {
return nil
}
c.DebugDetailf("Inserting a vchunk with a count of %d for referrer %s", count, referrer)
c.DebugDetailf("Inserting a vchunk with a count of %d for ref %s", count, referrer)
_, err := ref.insert.Exec(count, referrer)
return err
}

View File

@ -2,6 +2,8 @@ package counters
import (
"database/sql"
"sync"
"time"
c "github.com/Azareal/Gosora/common"
qgen "github.com/Azareal/Gosora/query_gen"
@ -10,49 +12,61 @@ import (
var RouteViewCounter *DefaultRouteViewCounter
type RVBucket struct {
counter int
avg int
sync.Mutex
}
// TODO: Make this lockless?
type DefaultRouteViewCounter struct {
buckets []*RWMutexCounterBucket //[RouteID]count
buckets []*RVBucket //[RouteID]count
insert *sql.Stmt
insert5 *sql.Stmt
}
func NewDefaultRouteViewCounter(acc *qgen.Accumulator) (*DefaultRouteViewCounter, error) {
routeBuckets := make([]*RWMutexCounterBucket, len(routeMapEnum))
routeBuckets := make([]*RVBucket, len(routeMapEnum))
for bucketID, _ := range routeBuckets {
routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
routeBuckets[bucketID] = &RVBucket{counter: 0, avg: 0}
}
fields := "?,UTC_TIMESTAMP(),?"
fields := "?,?,UTC_TIMESTAMP(),?"
co := &DefaultRouteViewCounter{
buckets: routeBuckets,
insert: acc.Insert("viewchunks").Columns("count,createdAt,route").Fields(fields).Prepare(),
insert5: acc.BulkInsert("viewchunks").Columns("count,createdAt,route").Fields(fields,fields,fields,fields,fields).Prepare(),
insert: acc.Insert("viewchunks").Columns("count,avg,createdAt,route").Fields(fields).Prepare(),
insert5: acc.BulkInsert("viewchunks").Columns("count,avg,createdAt,route").Fields(fields, fields, fields, fields, fields).Prepare(),
}
if !c.Config.DisableAnalytics {
c.AddScheduledFifteenMinuteTask(co.Tick) // There could be a lot of routes, so we don't want to be running this every second
//c.AddScheduledSecondTask(co.Tick)
c.AddShutdownTask(co.Tick)
}
return co, acc.FirstError()
}
type RVCount struct {
RouteID int
Count int
Avg int
}
func (co *DefaultRouteViewCounter) Tick() error {
func (co *DefaultRouteViewCounter) Tick() (err error) {
var tb []RVCount
for routeID, b := range co.buckets {
var count int
b.RLock()
var count, avg int
b.Lock()
count = b.counter
b.counter = 0
b.RUnlock()
avg = b.avg
b.avg = 0
b.Unlock()
if count == 0 {
continue
}
tb = append(tb, RVCount{routeID,count})
tb = append(tb, RVCount{routeID, count, avg})
}
// TODO: Expand on this?
@ -69,7 +83,8 @@ func (co *DefaultRouteViewCounter) Tick() error {
}
for ; len(tb) > i; i++ {
err := co.insertChunk(tb[i].Count, tb[i].RouteID)
it := tb[i]
err = co.insertChunk(it.Count, it.Avg, it.RouteID)
if err != nil {
c.DebugLogf("tb: %+v\n", tb)
c.DebugLog("i: ", i)
@ -80,32 +95,40 @@ func (co *DefaultRouteViewCounter) Tick() error {
return nil
}
func (co *DefaultRouteViewCounter) insertChunk(count, route int) error {
func (co *DefaultRouteViewCounter) insertChunk(count, avg, route int) error {
routeName := reverseRouteMapEnum[route]
c.DebugLogf("Inserting a vchunk with a count of %d for route %s (%d)", count, routeName, route)
_, err := co.insert.Exec(count, routeName)
c.DebugLogf("Inserting a vchunk with a count of %d, avg of %d for route %s (%d)", count, avg, routeName, route)
_, err := co.insert.Exec(count, avg, routeName)
return err
}
func (co *DefaultRouteViewCounter) insert5Chunk(rvs []RVCount) error {
args := make([]interface{}, len(rvs) * 2)
args := make([]interface{}, len(rvs)*3)
i := 0
for _, rv := range rvs {
routeName := reverseRouteMapEnum[rv.RouteID]
if rv.Avg == 0 {
c.DebugLogf("Queueing a vchunk with a count of %d for routes %s (%d)", rv.Count, routeName, rv.RouteID)
args[i] = rv.Count
args[i+1] = routeName
i += 2
} else {
c.DebugLogf("Queueing a vchunk with count %d, avg %d for routes %s (%d)", rv.Count, rv.Avg, routeName, rv.RouteID)
}
c.DebugLogf("args: %+v\n", args)
args[i] = rv.Count
args[i+1] = rv.Avg
args[i+2] = routeName
i += 3
}
c.DebugDetailf("args: %+v\n", args)
_, err := co.insert5.Exec(args...)
return err
}
func (co *DefaultRouteViewCounter) Bump(route int) {
if c.Config.DisableAnalytics {
return
}
// TODO: Test this check
b := co.buckets[route]
c.DebugDetail("co.buckets[", route, "]: ", b)
c.DebugDetail("buckets[", route, "]: ", b)
if len(co.buckets) <= route || route < 0 {
return
}
@ -114,3 +137,27 @@ func (co *DefaultRouteViewCounter) Bump(route int) {
b.counter++
b.Unlock()
}
// TODO: Eliminate the lock?
func (co *DefaultRouteViewCounter) Bump2(route int, t time.Time) {
if c.Config.DisableAnalytics {
return
}
// TODO: Test this check
b := co.buckets[route]
c.DebugDetail("buckets[", route, "]: ", b)
if len(co.buckets) <= route || route < 0 {
return
}
micro := int(time.Since(t).Microseconds())
b.Lock()
b.counter++
if micro != b.avg {
if b.avg == 0 {
b.avg = micro
} else {
b.avg = (micro + b.avg) / 2
}
}
b.Unlock()
}

View File

@ -5,7 +5,7 @@ import (
"sync/atomic"
c "github.com/Azareal/Gosora/common"
"github.com/Azareal/Gosora/query_gen"
qgen "github.com/Azareal/Gosora/query_gen"
"github.com/pkg/errors"
)

View File

@ -97,7 +97,7 @@ func (s *SQLReplyStore) Exists(id int) bool {
}
// TODO: Write a test for this
func (s *SQLReplyStore) Create(t *Topic, content, ip string, uid int) (rid int, err error) {
func (s *SQLReplyStore) Create(t *Topic, content, ip string, uid int) (id int, err error) {
if Config.DisablePostIP {
ip = ""
}
@ -110,8 +110,8 @@ func (s *SQLReplyStore) Create(t *Topic, content, ip string, uid int) (rid int,
if err != nil {
return 0, err
}
rid = int(lastID)
return rid, t.AddReply(rid, uid)
id = int(lastID)
return id, t.AddReply(id, uid)
}
// TODO: Write a test for this

View File

@ -113,6 +113,7 @@ type config struct {
EnableCDNPush bool
DisableNoavatarRange bool
DisableDefaultNoavatar bool
DisableAnalytics bool
RefNoTrack bool
RefNoRef bool

View File

@ -116,6 +116,8 @@ DisableNoavatarRange - This switch lets you disable the noavatar algorithm which
DisableDefaultNoavatar - This switch lets you disable the default noavatar algorithm which may intercept noavatars for increased efficiency. Default: false
DisableAnalytics - This switch lets you disable the analytics subsystem. Default: false
RefNoTrack - This switch disables tracking the referrers of users who click from another site to your site and the referrers of any requests to resources from other sites as-well.
RefNoRef - This switch makes it so that if a user clicks on a link, then the incoming site won't know which site they're coming from.

File diff suppressed because it is too large Load Diff

29
main.go
View File

@ -32,6 +32,7 @@ import (
qgen "github.com/Azareal/Gosora/query_gen"
"github.com/Azareal/Gosora/routes"
"github.com/fsnotify/fsnotify"
//"github.com/lucas-clemente/quic-go/http3"
"github.com/pkg/errors"
)
@ -272,6 +273,7 @@ func storeInit() (err error) {
}
log.Print("Initialising the view counters")
if !c.Config.DisableAnalytics {
co.GlobalViewCounter, err = co.NewGlobalViewCounter(acc)
if err != nil {
return errors.WithStack(err)
@ -288,6 +290,21 @@ func storeInit() (err error) {
if err != nil {
return errors.WithStack(err)
}
if !c.Config.RefNoTrack {
co.ReferrerTracker, err = co.NewDefaultReferrerTracker()
if err != nil {
return errors.WithStack(err)
}
}
co.MemoryCounter, err = co.NewMemoryCounter(acc)
if err != nil {
return errors.WithStack(err)
}
co.PerfCounter, err = co.NewDefaultPerfCounter(acc)
if err != nil {
return errors.WithStack(err)
}
}
co.RouteViewCounter, err = co.NewDefaultRouteViewCounter(acc)
if err != nil {
return errors.WithStack(err)
@ -308,18 +325,6 @@ func storeInit() (err error) {
if err != nil {
return errors.WithStack(err)
}
co.ReferrerTracker, err = co.NewDefaultReferrerTracker()
if err != nil {
return errors.WithStack(err)
}
co.MemoryCounter, err = co.NewMemoryCounter(acc)
if err != nil {
return errors.WithStack(err)
}
co.PerfCounter, err = co.NewDefaultPerfCounter(acc)
if err != nil {
return errors.WithStack(err)
}
log.Print("Initialising the meta store")
c.Meta, err = meta.NewDefaultMetaStore(acc)

View File

@ -50,6 +50,7 @@ func init() {
addPatch(30, patch30)
addPatch(31, patch31)
addPatch(32, patch32)
addPatch(33, patch33)
}
func patch0(scanner *bufio.Scanner) (err error) {
@ -894,7 +895,6 @@ func patch31(scanner *bufio.Scanner) error {
return nil
}
func patch32(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.CreateTable("perfchunks", "", "",
[]tC{
@ -905,3 +905,7 @@ func patch32(scanner *bufio.Scanner) error {
}, nil,
))
}
func patch33(scanner *bufio.Scanner) error {
return execStmt(qgen.Builder.AddColumn("viewchunks", tC{"avg", "int", 0, false, false, "0"}, nil))
}

View File

@ -31,6 +31,8 @@ func TestProcessWhere(t *testing.T) {
expectTokens(t, whs, MT{TokenColumn, "uid"}, MT{TokenOp, "="}, MT{TokenNumber, "0"})
whs = processWhere("uid = '1'")
expectTokens(t, whs, MT{TokenColumn, "uid"}, MT{TokenOp, "="}, MT{TokenString, "1"})
whs = processWhere("uid = 't'")
expectTokens(t, whs, MT{TokenColumn, "uid"}, MT{TokenOp, "="}, MT{TokenString, "t"})
whs = processWhere("uid = ''")
expectTokens(t, whs, MT{TokenColumn, "uid"}, MT{TokenOp, "="}, MT{TokenString, ""})
whs = processWhere("uid = '")
@ -42,8 +44,12 @@ func TestProcessWhere(t *testing.T) {
expectTokens(t, whs, MT{TokenColumn, "uid"}, MT{TokenOp, "="}, MT{TokenNumber, "1"})
whs = processWhere("uid=0")
expectTokens(t, whs, MT{TokenColumn, "uid"}, MT{TokenOp, "="}, MT{TokenNumber, "0"})
whs = processWhere("uid=20")
expectTokens(t, whs, MT{TokenColumn, "uid"}, MT{TokenOp, "="}, MT{TokenNumber, "20"})
whs = processWhere("uid='1'")
expectTokens(t, whs, MT{TokenColumn, "uid"}, MT{TokenOp, "="}, MT{TokenString, "1"})
whs = processWhere("uid='t'")
expectTokens(t, whs, MT{TokenColumn, "uid"}, MT{TokenOp, "="}, MT{TokenString, "t"})
whs = processWhere("uid")
expectTokens(t, whs, MT{TokenColumn, "uid"})

View File

@ -47,23 +47,24 @@ func main() {
allRouteNames = append(allRouteNames, RouteName{name, strings.Replace(name, "common.", "c.", -1)})
allRouteMap[name] = len(allRouteNames) - 1
}
mapIt("routes.Error")
countToIndents := func(indent int) (indentor string) {
for i := 0; i < indent; i++ {
indentor += "\t"
}
return indentor
}
runBefore := func(runnables []Runnable, indent int) (out string) {
indentor := countToIndents(indent)
runBefore := func(runnables []Runnable, indentCount int) (out string) {
indent := countToIndents(indentCount)
if len(runnables) > 0 {
for _, runnable := range runnables {
if runnable.Literal {
out += "\n\t" + indentor + runnable.Contents
out += "\n\t" + indent + runnable.Contents
} else {
out += "\n" + indentor + "err = c." + runnable.Contents + "(w,req,user)\n" +
indentor + "if err != nil {\n" +
indentor + "\treturn err\n" +
indentor + "}\n" + indentor
out += "\n" + indent + "err = c." + runnable.Contents + "(w,req,user)\n" +
indent + "if err != nil {\n" +
indent + "\treturn err\n" +
indent + "}\n" + indent
}
}
}
@ -74,13 +75,13 @@ func main() {
mapIt(route.Name)
end := len(route.Path) - 1
out += "\n\t\tcase \"" + route.Path[0:end] + "\":"
//out += "\n\t\t\tid = " + strconv.Itoa(allRouteMap[route.Name])
out += runBefore(route.RunBefore, 4)
out += "\n\t\t\tco.RouteViewCounter.Bump(" + strconv.Itoa(allRouteMap[route.Name]) + ")"
if !route.Action && !route.NoHead {
out += "\n\t\t\thead, err := c.UserCheck(w,req,&user)"
out += "\n\t\t\th, err := c.UserCheck(w,req,&user)"
out += "\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}"
vcpy := route.Vars
route.Vars = []string{"head"}
route.Vars = []string{"h"}
route.Vars = append(route.Vars, vcpy...)
}
out += "\n\t\t\terr = " + strings.Replace(route.Name, "common.", "c.", -1) + "(w,req,user"
@ -88,6 +89,11 @@ func main() {
out += "," + item
}
out += `)`
if !route.Action && !route.NoHead {
out += "\n\t\t\tco.RouteViewCounter.Bump2(" + strconv.Itoa(allRouteMap[route.Name]) + ", h.StartedAt)"
} else {
out += "\n\t\t\tco.RouteViewCounter.Bump(" + strconv.Itoa(allRouteMap[route.Name]) + ")"
}
}
for _, group := range r.routeGroups {
@ -105,6 +111,7 @@ func main() {
mapIt(route.Name)
out += "\n\t\t\t\tcase \"" + route.Path + "\":"
//out += "\n\t\t\t\t\tid = " + strconv.Itoa(allRouteMap[route.Name])
if len(route.RunBefore) > 0 {
skipRunnable:
for _, runnable := range route.RunBefore {
@ -136,12 +143,11 @@ func main() {
}
}
}
out += "\n\t\t\t\t\tco.RouteViewCounter.Bump(" + strconv.Itoa(allRouteMap[route.Name]) + ")"
if !route.Action && !route.NoHead && !group.NoHead {
out += "\n\t\t\t\thead, err := c.UserCheck(w,req,&user)"
out += "\n\t\t\t\th, err := c.UserCheck(w,req,&user)"
out += "\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}"
vcpy := route.Vars
route.Vars = []string{"head"}
route.Vars = []string{"h"}
route.Vars = append(route.Vars, vcpy...)
}
out += "\n\t\t\t\t\terr = " + strings.Replace(route.Name, "common.", "c.", -1) + "(w,req,user"
@ -149,18 +155,23 @@ func main() {
out += "," + item
}
out += ")"
if !route.Action && !route.NoHead && !group.NoHead {
out += "\n\t\t\t\t\tco.RouteViewCounter.Bump2(" + strconv.Itoa(allRouteMap[route.Name]) + ", h.StartedAt)"
} else {
out += "\n\t\t\t\t\tco.RouteViewCounter.Bump(" + strconv.Itoa(allRouteMap[route.Name]) + ")"
}
}
if defaultRoute.Name != "" {
mapIt(defaultRoute.Name)
out += "\n\t\t\t\tdefault:"
//out += "\n\t\t\t\t\tid = " + strconv.Itoa(allRouteMap[defaultRoute.Name])
out += runBefore(defaultRoute.RunBefore, 4)
out += "\n\t\t\t\t\tco.RouteViewCounter.Bump(" + strconv.Itoa(allRouteMap[defaultRoute.Name]) + ")"
if !defaultRoute.Action && !defaultRoute.NoHead && !group.NoHead {
out += "\n\t\t\t\t\thead, err := c.UserCheck(w,req,&user)"
out += "\n\t\t\t\t\th, err := c.UserCheck(w,req,&user)"
out += "\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}"
vcpy := defaultRoute.Vars
defaultRoute.Vars = []string{"head"}
defaultRoute.Vars = []string{"h"}
defaultRoute.Vars = append(defaultRoute.Vars, vcpy...)
}
out += "\n\t\t\t\t\terr = " + strings.Replace(defaultRoute.Name, "common.", "c.", -1) + "(w,req,user"
@ -168,6 +179,11 @@ func main() {
out += ", " + item
}
out += ")"
if !defaultRoute.Action && !defaultRoute.NoHead && !group.NoHead {
out += "\n\t\t\t\t\tco.RouteViewCounter.Bump2(" + strconv.Itoa(allRouteMap[defaultRoute.Name]) + ", h.StartedAt)"
} else {
out += "\n\t\t\t\t\tco.RouteViewCounter.Bump(" + strconv.Itoa(allRouteMap[defaultRoute.Name]) + ")"
}
}
out += `
}`
@ -180,6 +196,7 @@ func main() {
mapIt("routes.RobotsTxt")
mapIt("routes.SitemapXml")
mapIt("routes.OpenSearchXml")
mapIt("routes.Favicon")
mapIt("routes.BadRoute")
mapIt("routes.HTTPSRedirect")
tmplVars.AllRouteNames = allRouteNames
@ -462,7 +479,7 @@ func (r *GenRouter) DumpRequest(req *http.Request, prepend string) {
var heads string
for key, value := range req.Header {
for _, vvalue := range value {
heads += "Header '" + c.SanitiseSingleLine(key) + "': " + c.SanitiseSingleLine(vvalue) + "!\n"
heads += "Header '" + c.SanitiseSingleLine(key) + "': " + c.SanitiseSingleLine(vvalue) + "\n"
}
}
@ -472,7 +489,7 @@ func (r *GenRouter) DumpRequest(req *http.Request, prepend string) {
"Host: " + c.SanitiseSingleLine(req.Host) + "\n" +
"URL.Path: " + c.SanitiseSingleLine(req.URL.Path) + "\n" +
"URL.RawQuery: " + c.SanitiseSingleLine(req.URL.RawQuery) + "\n" +
"Referer(): " + c.SanitiseSingleLine(req.Referer()) + "\n" +
"Referer: " + c.SanitiseSingleLine(req.Referer()) + "\n" +
"RemoteAddr: " + req.RemoteAddr + "\n")
}
@ -562,9 +579,9 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
break
}
}
lowerPath := strings.ToLower(req.URL.Path)
lp := strings.ToLower(req.URL.Path)
// TODO: Flag any requests which has a dot with anything but a number after that
if strings.Contains(req.URL.Path,"..") || strings.Contains(req.URL.Path,"--") || strings.Contains(lowerPath,".php") || strings.Contains(lowerPath,".asp") || strings.Contains(lowerPath,".cgi") || strings.Contains(lowerPath,".py") || strings.Contains(lowerPath,".sql") || strings.Contains(lowerPath,".action") {
if strings.Contains(lp,"..")/* || strings.Contains(lp,"--")*/ || strings.Contains(lp,".php") || strings.Contains(lp,".asp") || strings.Contains(lp,".cgi") || strings.Contains(lp,".py") || strings.Contains(lp,".sql") || strings.Contains(lp,".action") {
r.SuspiciousRequest(req,"Bad snippet in path")
}
@ -572,6 +589,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.URL.Path == "/" {
req.URL.Path = c.Config.DefaultPath
}
//log.Print("URL.Path: ", req.URL.Path)
var prefix, extraData string
prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1]
@ -603,10 +621,14 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
r.DumpRequest(req,"before routes.StaticFile")
}
// Increment the request counter
if !c.Config.DisableAnalytics {
co.GlobalViewCounter.Bump()
}
if prefix == "/s" { //old prefix: /static
if !c.Config.DisableAnalytics {
co.RouteViewCounter.Bump({{index .AllRouteMap "routes.StaticFile"}})
}
req.URL.Path += extraData
routes.StaticFile(w, req)
return
@ -626,8 +648,10 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Track the user agents. Unfortunately, everyone pretends to be Mozilla, so this'll be a little less efficient than I would like.
// TODO: Add a setting to disable this?
// TODO: Use a more efficient detector instead of smashing every possible combination in
ua := strings.TrimSpace(strings.Replace(strings.TrimPrefix(req.UserAgent(),"Mozilla/5.0 ")," Safari/537.36","",-1)) // Noise, no one's going to be running this and it would require some sort of agent ranking system to determine which identifier should be prioritised over another
var agent string
if !c.Config.DisableAnalytics {
ua := strings.TrimSpace(strings.Replace(strings.TrimPrefix(req.UserAgent(),"Mozilla/5.0 ")," Safari/537.36","",-1)) // Noise, no one's going to be running this and it would require some sort of agent ranking system to determine which identifier should be prioritised over another
if ua == "" {
co.AgentViewCounter.Bump({{.AllAgentMap.blank}})
if c.Dev.DebugMode {
@ -736,8 +760,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// TODO: Do we want to track missing language headers too? Maybe as it's own type, e.g. "noheader"?
lang := req.Header.Get("Accept-Language")
if lang != "" {
lang = strings.TrimSpace(lang)
lLang := strings.Split(lang,"-")
lLang := strings.Split(strings.TrimSpace(lang),"-")
tLang := strings.Split(strings.Split(lLang[0],";")[0],",")
c.DebugDetail("tLang:", tLang)
var llLang string
@ -764,12 +787,15 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
ref = strings.TrimPrefix(strings.TrimPrefix(ref,"http://"),"https://")
ref = strings.Split(ref,"/")[0]
portless := strings.Split(ref,":")[0]
// TODO: Handle c.Site.Host in uppercase too?
if portless != "localhost" && portless != "127.0.0.1" && portless != c.Site.Host {
co.ReferrerTracker.Bump(ref)
}
}
}
}
// Deal with the session stuff, etc.
user, ok := c.PreRoute(w, req)
if !ok {
@ -804,12 +830,15 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if ferr != nil {
r.handleError(ferr,w,req,user)
}
/*if !c.Config.DisableAnalytics {
co.RouteViewCounter.Bump(id)
}*/
hTbl.VhookNoRet("router_end", w, req, user, prefix, extraData)
//c.StoppedServer("Profile end")
}
func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c.User, prefix, extraData string) c.RouteError {
func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c.User, prefix, extraData string) /*(id int, orerr */c.RouteError/*)*/ {
var err c.RouteError
switch(prefix) {` + out + `
/*case "/sitemaps": // TODO: Count these views
@ -817,6 +846,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
err = sitemapSwitch(w,req)*/
case "/uploads":
if extraData == "" {
co.RouteViewCounter.Bump({{index .AllRouteMap "routes.UploadedFile"}})
return c.NotFound(w,req,nil)
}
gzw, ok := w.(c.GzipResponseWriter)
@ -826,10 +856,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
h.Del("Content-Type")
h.Del("Content-Encoding")
}
co.RouteViewCounter.Bump({{index .AllRouteMap "routes.UploadedFile"}})
req.URL.Path += extraData
// TODO: Find a way to propagate errors up from this?
r.UploadHandler(w,req) // TODO: Count these views
co.RouteViewCounter.Bump({{index .AllRouteMap "routes.UploadedFile"}})
return nil
case "":
// Stop the favicons, robots.txt file, etc. resolving to the topics list
@ -848,6 +878,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
}
req.URL.Path = "/s/favicon.ico"
routes.StaticFile(w,req)
co.RouteViewCounter.Bump({{index .AllRouteMap "routes.Favicon"}})
return nil
case "opensearch.xml":
co.RouteViewCounter.Bump({{index .AllRouteMap "routes.OpenSearchXml"}})
@ -856,17 +887,19 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
co.RouteViewCounter.Bump({{index .AllRouteMap "routes.SitemapXml"}})
return routes.SitemapXml(w,req)*/
}
co.RouteViewCounter.Bump({{index .AllRouteMap "routes.Error"}})
return c.NotFound(w,req,nil)
default:
// A fallback for dynamic routes, e.g. ones declared by plugins
r.RLock()
handle, ok := r.extraRoutes[req.URL.Path]
h, ok := r.extraRoutes[req.URL.Path]
r.RUnlock()
if ok {
co.RouteViewCounter.Bump({{index .AllRouteMap "routes.DynamicRoute"}}) // TODO: Be more specific about *which* dynamic route it is
req.URL.Path += extraData
return handle(w,req,user)
// TODO: Be more specific about *which* dynamic route it is
co.RouteViewCounter.Bump({{index .AllRouteMap "routes.DynamicRoute"}})
return h(w,req,user)
}
lp := strings.ToLower(req.URL.Path)
@ -904,5 +937,8 @@ func writeFile(name, content string) {
if err != nil {
log.Fatal(err)
}
f.Close()
err = f.Close()
if err != nil {
log.Fatal(err)
}
}

View File

@ -63,8 +63,9 @@ func (r *RouteImpl) NoGzip() *RouteImpl {
return r.LitBeforeMultiline(`gzw, ok := w.(c.GzipResponseWriter)
if ok {
w = gzw.ResponseWriter
w.Header().Del("Content-Type")
w.Header().Del("Content-Encoding")
h := w.Header()
h.Del("Content-Type")
h.Del("Content-Encoding")
}`)
}
@ -77,15 +78,15 @@ func blankRoute() *RouteImpl {
return &RouteImpl{"", "", false, false, []string{}, []Runnable{}, nil}
}
func route(fname string, path string, action bool, special bool, args ...string) *RouteImpl {
func route(fname, path string, action, special bool, args ...string) *RouteImpl {
return &RouteImpl{fname, path, action, special, args, []Runnable{}, nil}
}
func View(fname string, path string, args ...string) *RouteImpl {
func View(fname, path string, args ...string) *RouteImpl {
return route(fname, path, false, false, args...)
}
func MView(fname string, path string, args ...string) *RouteImpl {
func MView(fname, path string, args ...string) *RouteImpl {
route := route(fname, path, false, false, args...)
if !route.hasBefore("SuperModOnly", "AdminOnly") {
route.Before("MemberOnly")
@ -93,7 +94,7 @@ func MView(fname string, path string, args ...string) *RouteImpl {
return route
}
func MemberView(fname string, path string, args ...string) *RouteImpl {
func MemberView(fname, path string, args ...string) *RouteImpl {
route := route(fname, path, false, false, args...)
if !route.hasBefore("SuperModOnly", "AdminOnly") {
route.Before("MemberOnly")
@ -101,7 +102,7 @@ func MemberView(fname string, path string, args ...string) *RouteImpl {
return route
}
func ModView(fname string, path string, args ...string) *RouteImpl {
func ModView(fname, path string, args ...string) *RouteImpl {
route := route(fname, path, false, false, args...)
if !route.hasBefore("AdminOnly") {
route.Before("SuperModOnly")
@ -109,7 +110,7 @@ func ModView(fname string, path string, args ...string) *RouteImpl {
return route
}
func Action(fname string, path string, args ...string) *RouteImpl {
func Action(fname, path string, args ...string) *RouteImpl {
route := route(fname, path, true, false, args...)
route.Before("NoSessionMismatch")
if !route.hasBefore("SuperModOnly", "AdminOnly") {
@ -118,11 +119,11 @@ func Action(fname string, path string, args ...string) *RouteImpl {
return route
}
func AnonAction(fname string, path string, args ...string) *RouteImpl {
func AnonAction(fname, path string, args ...string) *RouteImpl {
return route(fname, path, true, false, args...).Before("ParseForm")
}
func Special(fname string, path string, args ...string) *RouteImpl {
func Special(fname, path string, args ...string) *RouteImpl {
return route(fname, path, false, true, args...).LitBefore("req.URL.Path += extraData")
}
@ -131,7 +132,7 @@ type uploadAction struct {
Route *RouteImpl
}
func UploadAction(fname string, path string, args ...string) *uploadAction {
func UploadAction(fname, path string, args ...string) *uploadAction {
route := route(fname, path, true, false, args...)
if !route.hasBefore("SuperModOnly", "AdminOnly") {
route.Before("MemberOnly")
@ -154,6 +155,6 @@ type RouteSet struct {
Items []*RouteImpl
}
func Set(name string, path string, routes ...*RouteImpl) RouteSet {
func Set(name, path string, routes ...*RouteImpl) RouteSet {
return RouteSet{name, path, routes}
}

View File

@ -7,6 +7,12 @@ func UploadedFile() {
}
func BadRoute() {
}
func Favicon() {
}
// TODO: Temporary stub for handling route group errors
func Error() {
}
// Real implementation is in router_gen/main.go, this is just a stub to map the analytics onto
func HTTPSRedirect() {

View File

@ -1,5 +1,6 @@
CREATE TABLE [viewchunks] (
[count] int DEFAULT 0 not null,
[avg] int DEFAULT 0 not null,
[createdAt] datetime not null,
[route] nvarchar (200) not null
);

View File

@ -1,5 +1,6 @@
CREATE TABLE `viewchunks` (
`count` int DEFAULT 0 not null,
`avg` int DEFAULT 0 not null,
`createdAt` datetime not null,
`route` varchar(200) not null
);

View File

@ -1,5 +1,6 @@
CREATE TABLE "viewchunks" (
`count` int DEFAULT 0 not null,
`avg` int DEFAULT 0 not null,
`createdAt` timestamp not null,
`route` varchar (200) not null
);