<commit_before>package main

import (
	_ "time"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"encoding/json"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"runtime"
	"errors"
)

const MESSAGE_QUEUE_SIZE = 10

var authKey = []byte("somesecretauth")
var store sessions.Store
var pool *Pool
var clients map[int64]*Client

var db *sql.DB
// var tv syscall.Timeval

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      int64
	client1 *Client
	client2 *Client
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		fmt.Println("match found for ", c1.id, " and ", c2.id)

		b := make([]byte, 8)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 8 {
			return
		}
		crId, _ := binary.Varint(b)

		room := &Room{crId, c1, c2}

		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	fmt.Println(session.Values)
	if userid == nil {
		return 0, errors.New("no cookie set")
	}
	return userid.(int64), nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, _ = sql.Open("mysql", "root:pass@/suitup")
	defer db.Close()

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/", mainHandle)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)

	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("/home/suitup/hackmit/HackMIT2/"))))
	http.ListenAndServe(":8080", nil)
}

type IdQuery struct {
    Id            int64      `json:"id"`

}

func mainHandle(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "hey")
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

  	fmt.Println("uid: ", uid)
	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}
	clients[uid] = client
	pool.in <- client

	fmt.Println("added ", uid, " to queue")
	chatroom := <- retChan

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":", chatroom.id, "}")
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	fmt.Fprint(w, uid)
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println(uid)

	message := r.FormValue("s")

	// message := r.PostFormValue("message")

	client := clients[uid]

	if client != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}-", message)
	} else {
		fmt.Fprint(w, "{\"status\":\"failure\"}-", message)
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	client := clients[uid]

	if client != nil {
		fmt.Println("client found")
		select {
		case message, ok := <- clients[uid].in:
			fmt.Println("message pulled from channel")
			if ok {
				fmt.Fprint(w, message)
			} else {
				fmt.Fprint(w, "")
			}
		default:
			fmt.Println("")
			fmt.Fprint(w, "")
		}

	} else {
		fmt.Println("client not found")
		fmt.Fprint(w, "")
	}
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		// row := db.QueryRow("SELECT id FROM users")
		row := db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
		iq := new(IdQuery)
		err := row.Scan(&iq.Id)

		if err != nil {
			_, err = db.Exec("insert into users (facebook_id, username, email, level, points) values (?, ?, ?, 0, 0)", uid, "", "")
			if err != nil {
				fmt.Fprint(w, "{\"status\":\"failure\"}")
				return
			} else {
				row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
				err = row.Scan(&iq.Id)
				if err != nil {
					fmt.Fprint(w, "{\"status\":\"failure\"}")
					return
				}
			}

		}

		fmt.Println("session-id: ", iq.Id)

		session, _ := store.Get(r, "session")
		session.Values["userid"] = iq.Id
		session.Save(r, w)

		fmt.Println(session.Values)

		fmt.Fprint(w, "{\"status\":\"success\"}")

	// 	if err == nil {
	// 		fmt.Fprint(w, "{\"status\":\"success\",\"uid\":", iq.Id, "}")
	// 	} else {
	// 		_, err = db.Exec("insert into users (facebook_id, username, email, level, points) values (?, ?, ?, 0, 0)", uid, "", "")
	// 		if err == nil {
	// 			row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
	// 			err = row.Scan(&iq.Id)
	// 			if err == nil {
	// 				fmt.Fprint(w, "{\"status\":\"success\"},\"uid\":", iq.Id, "}")
	// 			} else {
	// 				fmt.Fprint(w, "{\"status\":\"failure\"}")
	// 			}
	// 		} else {
	// 			fmt.Fprint(w, "{\"status\":\"failure\"}")
	// 		}
	// 	}
	// } else {
	// 	fmt.Fprint(w, "{\"status\":\"failure\"}")
	}
}

func readHttpBody(response *http.Response) string {
	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if (err != nil) {
	}
	return nil, err

}

func GetMe(token string) string {
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<commit_msg>path change<commit_after>package main

import (
	_ "time"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"encoding/json"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"runtime"
	"errors"
	"html/template"
)

var templates = template.Must(template.ParseFiles("/HackMIT2/index.html"))

const MESSAGE_QUEUE_SIZE = 10

var authKey = []byte("somesecretauth")
var store sessions.Store
var pool *Pool
var clients map[int64]*Client

var db *sql.DB
// var tv syscall.Timeval

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      int64
	client1 *Client
	client2 *Client
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		fmt.Println("match found for ", c1.id, " and ", c2.id)

		b := make([]byte, 8)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 8 {
			return
		}
		crId, _ := binary.Varint(b)

		room := &Room{crId, c1, c2}

		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	fmt.Println(session.Values)
	if userid == nil {
		return 0, errors.New("no cookie set")
	}
	return userid.(int64), nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, _ = sql.Open("mysql", "root:pass@/suitup")
	defer db.Close()

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/", mainHandle)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)

	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("/home/suitup/hackmit/HackMIT2/"))))
	http.ListenAndServe(":8080", nil)
}

type IdQuery struct {
    Id            int64      `json:"id"`

}

func mainHandle(w http.ResponseWriter, r *http.Request) {
	templates.Execute(w, "HackMIT2/index.html", nil)
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

  	fmt.Println("uid: ", uid)
	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}
	clients[uid] = client
	pool.in <- client

	fmt.Println("added ", uid, " to queue")
	chatroom := <- retChan

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":", chatroom.id, "}")
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	fmt.Fprint(w, uid)
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println(uid)

	message := r.FormValue("s")

	// message := r.PostFormValue("message")

	client := clients[uid]

	if client != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}-", message)
	} else {
		fmt.Fprint(w, "{\"status\":\"failure\"}-", message)
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	client := clients[uid]

	if client != nil {
		fmt.Println("client found")
		select {
		case message, ok := <- clients[uid].in:
			fmt.Println("message pulled from channel")
			if ok {
				fmt.Fprint(w, message)
			} else {
				fmt.Fprint(w, "")
			}
		default:
			fmt.Println("")
			fmt.Fprint(w, "")
		}

	} else {
		fmt.Println("client not found")
		fmt.Fprint(w, "")
	}
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		// row := db.QueryRow("SELECT id FROM users")
		row := db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
		iq := new(IdQuery)
		err := row.Scan(&iq.Id)

		if err != nil {
			_, err = db.Exec("insert into users (facebook_id, username, email, level, points) values (?, ?, ?, 0, 0)", uid, "", "")
			if err != nil {
				fmt.Fprint(w, "{\"status\":\"failure\"}")
				return
			} else {
				row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
				err = row.Scan(&iq.Id)
				if err != nil {
					fmt.Fprint(w, "{\"status\":\"failure\"}")
					return
				}
			}

		}

		fmt.Println("session-id: ", iq.Id)

		session, _ := store.Get(r, "session")
		session.Values["userid"] = iq.Id
		session.Save(r, w)

		fmt.Println(session.Values)

		fmt.Fprint(w, "{\"status\":\"success\"}")

	// 	if err == nil {
	// 		fmt.Fprint(w, "{\"status\":\"success\",\"uid\":", iq.Id, "}")
	// 	} else {
	// 		_, err = db.Exec("insert into users (facebook_id, username, email, level, points) values (?, ?, ?, 0, 0)", uid, "", "")
	// 		if err == nil {
	// 			row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
	// 			err = row.Scan(&iq.Id)
	// 			if err == nil {
	// 				fmt.Fprint(w, "{\"status\":\"success\"},\"uid\":", iq.Id, "}")
	// 			} else {
	// 				fmt.Fprint(w, "{\"status\":\"failure\"}")
	// 			}
	// 		} else {
	// 			fmt.Fprint(w, "{\"status\":\"failure\"}")
	// 		}
	// 	}
	// } else {
	// 	fmt.Fprint(w, "{\"status\":\"failure\"}")
	}
}

func readHttpBody(response *http.Response) string {
	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if (err != nil) {
	}
	return nil, err

}

func GetMe(token string) string {
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"log"
	"runtime"
	"sync"
	"time"

	"github.com/janelia-flyem/dvid/datastore"
	"github.com/janelia-flyem/dvid/dvid"
	"github.com/janelia-flyem/dvid/storage"
)

// Local server configuration parameters.  Should be set by platform-specific implementations.
type Config interface {
	HTTPAddress() string
	RPCAddress() string

	// Path to web client files
	WebClient() string
}

var (
	// InteractiveOpsPer5Min gives the number of interactive-level requests
	// received over the last 5 minutes.  This is useful for throttling "batch"
	// operations on a single DVID server.  Note that this metric is an lower
	// bound on the number of interactive requests over the last minute since
	// we do non-blocking reports.
	InteractiveOpsPer5Min int

	// Channel to track the # of interactive requests.
	interactiveOpsCh = make(chan bool)

	// Current tally of interactive requests.
	interactiveOpsPer5Min int

	// MaxInteractiveOpsBeforeBlock specifies the number of interactive requests
	// per minute that are allowed before batch-like computation (e.g., loading
	// of voxel volumes) is blocked.
	MaxInteractiveOpsBeforeBlock int = 3

	// ActiveHandlers is maximum number of active handlers over last second.
	ActiveHandlers int

	// Running tally of active handlers up to the last second
	curActiveHandlers int

	// MaxChunkHandlers sets the maximum number of chunk handlers (goroutines) that
	// can be multiplexed onto available cores.  (See -numcpu setting in dvid.go)
	MaxChunkHandlers = runtime.NumCPU()

	// HandlerToken is buffered channel to limit spawning of goroutines.
	// See ProcessChunk() in datatype/voxels for example.
	HandlerToken = make(chan int, MaxChunkHandlers)

	// Throttle allows server-wide throttling of operations.  This is used for voxels-based
	// compute-intensive operations on constrained servers.
	// TODO: This should be replaced with message queue mechanism for prioritized requests.
	Throttle = make(chan int, MaxThrottledOps)

	// SpawnGoroutineMutex is a global lock for compute-intense processes that want to
	// spawn goroutines that consume handler tokens.  This lets processes capture most
	// if not all available handler tokens in a FIFO basis rather than have multiple
	// concurrent requests launch a few goroutines each.
	SpawnGoroutineMutex sync.Mutex

	// Timeout in seconds for waiting to open a datastore for exclusive access.
	TimeoutSecs int

	// Keep track of the startup time for uptime.
	startupTime time.Time = time.Now()

	// Read-only mode ignores all HTTP requests but GET and HEAD
	readonly bool

	config      Config
	initialized bool
)

func init() {
	// Initialize the number of throttled ops available.
	for i := 0; i < MaxThrottledOps; i++ {
		Throttle <- 1
	}

	// Initialize the number of handler tokens available.
	for i := 0; i < MaxChunkHandlers; i++ {
		HandlerToken <- 1
	}

	// Monitor the handler token load, resetting every second.
	loadCheckTimer := time.Tick(10 * time.Millisecond)
	ticks := 0
	go func() {
		for {
			<-loadCheckTimer
			ticks = (ticks + 1) % 100
			if ticks == 0 {
				ActiveHandlers = curActiveHandlers
				curActiveHandlers = 0
			}
			numHandlers := MaxChunkHandlers - len(HandlerToken)
			if numHandlers > curActiveHandlers {
				curActiveHandlers = numHandlers
			}
		}
	}()

	// Monitor the # of interactive requests.
	go func() {
		minutesTick := time.Tick(5 * time.Minute)
		for {
			select {
			case <-interactiveOpsCh:
				interactiveOpsPer5Min++
			case <-minutesTick:
				InteractiveOpsPer5Min = interactiveOpsPer5Min
				interactiveOpsPer5Min = 0
			}
		}
	}()
}

// GotInteractiveRequest can be called to track the # of interactive requests that
// require some amount of computation.  Don't use this to track simple polling APIs.
// This routine will not block.
func GotInteractiveRequest() {
	select {
	case interactiveOpsCh <- true:
	default:
	}
}

