Files

192 lines
4.1 KiB
Go

package main
import (
"encoding/json"
"flag"
"fmt"
"net/http"
"os"
"sync"
"time"
)
func getIp(r *http.Request) string {
// Get the remote address
var ip = r.RemoteAddr
// If behind a proxy, get the real IP
if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
ip = fwd
}
return ip
}
type Viewer struct {
LastActive int64
ActiveMirror int
}
type ViewCountRequest struct {
ActiveMirror int `json:"active_mirror"`
}
type ViewCountResponse struct {
TotalCount int `json:"total_count"`
ByMirror map[int]int `json:"by_mirror"`
}
func main() {
var addr = flag.String("addr", ":8080", "http service address")
flag.Parse()
var viewers = make(map[string]Viewer)
var mirrorActivity = make(map[int]int)
var lockViewers sync.Mutex
var lockMirrorActivity sync.Mutex
// Handle view counting
http.HandleFunc("/viewcount", func(w http.ResponseWriter, r *http.Request) {
// Get the current time
var now = time.Now().Unix()
// Get the remote address
var ip = getIp(r)
// Decode body JSON as a ViewerMetric
var metric ViewCountRequest
if err := json.NewDecoder(r.Body).Decode(&metric); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Update the last active time
lockViewers.Lock()
viewers[ip] = Viewer{
LastActive: now,
ActiveMirror: metric.ActiveMirror,
}
var viewersCount = len(viewers)
lockViewers.Unlock()
// Respond
lockMirrorActivity.Lock()
var response = ViewCountResponse{
TotalCount: viewersCount,
ByMirror: mirrorActivity,
}
lockMirrorActivity.Unlock()
if err := json.NewEncoder(w).Encode(response); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
// Periodically remove inactive viewers
go func() {
for {
time.Sleep(time.Second)
// Get the current time
var now = time.Now().Unix()
// Remove any viewers that have not been active in the last 10 seconds
lockViewers.Lock()
for ip, viewer := range viewers {
if now-viewer.LastActive > 10 {
delete(viewers, ip)
}
}
lockViewers.Unlock()
}
}()
// Periodically log the number of viewers
go func() {
// Prepare file to log to
var f, ferr = os.OpenFile("viewers.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if ferr != nil {
fmt.Println("Error opening file:", ferr)
fmt.Println("viewer count will not be logged")
return
}
// Loop forever
var last_count = 0
for {
var now = time.Now().Format(time.RFC3339)
lockViewers.Lock()
var count = len(viewers)
lockViewers.Unlock()
// If the viewer count has changed, log it
if count != last_count {
fmt.Fprintf(f, "%s\t%d\n", now, count)
last_count = count
}
time.Sleep(time.Second)
}
}()
// Periodically aggregate viewer count per mirror
go func() {
// Prepare file to log to
var f, ferr = os.OpenFile("viewers_per_mirror.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if ferr != nil {
fmt.Println("Error opening file:", ferr)
fmt.Println("mirror statistics will not be logged")
return
}
// Loop forever
for {
var now = time.Now().Format(time.RFC3339)
var counts = make(map[int]int)
// Loop through all viewers
lockViewers.Lock()
for _, viewer := range viewers {
// Increment the mirror count
counts[viewer.ActiveMirror]++
}
lockViewers.Unlock()
// Check if the counts have changed
var changed = false
lockMirrorActivity.Lock()
for mirror, count := range counts {
if count != mirrorActivity[mirror] {
changed = true
break
}
}
lockMirrorActivity.Unlock()
// If the counts have changed, log them
if changed {
for mirror, count := range counts {
fmt.Fprintf(f, "%s\t%d\t%d\n", now, mirror, count)
}
}
// Update the mirror activity
lockMirrorActivity.Lock()
mirrorActivity = counts
lockMirrorActivity.Unlock()
time.Sleep(time.Second)
}
}()
// Handle static files
http.Handle("/", http.FileServer(http.Dir("./static")))
// Start the server
fmt.Println("Starting server on", *addr)
err := http.ListenAndServe(*addr, nil)
if err != nil {
fmt.Println(err)
}
}