Hopefully prevent race conditions

This commit is contained in:
Neale Pickett 2019-11-24 10:24:08 -06:00
parent ba2d6a46ad
commit 0fdc23f923
3 changed files with 107 additions and 81 deletions

View File

@ -1,105 +1,125 @@
package main package main
import ( import (
"net/http" "encoding/json"
"log" "log"
"time" "net/http"
"strconv" "strconv"
"encoding/json" "time"
) )
type Message struct { type Message struct {
When int64 When int64
Who string Who string
Text string Text string
} }
type Forum struct { type Forum struct {
Name string Name string
Log []Message Log []Message
SendQ chan Message
LogQ chan []Message
} }
var foraByName map[string]*Forum var foraByName map[string]*Forum
func (f *Forum) HandleSends() {
for m := range f.SendQ {
f.Log = append(f.Log, m)
}
}
func (f *Forum) HandleReads() {
for {
f.LogQ <- f.Log
}
}
func newForum(name string) *Forum { func newForum(name string) *Forum {
f := &Forum{ f := &Forum{
Name: name, Name: name,
} SendQ: make(chan Message, 10),
foraByName[name] = f LogQ: make(chan []Message, 10),
return f }
go f.HandleSends()
go f.HandleReads()
foraByName[name] = f
return f
} }
func sayHandler(w http.ResponseWriter, r *http.Request) { func sayHandler(w http.ResponseWriter, r *http.Request) {
message := Message{ message := Message{
When: time.Now().Unix(), When: time.Now().Unix(),
Who: r.FormValue("who"), Who: r.FormValue("who"),
Text: r.FormValue("text"), Text: r.FormValue("text"),
} }
log.Println(message) forumName := r.FormValue("forum")
forumName := r.FormValue("forum") f, ok := foraByName[forumName]
forum, ok := foraByName[forumName] if !ok {
if !ok { f = newForum(forumName)
forum = newForum(forumName) }
} f.SendQ <- message
forum.Log = append(forum.Log, message)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
func readHandler(w http.ResponseWriter, r *http.Request) { func readHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm() r.ParseForm()
forumName := r.FormValue("forum") forumName := r.FormValue("forum")
entriesStr := r.FormValue("entries") entriesStr := r.FormValue("entries")
if entriesStr == "" { if entriesStr == "" {
entriesStr = "0" entriesStr = "0"
} }
entries, err := strconv.Atoi(entriesStr) entries, err := strconv.Atoi(entriesStr)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
if entries <= 0 { if entries <= 0 {
entries = 20 entries = 20
} else if entries > 500 { } else if entries > 500 {
entries = 500 entries = 500
} }
forum, ok := foraByName[forumName] f, ok := foraByName[forumName]
if !ok { if !ok {
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
pos := len(forum.Log) - entries log := <-f.LogQ
if pos < 0 {
pos = 0
}
w.Header().Set("Content-Type", "application/json") pos := len(log) - entries
w.WriteHeader(http.StatusOK) if pos < 0 {
pos = 0
}
e := json.NewEncoder(w) w.Header().Set("Content-Type", "application/json")
e.Encode(forum.Log[pos:]) w.WriteHeader(http.StatusOK)
e := json.NewEncoder(w)
e.Encode(f.Log[pos:])
} }
func main() { func main() {
http.HandleFunc("/say", sayHandler) http.HandleFunc("/say", sayHandler)
http.HandleFunc("/read", readHandler) http.HandleFunc("/read", readHandler)
http.Handle("/", http.FileServer(http.Dir("static/"))) http.Handle("/", http.FileServer(http.Dir("static/")))
foraByName = map[string]*Forum{} foraByName = map[string]*Forum{}
f := newForum("") f := newForum("")
f.Log = []Message{ f.Log = []Message{
{ {
When: time.Now().Unix(), When: time.Now().Unix(),
Who: "(system)", Who: "(system)",
Text: "Welcome to μChat", Text: "Welcome to μChat",
}, },
} }
bind := ":8080" bind := ":8080"
log.Printf("Listening on %s", bind) log.Printf("Listening on %s", bind)
log.Fatal(http.ListenAndServe(bind, nil)) log.Fatal(http.ListenAndServe(bind, nil))
} }

View File

@ -11,6 +11,10 @@ body {
input[name="who"] { input[name="who"] {
width: 6em; width: 6em;
} }
input[name="text"] {
min-width: 30em;
width: calc(90% - 6em);
}
.when { .when {
color: #888; color: #888;

View File

@ -18,7 +18,6 @@ function μchatInit(initEvent) {
let form = event.target let form = event.target
let inp = form.elements.text let inp = form.elements.text
let body = new FormData(form) let body = new FormData(form)
console.log(form, body)
fetch("say", { fetch("say", {
method: "POST", method: "POST",
body: body, body: body,
@ -86,6 +85,9 @@ function μchatInit(initEvent) {
.then(updateLog) .then(updateLog)
} }
}) })
.catch(err => {
toast("Server error: " + err)
})
} }
for (let f of document.forms) { for (let f of document.forms) {