// BlockOnInteractiveRequests will block this goroutine until the number of interactive
// requests dips below MaxInteractiveOpsBeforeBlock.
func BlockOnInteractiveRequests(caller ...string) {
	for {
		if InteractiveOpsPer5Min < MaxInteractiveOpsBeforeBlock {
			return
		}
		if len(caller) != 0 {
			dvid.Infof("Routine %q paused due to %d interactive requests/min...\n",
				caller[0], InteractiveOpsPer5Min)
		}
		time.Sleep(10 * time.Second)
	}
}

func SetReadOnly(on bool) {
	readonly = on
}

// AboutJSON returns a JSON string describing the properties of this server.
func AboutJSON() (jsonStr string, err error) {
	data := map[string]string{
		"Cores":           fmt.Sprintf("%d", dvid.NumCPU),
		"Maximum Cores":   fmt.Sprintf("%d", runtime.NumCPU()),
		"DVID datastore":  datastore.Version,
		"Storage backend": storage.EnginesAvailable(),
		"Server uptime":   time.Since(startupTime).String(),
	}
	m, err := json.Marshal(data)
	if err != nil {
		return
	}
	jsonStr = string(m)
	return
}

// Shutdown handles graceful cleanup of server functions before exiting DVID.
// This may not be so graceful if the chunk handler uses cgo since the interrupt
// may be caught during cgo execution.
func Shutdown() {
	waits := 0
	for {
		active := MaxChunkHandlers - len(HandlerToken)
		if waits >= 20 {
			log.Printf("Already waited for 20 seconds.  Continuing with shutdown...")
			break
		} else if active > 0 {
			log.Printf("Waiting for %d chunk handlers to finish...\n", active)
			waits++
		} else {
			log.Println("No chunk handlers active...")
			break
		}
		time.Sleep(1 * time.Second)
	}
	storage.Shutdown()
	dvid.BlockOnActiveCgo()
}
<commit_msg>Improve interactive op monitoring using sliding 5 min windows<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"log"
	"runtime"
	"sync"
	"time"

	"github.com/janelia-flyem/dvid/datastore"
	"github.com/janelia-flyem/dvid/dvid"
	"github.com/janelia-flyem/dvid/storage"
)

// Local server configuration parameters.  Should be set by platform-specific implementations.
type Config interface {
	HTTPAddress() string
	RPCAddress() string

	// Path to web client files
	WebClient() string
}

var (
	// InteractiveOpsPer5Min gives the number of interactive-level requests
	// received over the last 5 minutes.  This is useful for throttling "batch"
	// operations on a single DVID server.  Note that this metric is an lower
	// bound on the number of interactive requests over the last minute since
	// we do non-blocking reports.
	InteractiveOpsPer5Min int

	// Channel to track the # of interactive requests.
	interactiveOpsCh = make(chan bool)

	// Current tally of interactive requests over last 5 minutes in bins of 5 seconds,
	// where index 0 = current time bin.
	interactiveOps = make([]int, 60)

	// MaxInteractiveOpsBeforeBlock specifies the number of interactive requests
	// per minute that are allowed before batch-like computation (e.g., loading
	// of voxel volumes) is blocked.
	MaxInteractiveOpsBeforeBlock int = 3

	// ActiveHandlers is maximum number of active handlers over last second.
	ActiveHandlers int

	// Running tally of active handlers up to the last second
	curActiveHandlers int

	// MaxChunkHandlers sets the maximum number of chunk handlers (goroutines) that
	// can be multiplexed onto available cores.  (See -numcpu setting in dvid.go)
	MaxChunkHandlers = runtime.NumCPU()

	// HandlerToken is buffered channel to limit spawning of goroutines.
	// See ProcessChunk() in datatype/voxels for example.
	HandlerToken = make(chan int, MaxChunkHandlers)

	// Throttle allows server-wide throttling of operations.  This is used for voxels-based
	// compute-intensive operations on constrained servers.
	// TODO: This should be replaced with message queue mechanism for prioritized requests.
	Throttle = make(chan int, MaxThrottledOps)

	// SpawnGoroutineMutex is a global lock for compute-intense processes that want to
	// spawn goroutines that consume handler tokens.  This lets processes capture most
	// if not all available handler tokens in a FIFO basis rather than have multiple
	// concurrent requests launch a few goroutines each.
	SpawnGoroutineMutex sync.Mutex

	// Timeout in seconds for waiting to open a datastore for exclusive access.
	TimeoutSecs int

	// Keep track of the startup time for uptime.
	startupTime time.Time = time.Now()

	// Read-only mode ignores all HTTP requests but GET and HEAD
	readonly bool

	config      Config
	initialized bool
)

func init() {
	// Initialize the number of throttled ops available.
	for i := 0; i < MaxThrottledOps; i++ {
		Throttle <- 1
	}

	// Initialize the number of handler tokens available.
	for i := 0; i < MaxChunkHandlers; i++ {
		HandlerToken <- 1
	}

	// Monitor the handler token load, resetting every second.
	loadCheckTimer := time.Tick(10 * time.Millisecond)
	ticks := 0
	go func() {
		for {
			<-loadCheckTimer
			ticks = (ticks + 1) % 100
			if ticks == 0 {
				ActiveHandlers = curActiveHandlers
				curActiveHandlers = 0
			}
			numHandlers := MaxChunkHandlers - len(HandlerToken)
			if numHandlers > curActiveHandlers {
				curActiveHandlers = numHandlers
			}
		}
	}()

	// Monitor the # of interactive requests over last 5 minutes.
	go func() {
		tick := time.Tick(5 * time.Second)
		for {
			select {
			case <-interactiveOpsCh:
				interactiveOps[0]++
			case <-tick:
				newCount := InteractiveOpsPer5Min - interactiveOps[59] + interactiveOps[0]
				InteractiveOpsPer5Min = newCount
				copy(interactiveOps[1:], interactiveOps[:59])
				interactiveOps[0] = 0
			}
		}
	}()
}

// GotInteractiveRequest can be called to track the # of interactive requests that
// require some amount of computation.  Don't use this to track simple polling APIs.
// This routine will not block.
func GotInteractiveRequest() {
	select {
	case interactiveOpsCh <- true:
	default:
	}
}

// BlockOnInteractiveRequests will block this goroutine until the number of interactive
// requests dips below MaxInteractiveOpsBeforeBlock.
func BlockOnInteractiveRequests(caller ...string) {
	for {
		if InteractiveOpsPer5Min < MaxInteractiveOpsBeforeBlock {
			return
		}
		if len(caller) != 0 {
			dvid.Infof("Routine %q paused due to %d interactive requests/min...\n",
				caller[0], InteractiveOpsPer5Min)
		}
		time.Sleep(30 * time.Second)
	}
}

func SetReadOnly(on bool) {
	readonly = on
}

// AboutJSON returns a JSON string describing the properties of this server.
func AboutJSON() (jsonStr string, err error) {
	data := map[string]string{
		"Cores":           fmt.Sprintf("%d", dvid.NumCPU),
		"Maximum Cores":   fmt.Sprintf("%d", runtime.NumCPU()),
		"DVID datastore":  datastore.Version,
		"Storage backend": storage.EnginesAvailable(),
		"Server uptime":   time.Since(startupTime).String(),
	}
	m, err := json.Marshal(data)
	if err != nil {
		return
	}
	jsonStr = string(m)
	return
}

// Shutdown handles graceful cleanup of server functions before exiting DVID.
// This may not be so graceful if the chunk handler uses cgo since the interrupt
// may be caught during cgo execution.
func Shutdown() {
	waits := 0
	for {
		active := MaxChunkHandlers - len(HandlerToken)
		if waits >= 20 {
			log.Printf("Already waited for 20 seconds.  Continuing with shutdown...")
			break
		} else if active > 0 {
			log.Printf("Waiting for %d chunk handlers to finish...\n", active)
			waits++
		} else {
			log.Println("No chunk handlers active...")
			break
		}
		time.Sleep(1 * time.Second)
	}
	storage.Shutdown()
	dvid.BlockOnActiveCgo()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/daaku/go.httpgzip"
	"net/http"
)

func main() {
	panic(http.ListenAndServe(":8080", httpgzip.NewHandler(http.FileServer(http.Dir("public")))))
}
<commit_msg>ports on heroku<commit_after>package main

import (
	"fmt"
	"github.com/daaku/go.httpgzip"
	"github.com/darkhelmet/env"
	"net/http"
)

func main() {
	port := env.IntDefault("PORT", 8080)
	panic(http.ListenAndServe(fmt.Sprintf(":%d", port), httpgzip.NewHandler(http.FileServer(http.Dir("public")))))
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"os"
	"strings"
	"time"

	log "github.com/cihub/seelog"
	"github.com/golang/protobuf/proto"
	"github.com/streadway/amqp"
	"github.com/vinceprignano/bunny/rabbit"
)

type Server struct {
	Name       string
	registry   *Registry
	connection *rabbit.RabbitConnection
}

var NewServer = func(name string) *Server {
	return &Server{
		Name:       name,
		registry:   NewRegistry(),
		connection: rabbit.NewRabbitConnection(),
	}
}

func (s *Server) Init() {
	select {
	case <-s.connection.Init():
		log.Info("[Server] Connected to RabbitMQ")
	case <-time.After(10 * time.Second):
		log.Critical("[Server] Failed to connect to RabbitMQ")
		os.Exit(1)
	}
}

func (s *Server) RegisterEndpoint(endpoint Endpoint) {
	s.registry.RegisterEndpoint(endpoint)
}

func (s *Server) Run() {
	log.Infof("[Server] Listening for deliveries on %s.#", s.Name)

	deliveries, err := s.connection.Consume(s.Name)
	if err != nil {
		log.Criticalf("[Server] [%s] Failed to consume from Rabbit", s.Name)
	}

	// Range over deliveries from channel
	// This blocks until the channel closes
	for req := range deliveries {
		log.Info("[Server] [%s] Received new delivery", s.Name)
		go s.handleRequest(req)
	}

	log.Infof("Exiting")
	log.Flush()
}

func (s *Server) handleRequest(delivery amqp.Delivery) {
	endpointName := strings.Replace(delivery.RoutingKey, fmt.Sprintf("%s.", s.Name), "", -1)
	endpoint := s.registry.GetEndpoint(endpointName)
	if endpoint == nil {
		log.Error("[Server] Endpoint not found, cannot handle request")
		return
	}
	req := NewRequest(&delivery)
	rsp, err := endpoint.HandleRequest(req)
	if err != nil {
		log.Errorf("[Server] Endpoint %s returned an error", endpointName)
		log.Error(err.Error())
	}
	body, err := proto.Marshal(rsp)
	if err != nil {
		log.Errorf("[Server] Failed to marshal response")
	}
	msg := amqp.Publishing{
		CorrelationId: delivery.CorrelationId,
		Timestamp:     time.Now().UTC(),
		Body:          body,
	}
	s.connection.Publish("", delivery.ReplyTo, msg)
}
<commit_msg>Make tremendous progress<commit_after>package server

import (
	"fmt"
	"os"
	"strings"
	"time"

	log "github.com/cihub/seelog"
	"github.com/golang/protobuf/proto"
	"github.com/streadway/amqp"
	"github.com/vinceprignano/bunny/rabbit"
)

type Server struct {
	Name       string
	registry   *Registry
	connection *rabbit.RabbitConnection
}

var NewServer = func(name string) *Server {
	return &Server{
		Name:       name,
		registry:   NewRegistry(),
		connection: rabbit.NewRabbitConnection(),
	}
}

func (s *Server) Init() {
	select {
	case <-s.connection.Init():
		log.Info("[Server] Connected to RabbitMQ")
	case <-time.After(10 * time.Second):
		log.Critical("[Server] Failed to connect to RabbitMQ")
		os.Exit(1)
	}
}

func (s *Server) RegisterEndpoint(endpoint Endpoint) {
	s.registry.RegisterEndpoint(endpoint)
}

func (s *Server) Run() {
	log.Infof("[Server] Listening for deliveries on %s.#", s.Name)

	deliveries, err := s.connection.Consume(s.Name)
	if err != nil {
		log.Criticalf("[Server] [%s] Failed to consume from Rabbit", s.Name)
	}

	// Range over deliveries from channel
	// This blocks until the channel closes
	for req := range deliveries {
		log.Infof("[Server] [%s] Received new delivery", s.Name)
		go s.handleRequest(req)
	}

	log.Infof("Exiting")
	log.Flush()
}

