From: Scott Worley <sworley@chkno.net>
Date: Sat, 28 Jul 2012 05:45:54 +0000 (-0700)
Subject: First cut at minimum-functionality server
X-Git-Url: http://git.scottworley.com/reliable-chat/commitdiff_plain/92d1d6ac689a422fcf43dec8f5a081aa09f05ae5

First cut at minimum-functionality server
---

92d1d6ac689a422fcf43dec8f5a081aa09f05ae5
diff --git a/server/server.go b/server/server.go
new file mode 100644
index 0000000..a8b1c53
--- /dev/null
+++ b/server/server.go
@@ -0,0 +1,108 @@
+package main
+
+import "container/list"
+import "encoding/json"
+import "log"
+import "net/http"
+import "time"
+
+type Message struct {
+	Time time.Time
+	Text string
+}
+
+type FetchResponse struct {
+	Messages []Message
+	Time     time.Time
+}
+
+type StoreRequest struct {
+	StartTime time.Time
+	Messages  chan<- []Message
+}
+
+type Store struct {
+	Add chan Message
+	Get chan StoreRequest
+}
+
+// TODO: Monotonic clock
+
+func manage_store(store Store) {
+	messages := list.New()
+	message_count := 0
+	max_messages := 1000
+	waiting := list.New()
+	for {
+		select {
+		case new_message := <-store.Add:
+			messages.PushBack(new_message)
+			for waiter := waiting.Front(); waiter != nil; waiter = waiter.Next() {
+				waiter.Value.(StoreRequest).Messages <- []Message{new_message}
+				close(waiter.Value.(StoreRequest).Messages)
+			}
+			waiting.Init()
+			if message_count < max_messages {
+				message_count++
+			} else {
+				messages.Remove(messages.Front())
+			}
+		case request := <-store.Get:
+			if messages.Back() == nil || request.StartTime.After(messages.Back().Value.(Message).Time) {
+				waiting.PushBack(request)
+			} else {
+				start := messages.Back()
+				response_size := 1
+				if messages.Front().Value.(Message).Time.After(request.StartTime) {
+					start = messages.Front()
+					response_size = message_count
+				} else {
+					for start.Prev().Value.(Message).Time.After(request.StartTime) {
+						start = start.Prev()
+						response_size++
+					}
+				}
+				response_messages := make([]Message, 0, response_size)
+				for m := start; m != nil; m = m.Next() {
+					response_messages = append(response_messages, m.Value.(Message))
+				}
+				request.Messages <- response_messages
+			}
+		}
+	}
+}
+
+func start_store() Store {
+	store := Store{make(chan Message, 20), make(chan StoreRequest, 20)}
+	go manage_store(store)
+	return store
+}
+
+func start_server(store Store) {
+	http.HandleFunc("/fetch", func(w http.ResponseWriter, r *http.Request) {
+		var since time.Time // TODO: Get start time from URL
+		messages_from_store := make(chan []Message, 1)
+		store.Get <- StoreRequest{since, messages_from_store}
+
+		json_encoded, err := json.Marshal(FetchResponse{<-messages_from_store, time.Now()})
+		if err != nil {
+			log.Print("json encode: ", err)
+			w.WriteHeader(http.StatusInternalServerError)
+			return
+		}
+		w.Header().Add("Content-Type", "application/json")
+		w.Write(json_encoded)
+	})
+
+	http.HandleFunc("/speak", func(w http.ResponseWriter, r *http.Request) {
+		text := "woof" // TODO: Get text from URL
+		store.Add <- Message{time.Now(), text}
+	})
+
+	log.Fatal(http.ListenAndServe(":8080", nil))
+}
+
+func main() {
+	store := start_store()
+	start_server(store)
+}