func (s *Server) handleRequest(delivery amqp.Delivery) {
	endpointName := strings.Replace(delivery.RoutingKey, fmt.Sprintf("%s.", s.Name), "", -1)
	endpoint := s.registry.GetEndpoint(endpointName)
	if endpoint == nil {
		log.Error("[Server] Endpoint not found, cannot handle request")
		return
	}
	req := NewRequest(&delivery)
	rsp, err := endpoint.HandleRequest(req)
	if err != nil {
		log.Errorf("[Server] Endpoint %s returned an error", endpointName)
		log.Error(err.Error())
	}
	body, err := proto.Marshal(rsp)
	if err != nil {
		log.Errorf("[Server] Failed to marshal response")
	}
	msg := amqp.Publishing{
		CorrelationId: delivery.CorrelationId,
		Timestamp:     time.Now().UTC(),
		Body:          body,
	}
	s.connection.Publish("", delivery.ReplyTo, msg)
}
<|endoftext|>


<commit_before>// Copyright 2019 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package server implements an environment for running LUCI servers.
package server

import (
	"context"
	"flag"
	"fmt"
	"net"
	"net/http"
	"sync"
	"time"

	"go.chromium.org/luci/common/data/caching/cacheContext"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/logging/gologger"
	"go.chromium.org/luci/common/system/signals"

	"go.chromium.org/luci/server/caching"
	"go.chromium.org/luci/server/middleware"
	"go.chromium.org/luci/server/router"
)

// Options are exposed as command line flags.
type Options struct {
	Prod      bool   // must be set when running in production
	HTTPAddr  string // address to bind the main listening socket to
	AdminAddr string // address to bind the admin socket to

	testListeners map[string]net.Listener // addr => net.Listener, for tests
}

// Register registers the command line flags.
func (o *Options) Register(f *flag.FlagSet) {
	if o.HTTPAddr == "" {
		o.HTTPAddr = "127.0.0.1:8800"
	}
	if o.AdminAddr == "" {
		o.AdminAddr = "127.0.0.1:8900"
	}
	f.BoolVar(&o.Prod, "prod", o.Prod, "Switch the server into production mode")
	f.StringVar(&o.HTTPAddr, "http-addr", o.HTTPAddr, "Address to bind the main listening socket to")
	f.StringVar(&o.AdminAddr, "admin-addr", o.AdminAddr, "Address to bind the admin socket to")
}

// Server is responsible for initializing and launching the serving environment.
//
// Doesn't do TLS. Should be sitting behind a load balancer that terminates
// TLS.
type Server struct {
	Routes *router.Router // HTTP routes exposed via opts.HTTPAddr

	ctx     context.Context // the root server context, holds all global state
	opts    Options         // options passed to New
	m       sync.Mutex      // protects fields below
	httpSrv []*http.Server  // all registered HTTP servers
	started bool            // true inside and after ListenAndServe
	stopped bool            // true inside and after Shutdown
	done    chan struct{}   // closed after Shutdown returns
}

// New constructs a new server instance.
//
// It hosts one or more HTTP servers and starts and stops them in unison. It is
// also responsible for preparing contexts for incoming requests.
func New(opts Options) *Server {
	// TODO(vadimsh): Use JSON format when opts.Prod is true so that fluentd can
	// understand it.
	ctx := gologger.StdConfig.Use(context.Background())
	ctx = caching.WithProcessCacheData(ctx, caching.NewProcessCacheData())

	// TODO(vadimsh): Install settings via settings.Use(...).
	// TODO(vadimsh): Install secrets via secrets.Set(...).
	// TODO(vadimsh): Install auth via auth.Initialize(...).

	srv := &Server{
		ctx:  ctx,
		opts: opts,
		done: make(chan struct{}),
	}
	srv.Routes = srv.RegisterHTTP(opts.HTTPAddr)

	// TODO(vadimsh): Populate admin routes (admin portal, pprof).
	srv.RegisterHTTP(opts.AdminAddr)

	return srv
}

// RegisterHTTP prepares an additional HTTP server.
//
// Can be used to open more listening HTTP ports (in addition to opts.HTTPAddr
// and opts.AdminAddr). Returns a router that should be populated with routes
// exposed through the added server.
//
// Should be called before ListenAndServe (panics otherwise).
func (s *Server) RegisterHTTP(addr string) *router.Router {
	s.m.Lock()
	defer s.m.Unlock()
	if s.started {
		panic("the server has already been started")
	}

	// Setup middleware chain used by ALL requests.
	r := router.New()
	r.Use(router.NewMiddlewareChain(
		s.rootMiddleware,            // prepares the per-request context
		middleware.WithPanicCatcher, // transforms panics into HTTP 500
	))

	s.httpSrv = append(s.httpSrv, &http.Server{
		Addr:     addr,
		Handler:  r,
		ErrorLog: nil, // TODO(vadimsh): Log via 'logging' package.
	})
	return r
}

// ListenAndServe launches the serving loop.
//
// Blocks forever or until the server is stopped via Shutdown (from another
// goroutine or from a SIGTERM handler). Returns nil if the server was shutdown
// correctly or an error if it failed to start or unexpectedly died. The error
// is logged inside.
//
// Should be called only once. Panics otherwise.
func (s *Server) ListenAndServe() error {
	s.m.Lock()
	wasRunning := s.started
	httpSrv := append(make([]*http.Server, 0, len(s.httpSrv)), s.httpSrv...)
	s.started = true
	s.m.Unlock()
	if wasRunning {
		panic("the server has already been started")
	}

	// Catch SIGTERM while inside this function.
	stop := signals.HandleInterrupt(s.Shutdown)
	defer stop()

	// Run serving loops in parallel.
	errs := make(errors.MultiError, len(httpSrv))
	wg := sync.WaitGroup{}
	wg.Add(len(httpSrv))
	for i, srv := range httpSrv {
		logging.Infof(s.ctx, "Serving http://%s", srv.Addr)
		i := i
		srv := srv
		go func() {
			defer wg.Done()
			if err := s.serveLoop(srv); err != http.ErrServerClosed {
				logging.WithError(err).Errorf(s.ctx, "Server at %s failed", srv.Addr)
				errs[i] = err
				s.Shutdown() // close all other servers
			}
		}()
	}
	wg.Wait()

	// Per http.Server docs, we end up here *immediately* after Shutdown call was
	// initiated. Some requests can still be in-flight. We block until they are
	// done (as indicated by Shutdown call itself exiting).
	logging.Infof(s.ctx, "Waiting for the server to stop...")
	<-s.done
	logging.Infof(s.ctx, "The server has stopped")

	if errs.First() != nil {
		return errs
	}
	return nil
}

// Shutdown gracefully stops the server if it was running.
//
// Blocks until the server is stopped. Can be called multiple times.
func (s *Server) Shutdown() {
	s.m.Lock()
	defer s.m.Unlock()
	if s.stopped {
		return
	}

	logging.Infof(s.ctx, "Shutting down the server...")

	// Stop them all in parallel. Each Shutdown call blocks until the
	// corresponding server is stopped.
	wg := sync.WaitGroup{}
	wg.Add(len(s.httpSrv))
	for _, srv := range s.httpSrv {
		srv := srv
		go func() {
			defer wg.Done()
			srv.Shutdown(s.ctx)
		}()
	}
	wg.Wait()

	// Notify ListenAndServe that it can exit now.
	s.stopped = true
	close(s.done)
}

// serveLoop binds the socket and launches the serving loop.
//
// Basically srv.ListenAndServe with some testing helpers.
func (s *Server) serveLoop(srv *http.Server) error {
	// If not running tests, let http.Server bind the socket as usual.
	if s.opts.testListeners == nil {
		return srv.ListenAndServe()
	}
	// In test mode the listener MUST be prepared already.
	if l, _ := s.opts.testListeners[srv.Addr]; l != nil {
		return srv.Serve(l)
	}
	return fmt.Errorf("test listener is not set")
}

// rootMiddleware prepares the per-request context.
func (s *Server) rootMiddleware(c *router.Context, next router.Handler) {
	ctx, cancel := context.WithTimeout(s.ctx, time.Minute)
	defer cancel()

	// TODO(vadimsh): Configure pre-request logger.
	ctx = caching.WithRequestCache(ctx)

	c.Context = cacheContext.Wrap(ctx)
	next(c)
}
<commit_msg>[server] Add helpers for running background goroutines.<commit_after>// Copyright 2019 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package server implements an environment for running LUCI servers.
package server

import (
	"context"
	"flag"
	"fmt"
	"net"
	"net/http"
	"sync"
	"time"

	"go.chromium.org/luci/common/data/caching/cacheContext"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/logging/gologger"
	"go.chromium.org/luci/common/system/signals"

	"go.chromium.org/luci/server/caching"
	"go.chromium.org/luci/server/middleware"
	"go.chromium.org/luci/server/router"
)

// Options are exposed as command line flags.
type Options struct {
	Prod      bool   // must be set when running in production
	HTTPAddr  string // address to bind the main listening socket to
	AdminAddr string // address to bind the admin socket to

	testListeners map[string]net.Listener // addr => net.Listener, for tests
}

// Register registers the command line flags.
func (o *Options) Register(f *flag.FlagSet) {
	if o.HTTPAddr == "" {
		o.HTTPAddr = "127.0.0.1:8800"
	}
	if o.AdminAddr == "" {
		o.AdminAddr = "127.0.0.1:8900"
	}
	f.BoolVar(&o.Prod, "prod", o.Prod, "Switch the server into production mode")
	f.StringVar(&o.HTTPAddr, "http-addr", o.HTTPAddr, "Address to bind the main listening socket to")
	f.StringVar(&o.AdminAddr, "admin-addr", o.AdminAddr, "Address to bind the admin socket to")
}

// Server is responsible for initializing and launching the serving environment.
//
// Doesn't do TLS. Should be sitting behind a load balancer that terminates
// TLS.
type Server struct {
	Routes *router.Router // HTTP routes exposed via opts.HTTPAddr

	ctx     context.Context // the root server context, holds all global state
	opts    Options         // options passed to New
	m       sync.Mutex      // protects fields below
	httpSrv []*http.Server  // all registered HTTP servers
	started bool            // true inside and after ListenAndServe
	stopped bool            // true inside and after Shutdown
	done    chan struct{}   // closed after Shutdown returns

	bgrCtx    context.Context    // root context for background work, canceled in Shutdown
	bgrCancel context.CancelFunc // cancels bgrCtx
	bgrWg     sync.WaitGroup     // waits for runInBackground goroutines to stop
}

// New constructs a new server instance.
//
// It hosts one or more HTTP servers and starts and stops them in unison. It is
// also responsible for preparing contexts for incoming requests.
func New(opts Options) *Server {
	// TODO(vadimsh): Use JSON format when opts.Prod is true so that fluentd can
	// understand it.
	ctx := gologger.StdConfig.Use(context.Background())
	ctx = caching.WithProcessCacheData(ctx, caching.NewProcessCacheData())

	// TODO(vadimsh): Install settings via settings.Use(...).
	// TODO(vadimsh): Install secrets via secrets.Set(...).
	// TODO(vadimsh): Install auth via auth.Initialize(...).

	srv := &Server{
		ctx:  ctx,
		opts: opts,
		done: make(chan struct{}),
	}
	srv.Routes = srv.RegisterHTTP(opts.HTTPAddr)

	// TODO(vadimsh): Populate admin routes (admin portal, pprof).
	srv.RegisterHTTP(opts.AdminAddr)

	// Prepare the context used for background work. It is canceled as soon as we
	// enter the shutdown sequence.
	srv.bgrCtx, srv.bgrCancel = context.WithCancel(srv.ctx)

	return srv
}

// RegisterHTTP prepares an additional HTTP server.
//
// Can be used to open more listening HTTP ports (in addition to opts.HTTPAddr
// and opts.AdminAddr). Returns a router that should be populated with routes
// exposed through the added server.
//
// Should be called before ListenAndServe (panics otherwise).
func (s *Server) RegisterHTTP(addr string) *router.Router {
	s.m.Lock()
	defer s.m.Unlock()
	if s.started {
		panic("the server has already been started")
	}

	// Setup middleware chain used by ALL requests.
	r := router.New()
	r.Use(router.NewMiddlewareChain(
		s.rootMiddleware,            // prepares the per-request context
		middleware.WithPanicCatcher, // transforms panics into HTTP 500
	))

	s.httpSrv = append(s.httpSrv, &http.Server{
		Addr:     addr,
		Handler:  r,
		ErrorLog: nil, // TODO(vadimsh): Log via 'logging' package.
	})
	return r
}

// ListenAndServe launches the serving loop.
//
// Blocks forever or until the server is stopped via Shutdown (from another
// goroutine or from a SIGTERM handler). Returns nil if the server was shutdown
// correctly or an error if it failed to start or unexpectedly died. The error
// is logged inside.
//
// Should be called only once. Panics otherwise.
func (s *Server) ListenAndServe() error {
	s.m.Lock()
	wasRunning := s.started
	httpSrv := append(make([]*http.Server, 0, len(s.httpSrv)), s.httpSrv...)
	s.started = true
	s.m.Unlock()
	if wasRunning {
		panic("the server has already been started")
	}

	// Catch SIGTERM while inside this function.
	stop := signals.HandleInterrupt(s.Shutdown)
	defer stop()

	// Run serving loops in parallel.
	errs := make(errors.MultiError, len(httpSrv))
	wg := sync.WaitGroup{}
	wg.Add(len(httpSrv))
	for i, srv := range httpSrv {
		logging.Infof(s.ctx, "Serving http://%s", srv.Addr)
		i := i
		srv := srv
		go func() {
			defer wg.Done()
			if err := s.serveLoop(srv); err != http.ErrServerClosed {
				logging.WithError(err).Errorf(s.ctx, "Server at %s failed", srv.Addr)
				errs[i] = err
				s.Shutdown() // close all other servers
			}
		}()
	}
	wg.Wait()

	// Per http.Server docs, we end up here *immediately* after Shutdown call was
	// initiated. Some requests can still be in-flight. We block until they are
	// done (as indicated by Shutdown call itself exiting).
	logging.Infof(s.ctx, "Waiting for the server to stop...")
	<-s.done
	logging.Infof(s.ctx, "The server has stopped")

	if errs.First() != nil {
		return errs
	}
	return nil
}

// Shutdown gracefully stops the server if it was running.
//
// Blocks until the server is stopped. Can be called multiple times.
func (s *Server) Shutdown() {
	s.m.Lock()
	defer s.m.Unlock()
	if s.stopped {
		return
	}

	logging.Infof(s.ctx, "Shutting down the server...")

	// Tell all runInBackground goroutines to stop.
	s.bgrCancel()

	// Stop all http.Servers in parallel. Each Shutdown call blocks until the
	// corresponding server is stopped.
	wg := sync.WaitGroup{}
	wg.Add(len(s.httpSrv))
	for _, srv := range s.httpSrv {
		srv := srv
		go func() {
			defer wg.Done()
			srv.Shutdown(s.ctx)
		}()
	}
	wg.Wait()

	// Wait for all background goroutines to stop.
	s.bgrWg.Wait()

	// Notify ListenAndServe that it can exit now.
	s.stopped = true
	close(s.done)
}

// serveLoop binds the socket and launches the serving loop.
//
// Basically srv.ListenAndServe with some testing helpers.
func (s *Server) serveLoop(srv *http.Server) error {
	// If not running tests, let http.Server bind the socket as usual.
	if s.opts.testListeners == nil {
		return srv.ListenAndServe()
	}
	// In test mode the listener MUST be prepared already.
	if l, _ := s.opts.testListeners[srv.Addr]; l != nil {
		return srv.Serve(l)
	}
	return fmt.Errorf("test listener is not set")
}

// runInBackground starts a goroutine that does some background work.
//
// It is expected to exit soon after its context is canceled.
func (s *Server) runInBackground(activity string, f func(context.Context)) {
	ctx := logging.SetField(s.bgrCtx, "activity", activity)
	s.bgrWg.Add(1)
	go func() {
		defer s.bgrWg.Done()
		f(ctx)
	}()
}

// rootMiddleware prepares the per-request context.
func (s *Server) rootMiddleware(c *router.Context, next router.Handler) {
	ctx, cancel := context.WithTimeout(s.ctx, time.Minute)
	defer cancel()

	// TODO(vadimsh): Configure pre-request logger.
	ctx = caching.WithRequestCache(ctx)

	c.Context = cacheContext.Wrap(ctx)
	next(c)
}
<|endoftext|>


<commit_before>package server

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"

	"bazil.org/bazil/cas/chunks/kvchunks"
	"bazil.org/bazil/db"
	"bazil.org/bazil/fs"
	"bazil.org/bazil/kv"
	"bazil.org/bazil/kv/kvfiles"
	"bazil.org/bazil/kv/kvmulti"
	"bazil.org/bazil/kv/kvpeer"
	"bazil.org/bazil/kv/untrusted"
	"bazil.org/bazil/peer"
	"bazil.org/bazil/tokens"
	"bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
	"github.com/boltdb/bolt"
)

type mountState struct {
	// closed after the serve loop has exited
	unmounted chan struct{}
}

type App struct {
	DataDir  string
	lockFile *os.File
	DB       *db.DB
	mounts   struct {
		sync.Mutex
		open map[db.VolumeID]*mountState
	}
	Keys *CryptoKeys
	tls  struct {
		config atomic.Value
		gen    sync.Mutex
	}
}

func New(dataDir string) (app *App, err error) {
	err = os.Mkdir(dataDir, 0700)
	if err != nil && !os.IsExist(err) {
		return nil, err
	}

	lockPath := filepath.Join(dataDir, "lock")
	lockFile, err := lock(lockPath)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			// if we're reporting an error, also unlock
			_ = lockFile.Close()
		}
	}()

	kvpath := filepath.Join(dataDir, "chunks")
	err = kvfiles.Create(kvpath)
	if err != nil {
		return nil, err
	}

	dbpath := filepath.Join(dataDir, "bazil.bolt")
	database, err := db.Open(dbpath, 0600, nil)
	if err != nil {
		return nil, err
	}

	err = database.DB.Update(func(tx *bolt.Tx) error {
		if _, err := tx.CreateBucketIfNotExists([]byte(tokens.BucketBazil)); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		database.Close()
		return nil, err
	}

	keys, err := loadOrGenerateKeys(database.DB)
	if err != nil {
		return nil, err
	}

	app = &App{
		DataDir:  dataDir,
		lockFile: lockFile,
		DB:       database,
		Keys:     keys,
	}
	app.mounts.open = make(map[db.VolumeID]*mountState)
	return app, nil
}

func (app *App) Close() {
	app.DB.Close()
	app.lockFile.Close()
}

// TODO this function smells
func (app *App) serveMount(vol *fs.Volume, id *db.VolumeID, mountpoint string, conn *fuse.Conn, srv *fusefs.Server) error {
	serveErr := make(chan error, 1)
	go func() {
		defer func() {
			// remove map entry on unmount or failed mount
			app.mounts.Lock()
			delete(app.mounts.open, *id)
			app.mounts.Unlock()
		}()
		defer conn.Close()
		serveErr <- srv.Serve(vol)
	}()

	select {
	case <-conn.Ready:
		if err := conn.MountError; err != nil {
			return fmt.Errorf("mount fail (delayed): %v", err)
		}
		return nil
	case err := <-serveErr:
		// Serve quit early
		if err != nil {
			return fmt.Errorf("filesystem failure: %v", err)
		}
		return errors.New("Serve exited early")
	}
}

type MountInfo struct {
	VolumeID db.VolumeID
}

func (app *App) openKV(tx *db.Tx, storage *db.VolumeStorage) (kv.KV, error) {
	var kvstores []kv.KV

	c := storage.Cursor()
	for item := c.First(); item != nil; item = c.Next() {
		backend, err := item.Backend()
		if err != nil {
			return nil, err
		}
		s, err := app.openStorage(backend)
		if err != nil {
			return nil, err
		}

		sharingKeyName, err := item.SharingKeyName()
		if err != nil {
			return nil, err
		}
		sharingKey, err := tx.SharingKeys().Get(sharingKeyName)
		if err != nil {
			return nil, fmt.Errorf("getting sharing key %q: %v", sharingKeyName, err)
		}
		var secret [32]byte
		sharingKey.Secret(&secret)
		s = untrusted.New(s, &secret)

		kvstores = append(kvstores, s)
	}

	return kvmulti.New(kvstores...), nil
}

func (app *App) openStorage(backend string) (kv.KV, error) {
	switch backend {
	case "local":
		kvpath := filepath.Join(app.DataDir, "chunks")
		return kvfiles.Open(kvpath)
	}
	if backend != "" && backend[0] == '/' {
		return kvfiles.Open(backend)
	}
	idx := strings.IndexByte(backend, ':')
	if idx != -1 {
		scheme, rest := backend[:idx], backend[idx+1:]
		switch scheme {
		case "peerkey":
			var key peer.PublicKey
			if err := key.Set(rest); err != nil {
				return nil, err
			}
			p, err := app.DialPeer(&key)
			if err != nil {
				return nil, err
			}
			// TODO Close
			return kvpeer.Open(p)
		}
	}
	return nil, errors.New("unknown storage backend")
}

func (app *App) ValidateKV(backend string) error {
	// TODO opening a KV just to validate the input string would be a
	// bad idea if a backend was costly to open; then again, current
	// API doesn't support a Close anyway.
	_, err := app.openStorage(backend)
	return err
}

// Mount makes the contents of the volume visible at the given
// mountpoint. If Mount returns with a nil error, the mount has
// occurred.
func (app *App) Mount(volumeName string, mountpoint string) (*MountInfo, error) {
	// TODO obey `bazil -debug server run`

	info := &MountInfo{}
	var ready = make(chan error, 1)
	app.mounts.Lock()
	err := app.DB.View(func(tx *db.Tx) error {
		v, err := tx.Volumes().GetByName(volumeName)
		if err != nil {
			return err
		}
		v.VolumeID(&info.VolumeID)

		if _, ok := app.mounts.open[info.VolumeID]; ok {
			return errors.New("volume already mounted")
		}

		conn, err := fuse.Mount(mountpoint,
			fuse.MaxReadahead(32*1024*1024),
			fuse.AsyncRead(),
		)
		if err != nil {
			return fmt.Errorf("mount fail: %v", err)
		}

		srv := fusefs.New(conn, nil)

		kvstore, err := app.openKV(tx, v.Storage())
		if err != nil {
			return err
		}

		chunkStore := kvchunks.New(kvstore)
		vol, err := fs.Open(app.DB, chunkStore, &info.VolumeID, (*peer.PublicKey)(app.Keys.Sign.Pub))
		if err != nil {
			return err
		}
		mnt := &mountState{
			unmounted: make(chan struct{}),
		}
		go func() {
			defer close(mnt.unmounted)
			ready <- app.serveMount(vol, &info.VolumeID, mountpoint, conn, srv)
		}()
		app.mounts.open[info.VolumeID] = mnt
		return nil
	})
	app.mounts.Unlock()
	if err != nil {
		return nil, err
	}
	err = <-ready
	if err != nil {
		return nil, err
	}
	return info, nil
}

var ErrNotMounted = errors.New("not currently mounted")

func (app *App) WaitForUnmount(volumeID *db.VolumeID) error {
	app.mounts.Lock()
	// we hold onto mnt after releasing the lock, but it's safe in
	// this case; gc keeps it pinned, and we don't look at mutable
	// data
	mnt, ok := app.mounts.open[*volumeID]
	app.mounts.Unlock()
	if !ok {
		return ErrNotMounted
	}
	<-mnt.unmounted
	return nil
}
<commit_msg>server: Rename mountState to VolumeRef<commit_after>package server

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"

	"bazil.org/bazil/cas/chunks/kvchunks"
	"bazil.org/bazil/db"
	"bazil.org/bazil/fs"
	"bazil.org/bazil/kv"
	"bazil.org/bazil/kv/kvfiles"
	"bazil.org/bazil/kv/kvmulti"
	"bazil.org/bazil/kv/kvpeer"
	"bazil.org/bazil/kv/untrusted"
	"bazil.org/bazil/peer"
	"bazil.org/bazil/tokens"
	"bazil.org/fuse"
	fusefs "bazil.org/fuse/fs"
	"github.com/boltdb/bolt"
)

type VolumeRef struct {
	// closed after the serve loop has exited
	unmounted chan struct{}
}

type App struct {
	DataDir  string
	lockFile *os.File
	DB       *db.DB
	volumes  struct {
		sync.Mutex
		open map[db.VolumeID]*VolumeRef
	}
	Keys *CryptoKeys
	tls  struct {
		config atomic.Value
		gen    sync.Mutex
	}
}

func New(dataDir string) (app *App, err error) {
	err = os.Mkdir(dataDir, 0700)
	if err != nil && !os.IsExist(err) {
		return nil, err
	}

	lockPath := filepath.Join(dataDir, "lock")
	lockFile, err := lock(lockPath)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			// if we're reporting an error, also unlock
			_ = lockFile.Close()
		}
	}()

	kvpath := filepath.Join(dataDir, "chunks")
	err = kvfiles.Create(kvpath)
	if err != nil {
		return nil, err
	}

	dbpath := filepath.Join(dataDir, "bazil.bolt")
	database, err := db.Open(dbpath, 0600, nil)
	if err != nil {
		return nil, err
	}

	err = database.DB.Update(func(tx *bolt.Tx) error {
		if _, err := tx.CreateBucketIfNotExists([]byte(tokens.BucketBazil)); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		database.Close()
		return nil, err
	}

	keys, err := loadOrGenerateKeys(database.DB)
	if err != nil {
		return nil, err
	}

	app = &App{
		DataDir:  dataDir,
		lockFile: lockFile,
		DB:       database,
		Keys:     keys,
	}
	app.volumes.open = make(map[db.VolumeID]*VolumeRef)
	return app, nil
}

func (app *App) Close() {
	app.DB.Close()
	app.lockFile.Close()
}

// TODO this function smells
func (app *App) serveMount(vol *fs.Volume, id *db.VolumeID, mountpoint string, conn *fuse.Conn, srv *fusefs.Server) error {
	serveErr := make(chan error, 1)
	go func() {
		defer func() {
			// remove map entry on unmount or failed mount
			app.volumes.Lock()
			delete(app.volumes.open, *id)
			app.volumes.Unlock()
		}()
		defer conn.Close()
		serveErr <- srv.Serve(vol)
	}()

	select {
	case <-conn.Ready:
		if err := conn.MountError; err != nil {
			return fmt.Errorf("mount fail (delayed): %v", err)
		}
		return nil
	case err := <-serveErr:
		// Serve quit early
		if err != nil {
			return fmt.Errorf("filesystem failure: %v", err)
		}
		return errors.New("Serve exited early")
	}
}

type MountInfo struct {
	VolumeID db.VolumeID
}

func (app *App) openKV(tx *db.Tx, storage *db.VolumeStorage) (kv.KV, error) {
	var kvstores []kv.KV

	c := storage.Cursor()
	for item := c.First(); item != nil; item = c.Next() {
		backend, err := item.Backend()
		if err != nil {
			return nil, err
		}
		s, err := app.openStorage(backend)
		if err != nil {
			return nil, err
		}

		sharingKeyName, err := item.SharingKeyName()
		if err != nil {
			return nil, err
		}
		sharingKey, err := tx.SharingKeys().Get(sharingKeyName)
		if err != nil {
			return nil, fmt.Errorf("getting sharing key %q: %v", sharingKeyName, err)
		}
		var secret [32]byte
		sharingKey.Secret(&secret)
		s = untrusted.New(s, &secret)

		kvstores = append(kvstores, s)
	}

	return kvmulti.New(kvstores...), nil
}

func (app *App) openStorage(backend string) (kv.KV, error) {
	switch backend {
	case "local":
		kvpath := filepath.Join(app.DataDir, "chunks")
		return kvfiles.Open(kvpath)
	}
	if backend != "" && backend[0] == '/' {
		return kvfiles.Open(backend)
	}
	idx := strings.IndexByte(backend, ':')
	if idx != -1 {
		scheme, rest := backend[:idx], backend[idx+1:]
		switch scheme {
		case "peerkey":
			var key peer.PublicKey
			if err := key.Set(rest); err != nil {
				return nil, err
			}
			p, err := app.DialPeer(&key)
			if err != nil {
				return nil, err
			}
			// TODO Close
			return kvpeer.Open(p)
		}
	}
	return nil, errors.New("unknown storage backend")
}

func (app *App) ValidateKV(backend string) error {
	// TODO opening a KV just to validate the input string would be a
	// bad idea if a backend was costly to open; then again, current
	// API doesn't support a Close anyway.
	_, err := app.openStorage(backend)
	return err
}

// Mount makes the contents of the volume visible at the given
// mountpoint. If Mount returns with a nil error, the mount has
// occurred.
func (app *App) Mount(volumeName string, mountpoint string) (*MountInfo, error) {
	// TODO obey `bazil -debug server run`

	info := &MountInfo{}
	var ready = make(chan error, 1)
	app.volumes.Lock()
	err := app.DB.View(func(tx *db.Tx) error {
		v, err := tx.Volumes().GetByName(volumeName)
		if err != nil {
			return err
		}
		v.VolumeID(&info.VolumeID)

		if _, ok := app.volumes.open[info.VolumeID]; ok {
			return errors.New("volume already mounted")
		}

		conn, err := fuse.Mount(mountpoint,
			fuse.MaxReadahead(32*1024*1024),
			fuse.AsyncRead(),
		)
		if err != nil {
			return fmt.Errorf("mount fail: %v", err)
		}

		srv := fusefs.New(conn, nil)

		kvstore, err := app.openKV(tx, v.Storage())
		if err != nil {
			return err
		}

		chunkStore := kvchunks.New(kvstore)
		vol, err := fs.Open(app.DB, chunkStore, &info.VolumeID, (*peer.PublicKey)(app.Keys.Sign.Pub))
		if err != nil {
			return err
		}
		mnt := &VolumeRef{
			unmounted: make(chan struct{}),
		}
		go func() {
			defer close(mnt.unmounted)
			ready <- app.serveMount(vol, &info.VolumeID, mountpoint, conn, srv)
		}()
		app.volumes.open[info.VolumeID] = mnt
		return nil
	})
	app.volumes.Unlock()
	if err != nil {
		return nil, err
	}
	err = <-ready
	if err != nil {
		return nil, err
	}
	return info, nil
}

var ErrNotMounted = errors.New("not currently mounted")

func (app *App) WaitForUnmount(volumeID *db.VolumeID) error {
	app.volumes.Lock()
	// we hold onto mnt after releasing the lock, but it's safe in
	// this case; gc keeps it pinned, and we don't look at mutable
	// data
	mnt, ok := app.volumes.open[*volumeID]
	app.volumes.Unlock()
	if !ok {
		return ErrNotMounted
	}
	<-mnt.unmounted
	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net/http"
	"strconv"

	netContext "context"

	"github.com/getsentry/raven-go"
	"github.com/gin-gonic/contrib/cors"
	"github.com/gin-gonic/contrib/sentry"
	"github.com/gin-gonic/gin"
	"github.com/thoas/picfit/application"
	"github.com/thoas/picfit/config"
	"github.com/thoas/picfit/engine"
	"github.com/thoas/picfit/kvstore"
	"github.com/thoas/picfit/logger"
	"github.com/thoas/picfit/middleware"
	"github.com/thoas/picfit/middleware/context"
	"github.com/thoas/picfit/storage"
	"github.com/thoas/picfit/views"
	"github.com/thoas/stats"
)

// Load loads the application and launch the webserver
func Load(path string) error {
	ctx, err := application.Load(path)

	if err != nil {
		return err
	}

	return Run(ctx)
}

// Router returns a gin Engine
func Router(ctx netContext.Context) (*gin.Engine, error) {
	router := gin.New()
	router.Use(gin.Recovery())

	cfg := config.FromContext(ctx)

	if cfg.Debug {
		router.Use(gin.Logger())
	}

	methods := map[string]gin.HandlerFunc{
		"redirect": views.RedirectView,
		"display":  views.DisplayView,
		"get":      views.GetView,
	}

	kv := kvstore.FromContext(ctx)

	if cfg.Sentry != nil {
		client, err := raven.NewClient(cfg.Sentry.DSN, cfg.Sentry.Tags)

		if err != nil {
			return nil, err
		}

		router.Use(sentry.Recovery(client, true))
	}

	router.Use(context.SetLogger(logger.FromContext(ctx)))
	router.Use(context.SetKVStore(kv))
	router.Use(context.SetSourceStorage(storage.SourceFromContext(ctx)))
	router.Use(context.SetDestinationStorage(storage.DestinationFromContext(ctx)))
	router.Use(context.SetEngine(engine.FromContext(ctx)))
	router.Use(context.SetConfig(cfg))

	if cfg.AllowedOrigins != nil && cfg.AllowedMethods != nil {
		allowedOrigins := cfg.AllowedOrigins

		allowAllOrigins := false

		if len(allowedOrigins) == 1 {
			if allowedOrigins[0] == "*" {
				allowAllOrigins = true
			}
		}

		if allowAllOrigins {
			allowedOrigins = nil
		}

		router.Use(cors.New(cors.Config{
			AllowAllOrigins: allowAllOrigins,
			AllowedOrigins:  allowedOrigins,
			AllowedMethods:  cfg.AllowedMethods,
			AllowedHeaders:  cfg.AllowedHeaders,
		}))
	}

	router.GET("/healthcheck", views.HealthcheckView)

	if cfg.Options.EnableStats {
		s := stats.New()

		router.Use(func() gin.HandlerFunc {
			return func(c *gin.Context) {
				beginning, recorder := s.Begin(c.Writer)
				c.Next()
				s.End(beginning, recorder)
			}
		}())

		router.GET("/stats", func(c *gin.Context) {
			c.JSON(http.StatusOK, s.Data())
		})
	}

	for name, view := range methods {
		views := []gin.HandlerFunc{
			middleware.ParametersParser(),
			middleware.KeyParser(),
			middleware.Security(),
			middleware.URLParser(),
			middleware.OperationParser(),
			middleware.RestrictSizes(),
			view,
		}

		router.GET(fmt.Sprintf("/%s", name), views...)

		if cfg.Storage != nil && cfg.Storage.Src != nil {
			router.GET(fmt.Sprintf("/%s/*parameters", name), views...)
		}
	}

	if cfg.Options.EnableUpload {
		router.POST("/upload", views.UploadView)
	}

	if cfg.Options.EnableDelete {
		router.DELETE("/*path", views.DeleteView)
	}

	return router, nil
}

// Run loads a new server
func Run(ctx netContext.Context) error {
	engine, err := Router(ctx)

	if err != nil {
		return err
	}

	engine.Run(fmt.Sprintf(":%s", strconv.Itoa(config.FromContext(ctx).Port)))

	return nil
}
<commit_msg>Only activate gin recovery on debug, use debug logger<commit_after>package server

import (
	"fmt"
	"net/http"
	"strconv"

	netContext "context"

	"github.com/getsentry/raven-go"
	"github.com/gin-gonic/contrib/cors"
	"github.com/gin-gonic/contrib/sentry"
	"github.com/gin-gonic/gin"
	"github.com/thoas/picfit/application"
	"github.com/thoas/picfit/config"
	"github.com/thoas/picfit/engine"
	"github.com/thoas/picfit/kvstore"
	"github.com/thoas/picfit/logger"
	"github.com/thoas/picfit/middleware"
	"github.com/thoas/picfit/middleware/context"
	"github.com/thoas/picfit/storage"
	"github.com/thoas/picfit/views"
	"github.com/thoas/stats"
)

// Load loads the application and launch the webserver
func Load(path string) error {
	ctx, err := application.Load(path)

	if err != nil {
		return err
	}

	return Run(ctx)
}

// Router returns a gin Engine
func Router(ctx netContext.Context) (*gin.Engine, error) {
	router := gin.New()

	cfg := config.FromContext(ctx)

	if cfg.Debug {
		router.Use(gin.Recovery())
	}

	if cfg.Logger.GetLevel() == "debug" {
		router.Use(gin.Logger())
	}

	methods := map[string]gin.HandlerFunc{
		"redirect": views.RedirectView,
		"display":  views.DisplayView,
		"get":      views.GetView,
	}

	kv := kvstore.FromContext(ctx)

	if cfg.Sentry != nil {
		client, err := raven.NewClient(cfg.Sentry.DSN, cfg.Sentry.Tags)

		if err != nil {
			return nil, err
		}

		router.Use(sentry.Recovery(client, true))
	}

	router.Use(context.SetLogger(logger.FromContext(ctx)))
	router.Use(context.SetKVStore(kv))
	router.Use(context.SetSourceStorage(storage.SourceFromContext(ctx)))
	router.Use(context.SetDestinationStorage(storage.DestinationFromContext(ctx)))
	router.Use(context.SetEngine(engine.FromContext(ctx)))
	router.Use(context.SetConfig(cfg))

	if cfg.AllowedOrigins != nil && cfg.AllowedMethods != nil {
		allowedOrigins := cfg.AllowedOrigins

		allowAllOrigins := false

		if len(allowedOrigins) == 1 {
			if allowedOrigins[0] == "*" {
				allowAllOrigins = true
			}
		}

		if allowAllOrigins {
			allowedOrigins = nil
		}

		router.Use(cors.New(cors.Config{
			AllowAllOrigins: allowAllOrigins,
			AllowedOrigins:  allowedOrigins,
			AllowedMethods:  cfg.AllowedMethods,
			AllowedHeaders:  cfg.AllowedHeaders,
		}))
	}

	router.GET("/healthcheck", views.HealthcheckView)

	if cfg.Options.EnableStats {
		s := stats.New()

		router.Use(func() gin.HandlerFunc {
			return func(c *gin.Context) {
				beginning, recorder := s.Begin(c.Writer)
				c.Next()
				s.End(beginning, recorder)
			}
		}())

		router.GET("/stats", func(c *gin.Context) {
			c.JSON(http.StatusOK, s.Data())
		})
	}

	for name, view := range methods {
		views := []gin.HandlerFunc{
			middleware.ParametersParser(),
			middleware.KeyParser(),
			middleware.Security(),
			middleware.URLParser(),
			middleware.OperationParser(),
			middleware.RestrictSizes(),
			view,
		}

		router.GET(fmt.Sprintf("/%s", name), views...)

		if cfg.Storage != nil && cfg.Storage.Src != nil {
			router.GET(fmt.Sprintf("/%s/*parameters", name), views...)
		}
	}

	if cfg.Options.EnableUpload {
		router.POST("/upload", views.UploadView)
	}

	if cfg.Options.EnableDelete {
		router.DELETE("/*path", views.DeleteView)
	}

	return router, nil
}

// Run loads a new server
func Run(ctx netContext.Context) error {
	engine, err := Router(ctx)

	if err != nil {
		return err
	}

	engine.Run(fmt.Sprintf(":%s", strconv.Itoa(config.FromContext(ctx).Port)))

	return nil
}
<|endoftext|>


<commit_before>package main

/*import "fmt"
import "strings"
import "net"  //now look at this net that I just found
import "time" //time is a tool you can put on the wall or wear it on your wrist
//the past is far behind us, the future doesn't exist
import "./players"

func getPlayerOfIp(ip string) players.NetworkPlayer {
	for _, plr := range networkPlayerList {
		plrIp := plr.GetIp()
		if plrIp == ip[:strings.Index(ip, ":")] {
			return plr
		}
	}
	//for now, add a player to the list and return it
	networkPlayerList=append(networkPlayerList,players.NewNetworkPlayer("uwe",3,ip))
	return networkPlayerList[len(networkPlayerList)-1]
}

func handleConn1(conn net.Conn) {
	fmt.Println("conn 1")
	time.Sleep(time.Second / 2)
	for {
		conn.Write([]byte{65})
	}
	fmt.Println(conn.RemoteAddr().String())
	plr := getPlayerOfIp(conn.RemoteAddr().String())
	for len(plr.GetDataToBeSent()) == 0 {
		time.Sleep(time.Second / 2)
	}
	_, err := conn.Write(plr.GetDataToBeSent()[0])
	if err == nil {
		plr.RemoveASentData()
	}
}

func handleConn2(conn net.Conn) {
	fmt.Println("conn 2")
	fmt.Println(conn.RemoteAddr().String())
	var chat []byte
	_, err := conn.Read(chat)
	fmt.Println(chat,err)
	if err != nil {
		return
	}
	chat = append([]byte("CHAT"), chat...)
	plr := getPlayerOfIp(conn.RemoteAddr().String())
	for _, plr2 := range networkPlayerList {
		if plr2 != plr {
			plr2.SendData(chat)
		}
	}
}

func checkLoop1(quitChan chan struct{}) {
	ln, _ := net.Listen("tcp", ":5252") //event listeners
	for {
		select {
		case <-quitChan:
			fmt.Println("65")
			return
		default:
			conn, _ := ln.Accept()
			go handleConn1(conn)
		}
	}
}
func checkLoop2(quitChan chan struct{}) {
	ln, _ := net.Listen("tcp", ":6565") //chat
	for {
		select {
		case <-quitChan:
			fmt.Println("65")
			return
		default:
			conn, _ := ln.Accept()
			go handleConn2(conn)
		}
	}
}

func setupServer(quitChan chan struct{}) {
	go checkLoop1(quitChan)
	go checkLoop2(quitChan)
	fmt.Println("server loaded")
}
*/

import "net/http"
import "fmt"
import "html"
import "time"
import "io/ioutil"
import "strings"
import "./players"
func getPlayerOfIp(ip string) players.NetworkPlayer {
	for _, plr := range networkPlayerList {
		plrIp := plr.GetIp()
		if plrIp == ip[:strings.Index(ip, ":")] {
			return plr
		}
	}
	//for now, add a player to the list and return it
	networkPlayerList=append(networkPlayerList,players.NewNetworkPlayer("uwe",3,ip))
	return networkPlayerList[len(networkPlayerList)-1]
}
func fileServe(w http.ResponseWriter, r *http.Request) {
	fmt.Println("conn0")
	b,e := ioutil.ReadFile("client/"+html.EscapeString(r.URL.Path)[1:])
	fmt.Println(e)
	w.Write(b)
}
func eventFunc(w http.ResponseWriter, r *http.Request) {
	fmt.Println("conn1")
	fmt.Println(r.RemoteAddr)
	plr := getPlayerOfIp(r.RemoteAddr)
	for len(plr.GetDataToBeSent()) == 0 {
		time.Sleep(time.Second / 2)
	}
	_, err := w.Write(plr.GetDataToBeSent()[0])
	if err == nil {
		plr.RemoveASentData()
	}
}
func chatFunc(w http.ResponseWriter, r *http.Request) {
	fmt.Println("conn 2")
	fmt.Println(r.RemoteAddr)
	chat := ""
	r.ParseForm()
	for key := range r.Form {
		chat=key
		break
	}
	plr := getPlayerOfIp(r.RemoteAddr)
	data := []byte("CHAT"+plr.GetName()+": "+chat)
	for _, plr2 := range networkPlayerList {
		plr2.SendData(data)
	}
}
func setupServer(quitChan chan struct{}) {
	http.HandleFunc("/ordos.html",fileServe)
	http.HandleFunc("/ordos.js",fileServe)
	http.HandleFunc("/ordos.css",fileServe)
	http.HandleFunc("/event",eventFunc)
	http.HandleFunc("/chat",chatFunc)
	http.ListenAndServe(":8081",nil)
}
<commit_msg>not cri<commit_after>package main

import "net/http"
import "fmt"
import "html"
import "time"
import "io/ioutil"
import "strings"
import "./players"
func getPlayerOfIp(ip string) players.NetworkPlayer {
	for _, plr := range networkPlayerList {
		plrIp := plr.GetIp()
		if plrIp == ip[:strings.Index(ip, ":")] {
			return plr
		}
	}
	//for now, add a player to the list and return it
	networkPlayerList=append(networkPlayerList,players.NewNetworkPlayer("uwe",3,ip))
	return networkPlayerList[len(networkPlayerList)-1]
}
func fileServe(w http.ResponseWriter, r *http.Request) {
	fmt.Println("conn0")
	b,e := ioutil.ReadFile("client/"+html.EscapeString(r.URL.Path)[1:])
	fmt.Println(e)
	w.Write(b)
}
func eventFunc(w http.ResponseWriter, r *http.Request) {
	fmt.Println("conn1")
	fmt.Println(r.RemoteAddr)
	plr := getPlayerOfIp(r.RemoteAddr)
	for len(plr.GetDataToBeSent()) == 0 {
		time.Sleep(time.Second / 2)
	}
	_, err := w.Write(plr.GetDataToBeSent()[0])
	if err == nil {
		plr.RemoveASentData()
	}
}
func chatFunc(w http.ResponseWriter, r *http.Request) {
	fmt.Println("conn 2")
	fmt.Println(r.RemoteAddr)
	chat := ""
	r.ParseForm()
	for key := range r.Form {
		chat=key
		break
	}
	plr := getPlayerOfIp(r.RemoteAddr)
	data := []byte("CHAT"+plr.GetName()+": "+chat)
	for _, plr2 := range networkPlayerList {
		plr2.SendData(data)
	}
}
func setupServer(quitChan chan struct{}) {
	http.HandleFunc("/ordos.html",fileServe)
	http.HandleFunc("/ordos.js",fileServe)
	http.HandleFunc("/ordos.css",fileServe)
	http.HandleFunc("/event",eventFunc)
	http.HandleFunc("/chat",chatFunc)
	http.ListenAndServe(":8081",nil)
}
<|endoftext|>


<commit_before>package server

import(
    "github.com/brutella/hap/db"
    "github.com/brutella/hap/model"
    "github.com/brutella/hap/netio"
    "github.com/brutella/hap/netio/pair"
    "github.com/brutella/hap/netio/endpoint"
    "github.com/brutella/hap/netio/controller"

    "net/http"
    "fmt"
    "strconv"
    "os"
    "os/signal"
)

type Server interface {
}

type hkServer struct {
    model *model.Model
    context netio.HAPContext
    database db.Database
    bridge *netio.Bridge
    mux *http.ServeMux
    port int
}

func NewServer(c netio.HAPContext, d db.Database, m *model.Model, b *netio.Bridge, port int) *hkServer {
    s := hkServer{
        context: c,
        database: d,
        model: m,
        bridge: b,
        mux: http.NewServeMux(),
        port: port,
    }

    s.setupEndpoints()

    return &s
}

func (s *hkServer) ListenAndServe() error {
    s.teardownOnExit()
    return netio.ListenAndServe(s.addrString(), s.mux, s.context)
}

func (s *hkServer) Teardown() {
    for _, c := range s.context.ActiveConnection() {
        c.Close()
    }
}

func (s *hkServer) DNSSDCommand() string {
    return fmt.Sprintf("dns-sd -P %s _hap local %s macbookpro.local 192.168.0.14 pv=1.0 id=%s c#=1 s#=1 sf=1 ff=0 md=%s\n", s.bridge.Name(), s.portString(), s.bridge.Id(), s.bridge.Name())
}

func (s *hkServer) teardownOnExit() {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    signal.Notify(c, os.Kill)

    go func() {
        select {
        case <- c:
            s.Teardown()
            os.Exit(1)
        }
	}()
}

func (s *hkServer) portString() string {
    return strconv.Itoa(s.port)
}

func (s *hkServer) addrString() string {
    return ":" + s.portString()
}

func (s *hkServer) setupEndpoints() {
    model_controller           := controller.NewModelController(s.model)
    characteristics_controller := controller.NewCharacteristicController(s.model)
    pairing_controller         := pair.NewPairingController(s.database)

    s.mux.Handle("/pair-setup", endpoint.NewPairSetup(s.bridge, s.database, s.context))
    s.mux.Handle("/pair-verify", endpoint.NewPairVerify(s.context, s.database))
    s.mux.Handle("/accessories", endpoint.NewAccessories(model_controller))
    s.mux.Handle("/characteristics", endpoint.NewCharacteristics(characteristics_controller))
    s.mux.Handle("/pairings", endpoint.NewPairing(pairing_controller))
}<commit_msg>Use host name in dns-sd command output<commit_after>package server

import(
    "github.com/brutella/hap/db"
    "github.com/brutella/hap/model"
    "github.com/brutella/hap/netio"
    "github.com/brutella/hap/netio/pair"
    "github.com/brutella/hap/netio/endpoint"
    "github.com/brutella/hap/netio/controller"

    "net/http"
    "fmt"
    "strconv"
    "os"
    "os/signal"
)

type Server interface {
}

type hkServer struct {
    model *model.Model
    context netio.HAPContext
    database db.Database
    bridge *netio.Bridge
    mux *http.ServeMux
    port int
}

func NewServer(c netio.HAPContext, d db.Database, m *model.Model, b *netio.Bridge, port int) *hkServer {
    s := hkServer{
        context: c,
        database: d,
        model: m,
        bridge: b,
        mux: http.NewServeMux(),
        port: port,
    }

    s.setupEndpoints()

    return &s
}

func (s *hkServer) ListenAndServe() error {
    s.teardownOnExit()
    return netio.ListenAndServe(s.addrString(), s.mux, s.context)
}

func (s *hkServer) Teardown() {
    for _, c := range s.context.ActiveConnection() {
        c.Close()
    }
}

func (s *hkServer) DNSSDCommand() string {
    hostname, _ := os.Hostname()
    return fmt.Sprintf("dns-sd -P %s _hap local %s %s 192.168.0.14 pv=1.0 id=%s c#=1 s#=1 sf=1 ff=0 md=%s\n", s.bridge.Name(), s.portString(), hostname, s.bridge.Id(), s.bridge.Name())
}

func (s *hkServer) teardownOnExit() {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    signal.Notify(c, os.Kill)

    go func() {
        select {
        case <- c:
            s.Teardown()
            os.Exit(1)
        }
	}()
}

func (s *hkServer) portString() string {
    return strconv.Itoa(s.port)
}

func (s *hkServer) addrString() string {
    return ":" + s.portString()
}

func (s *hkServer) setupEndpoints() {
    model_controller           := controller.NewModelController(s.model)
    characteristics_controller := controller.NewCharacteristicController(s.model)
    pairing_controller         := pair.NewPairingController(s.database)

    s.mux.Handle("/pair-setup", endpoint.NewPairSetup(s.bridge, s.database, s.context))
    s.mux.Handle("/pair-verify", endpoint.NewPairVerify(s.context, s.database))
    s.mux.Handle("/accessories", endpoint.NewAccessories(model_controller))
    s.mux.Handle("/characteristics", endpoint.NewCharacteristics(characteristics_controller))
    s.mux.Handle("/pairings", endpoint.NewPairing(pairing_controller))
}<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"github.com/golang/glog"
	"github.com/heidi-ann/hydra/cache"
	"github.com/heidi-ann/hydra/config"
	"github.com/heidi-ann/hydra/consensus"
	"github.com/heidi-ann/hydra/msgs"
	"github.com/heidi-ann/hydra/store"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

var keyval *store.Store
var c *cache.Cache
var cons_io *msgs.Io
var notifyclient map[msgs.ClientRequest](chan msgs.ClientResponse)

type Peer struct {
	id      int
	address string
	handled bool // TOOD: replace with Mutex
}

var peers []Peer

var client_port = flag.Int("client-port", 8080, "port to listen on for clients")
var peer_port = flag.Int("peer-port", 8090, "port to listen on for peers")
var id = flag.Int("id", -1, "server ID")
var config_file = flag.String("config", "example.conf", "Server configuration file")

func openFile(filename string) (*bufio.Writer, *bufio.Reader, bool) {
	// check if file exists already for logging
	var is_new bool
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		glog.Info("Creating and opening file: ", filename)
		is_new = true
	} else {
		glog.Info("Opening file: ", filename)
		is_new = false
	}

	// open file
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777)
	if err != nil {
		glog.Fatal(err)
	}

	// create writer and reader
	w := bufio.NewWriter(file)
	r := bufio.NewReader(file)
	return w, r, is_new
}

func stateMachine() {
	for {
		req := <-(*cons_io).OutgoingRequests
		glog.Info("Request has been safely replicated by consensus algorithm", req)

		// check if request already applied
		found, reply := c.Check(req)
		if found {
			glog.Info("Request found in cache and thus cannot be applied")
		} else {
			// apply request
			output := keyval.Process(req.Request)
			keyval.Print()

			// write response to request cache
			reply = msgs.ClientResponse{
				req.ClientID, req.RequestID, output}
			c.Add(reply)
		}

		// if any handleRequests are waiting on this reply, then reply to them
		if notifyclient[req] != nil {
			notifyclient[req] <- reply
		}
	}
}

func handleRequest(req msgs.ClientRequest) msgs.ClientResponse {
	glog.Info("Handling ", req.Request)

	// check if already applied
	found, res := c.Check(req)
	if found {
		glog.Info("Request found in cache")
		return res // FAST PASS
	}

	// CONSENESUS ALGORITHM HERE
	glog.Info("Passing request to consensus algorithm")
	(*cons_io).IncomingRequests <- req

	// wait for reply
	notifyclient[req] = make(chan msgs.ClientResponse)
	reply := <-notifyclient[req]

	// check reply
	if reply.ClientID != req.ClientID {
		glog.Fatal("ClientID is different")
	}
	if reply.RequestID != req.RequestID {
		glog.Fatal("RequestID is different")
	}

	return reply
}

// iterative through peers and check there is a handler for each
// try to create one if not
func checkPeer() {
	for i := range peers {
		if !peers[i].handled {
			glog.Info("Peer ", i, " is not currently connected")
			cn, err := net.Dial("tcp", peers[i].address)

			if err != nil {
				glog.Warning(err)
			} else {
				handlePeer(cn, true)
			}
		}
	}
}

func handlePeer(cn net.Conn, _ bool) {
	addr := cn.RemoteAddr().String()
	glog.Info("Peer connection from ", addr)

	// handle requests
	reader := bufio.NewReader(cn)
	writer := bufio.NewWriter(cn)

	// exchange peer ID's
	_, _ = writer.WriteString(strconv.Itoa(*id) + "\n")
	_ = writer.Flush()
	text, _ := reader.ReadString('\n')
	glog.Info("Received ", text)
	peer_id, err := strconv.Atoi(strings.Trim(text, "\n"))
	if err != nil {
		glog.Warning(err)
		return
	}

	glog.Infof("Ready to handle traffic from peer %d at %s ", peer_id, addr)

	peers[peer_id].handled = true

	close_err := make(chan error)
	go func() {
		for {
			// read request
			glog.Infof("Ready for next message from %d", peer_id)
			text, err := reader.ReadBytes(byte('\n'))
			if err != nil {
				glog.Warning(err)
				close_err <- err
				break
			}
			glog.Infof("Read from peer %d: ", peer_id, string(text))
			(*cons_io).Incoming.BytesToProtoMsg(text)

		}
	}()

	go func() {
		for {
			// send reply
			b, err := (*cons_io).OutgoingUnicast[peer_id].ProtoMsgToBytes()
			if err != nil {
				glog.Fatal("Could not marshal message")
			}
			glog.Infof("Sending to %d: %s", peer_id, string(b))
			_, err = writer.Write(b)
			_, err = writer.Write([]byte("\n"))
			if err != nil {
				glog.Warning(err)
				close_err <- err
				break
			}
			err = writer.Flush()
			glog.Info("Sent")
		}
	}()

	// block until connection fails
	err = <-close_err

	// tidy up
	glog.Infof("No longer able to handle traffic from peer %d at %s ", id, addr)
	peers[peer_id].handled = false
	(*cons_io).Failure <- peer_id
	cn.Close()
}

func handleConnection(cn net.Conn) {
	glog.Info("Incoming client connection from ",
		cn.RemoteAddr().String())

	reader := bufio.NewReader(cn)
	writer := bufio.NewWriter(cn)

	for {

		// read request
		glog.Info("Reading")
		text, err := reader.ReadBytes(byte('\n'))
		if err != nil {
			if err == io.EOF {
				break
			}
			glog.Fatal(err)
		}
		glog.Info(string(text))
		req := new(msgs.ClientRequest)
		msgs.Unmarshal(text, req)

		// construct reply
		reply := handleRequest(*req)
		b, err := msgs.Marshal(reply)
		if err != nil {
			glog.Fatal("error:", err)
		}
		glog.Info(string(b))

		// send reply
		// TODO: FIX currently all server send back replies
		glog.Info("Sending ", string(b))
		n, err := writer.Write(b)
		_, err = writer.Write([]byte("\n"))
		if err != nil {
			glog.Fatal(err)
		}

		// tidy up
		err = writer.Flush()
		glog.Info("Finished sending ", n, " bytes")

	}

	cn.Close()
}

func main() {
	// set up logging
	flag.Parse()
	defer glog.Flush()

	conf := config.ParseServerConfig(*config_file)
	if *id == -1 {
		glog.Fatal("ID is required")
	}

	glog.Info("Starting server ", *id)

	//set up state machine
	keyval = store.New()
	c = cache.Create()
	// setup IO
	cons_io = msgs.MakeIo(1000, len(conf.Peers.Address))

	notifyclient = make(map[msgs.ClientRequest](chan msgs.ClientResponse))
	go stateMachine()

	// setting up persistent log
	disk, disk_reader, is_empty := openFile("persistent_log_" + strconv.Itoa(*id) + ".temp")
	defer disk.Flush()
	meta_disk, meta_disk_reader, is_new := openFile("persistent_data_" + strconv.Itoa(*id) + ".temp")

	// check persistent storage for commands
	found := false
	log := make([]msgs.Entry, 1000) //TODO: Fix this

	if !is_empty {
		for {
			b, err := disk_reader.ReadBytes(byte('\n'))
			if err != nil {
				glog.Info("No more commands in persistent storage")
				break
			}
			found = true
			var update msgs.LogUpdate
			err = msgs.Unmarshal(b, &update)
			if err != nil {
				glog.Fatal("Cannot parse log update", err)
			}
			log[update.Index] = update.Entry
		}
	}

	// check persistent storage for view
	view := 0
	if !is_new {
		for {
			b, err := meta_disk_reader.ReadBytes(byte('\n'))
			if err != nil {
				glog.Info("No more view updates in persistent storage")
				break
			}
			found = true
			view, _ = strconv.Atoi(string(b))
		}
	}

	// write updates to persistent storage
	go func() {
		for {
			// get write requests
			select {
			//disgard view updates
			case view := <-(*cons_io).ViewPersist:
				glog.Info("Updating view to ", view)
				_, err := meta_disk.Write([]byte(strconv.Itoa(view)))
				_, err = meta_disk.Write([]byte("\n"))
				_ = disk.Flush()
				if err != nil {
					glog.Fatal(err)
				}
			case log := <-(*cons_io).LogPersist:
				glog.Info("Updating log with ", log)
				b, err := msgs.Marshal(log)
				// write to persistent storage
				_, err = disk.Write(b)
				_, err = disk.Write([]byte("\n"))
				_ = disk.Flush()
				if err != nil {
					glog.Fatal(err)
				}
			}

		}
	}()

	// set up client server
	glog.Info("Starting up client server")
	listeningPort := ":" + strconv.Itoa(*client_port)
	ln, err := net.Listen("tcp", listeningPort)
	if err != nil {
		glog.Fatal(err)
	}

	// handle for incoming clients
	go func() {
		for {
			conn, err := ln.Accept()
			if err != nil {
				glog.Fatal(err)
			}
			go handleConnection(conn)
		}
	}()

	//set up peer state
	peers = make([]Peer, len(conf.Peers.Address))
	for i := range conf.Peers.Address {
		peers[i] = Peer{
			i, conf.Peers.Address[i], false}
	}

	//set up peer server
	glog.Info("Starting up peer server")
	listeningPort = ":" + strconv.Itoa(*peer_port)
	lnPeers, err := net.Listen("tcp", listeningPort)
	if err != nil {
		glog.Fatal(err)
	}

	// handle local peer (without sending network traffic)
	peers[*id].handled = true
	from := &((*cons_io).Incoming)
	go from.Forward((*cons_io).OutgoingUnicast[*id])

	// regularly check if all peers are connected and reply if not
	go func() {
		for {
			checkPeer()
			time.Sleep(10 * time.Second)
		}
	}()

	// handle for incoming peers
	go func() {
		for {
			conn, err := lnPeers.Accept()
			if err != nil {
				glog.Fatal(err)
			}
			go handlePeer(conn, false)
		}
	}()

	// setting up the consensus algorithm
	cons_config := consensus.Config{*id, len(conf.Peers.Address)}
	if !found {
		glog.Info("Starting fresh consensus instance")
		consensus.Init(cons_io, cons_config)
	} else {
		glog.Info("Restoring consensus instance")
		consensus.Recover(cons_io, cons_config, view, log)
	}
	cons_io.DumpPersistentStorage()

	// tidy up
	time.Sleep(30 * time.Second)
	glog.Info("Shutting down")
}
<commit_msg>error checking on result of msgs.Unmarshel<commit_after>package main

import (
	"bufio"
	"flag"
	"github.com/golang/glog"
	"github.com/heidi-ann/hydra/cache"
	"github.com/heidi-ann/hydra/config"
	"github.com/heidi-ann/hydra/consensus"
	"github.com/heidi-ann/hydra/msgs"
	"github.com/heidi-ann/hydra/store"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

var keyval *store.Store
var c *cache.Cache
var cons_io *msgs.Io
var notifyclient map[msgs.ClientRequest](chan msgs.ClientResponse)

type Peer struct {
	id      int
	address string
	handled bool // TOOD: replace with Mutex
}

var peers []Peer

var client_port = flag.Int("client-port", 8080, "port to listen on for clients")
var peer_port = flag.Int("peer-port", 8090, "port to listen on for peers")
var id = flag.Int("id", -1, "server ID")
var config_file = flag.String("config", "example.conf", "Server configuration file")

func openFile(filename string) (*bufio.Writer, *bufio.Reader, bool) {
	// check if file exists already for logging
	var is_new bool
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		glog.Info("Creating and opening file: ", filename)
		is_new = true
	} else {
		glog.Info("Opening file: ", filename)
		is_new = false
	}

	// open file
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777)
	if err != nil {
		glog.Fatal(err)
	}

	// create writer and reader
	w := bufio.NewWriter(file)
	r := bufio.NewReader(file)
	return w, r, is_new
}

func stateMachine() {
	for {
		req := <-(*cons_io).OutgoingRequests
		glog.Info("Request has been safely replicated by consensus algorithm", req)

		// check if request already applied
		found, reply := c.Check(req)
		if found {
			glog.Info("Request found in cache and thus cannot be applied")
		} else {
			// apply request
			output := keyval.Process(req.Request)
			keyval.Print()

			// write response to request cache
			reply = msgs.ClientResponse{
				req.ClientID, req.RequestID, output}
			c.Add(reply)
		}

		// if any handleRequests are waiting on this reply, then reply to them
		if notifyclient[req] != nil {
			notifyclient[req] <- reply
		}
	}
}

func handleRequest(req msgs.ClientRequest) msgs.ClientResponse {
	glog.Info("Handling ", req.Request)

	// check if already applied
	found, res := c.Check(req)
	if found {
		glog.Info("Request found in cache")
		return res // FAST PASS
	}

	// CONSENESUS ALGORITHM HERE
	glog.Info("Passing request to consensus algorithm")
	(*cons_io).IncomingRequests <- req

	// wait for reply
	notifyclient[req] = make(chan msgs.ClientResponse)
	reply := <-notifyclient[req]

	// check reply
	if reply.ClientID != req.ClientID {
		glog.Fatal("ClientID is different")
	}
	if reply.RequestID != req.RequestID {
		glog.Fatal("RequestID is different")
	}

	return reply
}

// iterative through peers and check there is a handler for each
// try to create one if not
func checkPeer() {
	for i := range peers {
		if !peers[i].handled {
			glog.Info("Peer ", i, " is not currently connected")
			cn, err := net.Dial("tcp", peers[i].address)

			if err != nil {
				glog.Warning(err)
			} else {
				handlePeer(cn, true)
			}
		}
	}
}

func handlePeer(cn net.Conn, _ bool) {
	addr := cn.RemoteAddr().String()
	glog.Info("Peer connection from ", addr)

	// handle requests
	reader := bufio.NewReader(cn)
	writer := bufio.NewWriter(cn)

	// exchange peer ID's
	_, _ = writer.WriteString(strconv.Itoa(*id) + "\n")
	_ = writer.Flush()
	text, _ := reader.ReadString('\n')
	glog.Info("Received ", text)
	peer_id, err := strconv.Atoi(strings.Trim(text, "\n"))
	if err != nil {
		glog.Warning(err)
		return
	}

	glog.Infof("Ready to handle traffic from peer %d at %s ", peer_id, addr)

	peers[peer_id].handled = true

	close_err := make(chan error)
	go func() {
		for {
			// read request
			glog.Infof("Ready for next message from %d", peer_id)
			text, err := reader.ReadBytes(byte('\n'))
			if err != nil {
				glog.Warning(err)
				close_err <- err
				break
			}
			glog.Infof("Read from peer %d: ", peer_id, string(text))
			(*cons_io).Incoming.BytesToProtoMsg(text)

		}
	}()

	go func() {
		for {
			// send reply
			b, err := (*cons_io).OutgoingUnicast[peer_id].ProtoMsgToBytes()
			if err != nil {
				glog.Fatal("Could not marshal message")
			}
			glog.Infof("Sending to %d: %s", peer_id, string(b))
			_, err = writer.Write(b)
			_, err = writer.Write([]byte("\n"))
			if err != nil {
				glog.Warning(err)
				close_err <- err
				break
			}
			err = writer.Flush()
			glog.Info("Sent")
		}
	}()

	// block until connection fails
	err = <-close_err

	// tidy up
	glog.Infof("No longer able to handle traffic from peer %d at %s ", id, addr)
	peers[peer_id].handled = false
	(*cons_io).Failure <- peer_id
	cn.Close()
}

func handleConnection(cn net.Conn) {
	glog.Info("Incoming client connection from ",
		cn.RemoteAddr().String())

	reader := bufio.NewReader(cn)
	writer := bufio.NewWriter(cn)

	for {

		// read request
		glog.Info("Reading")
		text, err := reader.ReadBytes(byte('\n'))
		if err != nil {
			if err == io.EOF {
				break
			}
			glog.Fatal(err)
		}
		glog.Info(string(text))
		req := new(msgs.ClientRequest)
		err = msgs.Unmarshal(text, req)
		if err != nil {
			glog.Fatal(err)
		}

		// construct reply
		reply := handleRequest(*req)
		b, err := msgs.Marshal(reply)
		if err != nil {
			glog.Fatal("error:", err)
		}
		glog.Info(string(b))

		// send reply
		// TODO: FIX currently all server send back replies
		glog.Info("Sending ", string(b))
		n, err := writer.Write(b)
		_, err = writer.Write([]byte("\n"))
		if err != nil {
			glog.Fatal(err)
		}

		// tidy up
		err = writer.Flush()
		glog.Info("Finished sending ", n, " bytes")

	}

	cn.Close()
}

func main() {
	// set up logging
	flag.Parse()
	defer glog.Flush()

	conf := config.ParseServerConfig(*config_file)
	if *id == -1 {
		glog.Fatal("ID is required")
	}

	glog.Info("Starting server ", *id)

	//set up state machine
	keyval = store.New()
	c = cache.Create()
	// setup IO
	cons_io = msgs.MakeIo(1000, len(conf.Peers.Address))

	notifyclient = make(map[msgs.ClientRequest](chan msgs.ClientResponse))
	go stateMachine()

	// setting up persistent log
	disk, disk_reader, is_empty := openFile("persistent_log_" + strconv.Itoa(*id) + ".temp")
	defer disk.Flush()
	meta_disk, meta_disk_reader, is_new := openFile("persistent_data_" + strconv.Itoa(*id) + ".temp")

	// check persistent storage for commands
	found := false
	log := make([]msgs.Entry, 1000) //TODO: Fix this

	if !is_empty {
		for {
			b, err := disk_reader.ReadBytes(byte('\n'))
			if err != nil {
				glog.Info("No more commands in persistent storage")
				break
			}
			found = true
			var update msgs.LogUpdate
			err = msgs.Unmarshal(b, &update)
			if err != nil {
				glog.Fatal("Cannot parse log update", err)
			}
			log[update.Index] = update.Entry
		}
	}

	// check persistent storage for view
	view := 0
	if !is_new {
		for {
			b, err := meta_disk_reader.ReadBytes(byte('\n'))
			if err != nil {
				glog.Info("No more view updates in persistent storage")
				break
			}
			found = true
			view, _ = strconv.Atoi(string(b))
		}
	}

	// write updates to persistent storage
	go func() {
		for {
			// get write requests
			select {
			//disgard view updates
			case view := <-(*cons_io).ViewPersist:
				glog.Info("Updating view to ", view)
				_, err := meta_disk.Write([]byte(strconv.Itoa(view)))
				_, err = meta_disk.Write([]byte("\n"))
				_ = disk.Flush()
				if err != nil {
					glog.Fatal(err)
				}
			case log := <-(*cons_io).LogPersist:
				glog.Info("Updating log with ", log)
				b, err := msgs.Marshal(log)
				// write to persistent storage
				_, err = disk.Write(b)
				_, err = disk.Write([]byte("\n"))
				_ = disk.Flush()
				if err != nil {
					glog.Fatal(err)
				}
			}

		}
	}()

	// set up client server
	glog.Info("Starting up client server")
	listeningPort := ":" + strconv.Itoa(*client_port)
	ln, err := net.Listen("tcp", listeningPort)
	if err != nil {
		glog.Fatal(err)
	}

	// handle for incoming clients
	go func() {
		for {
			conn, err := ln.Accept()
			if err != nil {
				glog.Fatal(err)
			}
			go handleConnection(conn)
		}
	}()

	//set up peer state
	peers = make([]Peer, len(conf.Peers.Address))
	for i := range conf.Peers.Address {
		peers[i] = Peer{
			i, conf.Peers.Address[i], false}
	}

	//set up peer server
	glog.Info("Starting up peer server")
	listeningPort = ":" + strconv.Itoa(*peer_port)
	lnPeers, err := net.Listen("tcp", listeningPort)
	if err != nil {
		glog.Fatal(err)
	}

	// handle local peer (without sending network traffic)
	peers[*id].handled = true
	from := &((*cons_io).Incoming)
	go from.Forward((*cons_io).OutgoingUnicast[*id])

	// regularly check if all peers are connected and reply if not
	go func() {
		for {
			checkPeer()
			time.Sleep(10 * time.Second)
		}
	}()

	// handle for incoming peers
	go func() {
		for {
			conn, err := lnPeers.Accept()
			if err != nil {
				glog.Fatal(err)
			}
			go handlePeer(conn, false)
		}
	}()

	// setting up the consensus algorithm
	cons_config := consensus.Config{*id, len(conf.Peers.Address)}
	if !found {
		glog.Info("Starting fresh consensus instance")
		consensus.Init(cons_io, cons_config)
	} else {
		glog.Info("Restoring consensus instance")
		consensus.Recover(cons_io, cons_config, view, log)
	}
	cons_io.DumpPersistentStorage()

	// tidy up
	time.Sleep(30 * time.Second)
	glog.Info("Shutting down")
}
<|endoftext|>