<commit_before>package server

import (
	"net/http"
	"fmt"
	"log"
	"os"
	"io/ioutil"
	"strconv"
	"time"
	"gopkg.in/mgo.v2/bson"
	"encoding/json"
	"gopkg.in/mgo.v2"
	"github.com/rcrowley/go-metrics"
	 //"github.com/vrischmann/go-metrics-influxdb"

)

type UploadResponse struct {
	Success      bool   `json:"success"`
	Error        string `json:"error,omitempty"`
	PreventRetry bool   `json:"preventRetry"`
}

type gfsFile struct {
	Id          interface{} "_id"
	ChunkSize   int         "chunkSize"
	UploadDate  time.Time   "uploadDate"
	Length      int64       ",minsize"
	MD5         string
	Filename    string    ",omitempty"
	ContentType string    "contentType,omitempty"
	Metadata    *bson.Raw ",omitempty"
}

// Chunked request parameters
const (
	paramUuid = "qquuid" // uuid
	paramFile = "qqfile" // file name

	paramPartIndex       = "qqpartindex"      // part index
	paramPartBytesOffset = "qqpartbyteoffset" // part byte offset
	paramTotalFileSize   = "qqtotalfilesize"  // total file size
	paramTotalParts      = "qqtotalparts"     // total parts
	paramFileName        = "qqfilename"       // file name for chunked requests
	paramChunkSize       = "qqchunksize"      // size of the chunks
)

var (
	reqUploadCount = metrics.NewRegisteredCounter("upload.request", metrics.DefaultRegistry)
	reqUploadOkCount = metrics.NewRegisteredCounter("upload.request.ok", metrics.DefaultRegistry)
	reqUploadErrCount = metrics.NewRegisteredCounter("upload.request.error", metrics.DefaultRegistry)
	reqUploadParamErrCount = metrics.NewRegisteredCounter("upload.request.params.error", metrics.DefaultRegistry)

	reqUploadDoneCount = metrics.NewRegisteredCounter("uploaddone.request", metrics.DefaultRegistry)
	reqUploadDoneOkCount = metrics.NewRegisteredCounter("uploaddone.request.ok", metrics.DefaultRegistry)
	reqUploadDoneErrCount = metrics.NewRegisteredCounter("uploaddone.request.error", metrics.DefaultRegistry)
	reqUploadDoneParamErrCount = metrics.NewRegisteredCounter("uploaddone.request.params.error", metrics.DefaultRegistry)

	DBErrCount = metrics.NewRegisteredCounter("db.error", metrics.DefaultRegistry)

)

type HttpServer struct {
	host string
	port int
	dir string
	mongo string
	session *mgo.Session
	gridFs *mgo.GridFS
}

func New(port int,host string,mongo string,dir string) *HttpServer {
	return &HttpServer{
		host:host,
		port:port,
		dir:dir,
		mongo:mongo,
	}
}

func (srv *HttpServer) EnsureConnected(){
	defer func() {
		if r := recover(); r != nil {
			log.Println("ReConnect....")
			//Your reconnect logic here.
			session, err := mgo.Dial(srv.mongo)
			if err != nil {
				log.Println("[grid]mgo.Dial ", err)
				panic(err)
				return
			}
			srv.session = session

			database := session.DB("test")
			srv.gridFs = database.GridFS("uploader")
			log.Println("ReConnect ok")
		}
	}()

	//Ping panics if session is closed. (see mgo.Session.Panic())
	err := srv.session.Ping()
	if err != nil {
		log.Println("ping:",err)
		DBErrCount.Inc(1)
		panic(err)
	}
}

func (srv *HttpServer) Start() {
	session, err := mgo.Dial(srv.mongo)

	log.Println("dial mongodb:",srv.mongo)
	if err != nil {
		log.Println("[grid]mgo.Dial ", err)
		return
	}
	srv.session = session
	defer srv.session.Close()

	database := session.DB("test")
	srv.gridFs = database.GridFS("uploader")

	http.HandleFunc("/upload",srv.UploadHandler)
	http.HandleFunc("/chunksdone", srv.ChunksDoneHandler)
	http.Handle("/upload/", http.StripPrefix("/upload/", http.HandlerFunc(srv.UploadHandler)))
	http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, r.URL.Path[1:])
	})
	http.HandleFunc("/res/", func(w http.ResponseWriter, r *http.Request) {
		http.StripPrefix("/res/", Assets(AssetsOpts{
			Develop:false,
		})).ServeHTTP(w, r)
	})
	http.HandleFunc("/uploads/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, r.URL.Path[1:])
	})
	http.HandleFunc("/metrics",srv.Metrics)
	hostPort := fmt.Sprintf("%s:%d",srv.host, srv.port)
	log.Printf("Initiating server listening at [%s]", hostPort)
	log.Printf("Base upload directory set to [%s]", srv.dir)

	metrics.RegisterRuntimeMemStats(metrics.DefaultRegistry)
	go metrics.CaptureRuntimeMemStats(metrics.DefaultRegistry, 5*time.Second)

	//influxdb.InfluxDB(metrics.DefaultRegistry, 10e9, "127.0.0.1:8086","metrics", "test", "test" )

	log.Fatal(http.ListenAndServe(hostPort, nil))

}

func (srv *HttpServer) Metrics(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("content-type", "application/json")
	b, _ := metrics.DefaultRegistry.(*metrics.StandardRegistry).MarshalJSON()

	w.Write(b)
//	metrics.WriteJSONOnce(metrics.DefaultRegistry,w)
}

func (srv *HttpServer) UploadHandler(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case http.MethodPost:
		srv.upload(w, req)
		return
	case http.MethodDelete:
		srv.delete(w, req)
		return
	}
	errorMsg := fmt.Sprintf("Method [%s] is not supported:", req.Method)
	http.Error(w, errorMsg, http.StatusMethodNotAllowed)
}

func (srv *HttpServer)delete(w http.ResponseWriter, req *http.Request) {
	log.Printf("Delete request received for uuid [%s]", req.URL.Path)
	err := os.RemoveAll(srv.dir + "/" + req.URL.Path)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}
	w.WriteHeader(http.StatusOK)
}

func (srv *HttpServer)singleFile(w http.ResponseWriter, req *http.Request) {
	uuid := req.FormValue(paramUuid)
	if len(uuid) == 0 {
		log.Printf("No uuid received, invalid upload request")
		http.Error(w, "No uuid received", http.StatusBadRequest)
		reqUploadParamErrCount.Inc(1)
		return
	}

	file, headers, err := req.FormFile(paramFile)
	if err != nil {
		log.Println(err)
		srv.writeUploadResponse(w, err)
		reqUploadParamErrCount.Inc(1)
		return
	}

	datas,err := ioutil.ReadAll(file)

	filename := fmt.Sprintf("%s",  headers.Filename)
	totalSize := len(datas)
	totalPart := 1
	offset := 0
	index := 0

	srv.writeGridFile(filename,req.FormValue(paramFileName),uuid,totalSize,totalSize,totalPart,offset,index,datas)
	srv.writeUploadResponse(w, nil)

	reqUploadOkCount.Inc(1)
}

func (srv *HttpServer)multiFile(w http.ResponseWriter, req *http.Request) {
	uuid := req.FormValue(paramUuid)
	if len(uuid) == 0 {
		log.Printf("No uuid received, invalid upload request")
		http.Error(w, "No uuid received", http.StatusBadRequest)
		reqUploadParamErrCount.Inc(1)
		return
	}

	file, _, err := req.FormFile(paramFile)
	if err != nil {
		log.Println(err)
		srv.writeUploadResponse(w, err)
		reqUploadParamErrCount.Inc(1)
		return
	}
	datas,err := ioutil.ReadAll(file)

	chunkSize,err := strconv.Atoi(req.FormValue(paramChunkSize))
	totalSize,err := strconv.Atoi(req.FormValue(paramTotalFileSize))
	index,err := strconv.Atoi(req.FormValue(paramPartIndex))
	srv.writeChunks(index,datas,uuid,chunkSize,totalSize,req.FormValue(paramFileName))

	srv.writeUploadResponse(w, nil)
	reqUploadOkCount.Inc(1)
}

func (srv *HttpServer)getFinalFileID(uuid string,chunkSize int,totalFileSize int,filename string) interface{}{
	srv.EnsureConnected()

	var objFile gfsFile
	err := srv.gridFs.Files.Find(bson.M{ "filename": bson.RegEx{fmt.Sprintf("%s#%s", uuid,filename), "i"}}).One(&objFile)
	if err == nil {
		return objFile.Id
	}
	finalId := bson.NewObjectId()

	objFile = gfsFile {
		Id:finalId,
		UploadDate: bson.Now(),
		Length: int64(totalFileSize),
		ChunkSize: chunkSize,
		Filename:fmt.Sprintf("%s#%s", uuid,filename),
		MD5:uuid,
	}
	err = srv.gridFs.Files.Insert(objFile)
	if err != nil {
		DBErrCount.Inc(1)
		log.Println("Files.Insert",err)
		panic(err)
	}

	return finalId
}

func (srv*HttpServer)writeChunks(index int,datas []byte,uuid string,chunkSize int,totalSize int,filename string)  {
	type gfsChunk struct {
		Id      interface{} "_id"
		FilesId interface{} "files_id"
		N       int
		Data    []byte
	}

	// We may not own the memory of data, so rather than
	// simply copying it, we'll marshal the document ahead of time.
	fileid := srv.getFinalFileID(uuid,chunkSize,totalSize,filename)
	data, err := bson.Marshal(gfsChunk{bson.NewObjectId(), fileid, index, datas})
	if err != nil {
		log.Println(err)
		panic(err)
		return
	}

	err = srv.gridFs.Chunks.Insert(bson.Raw{Data: data})
	if err != nil{
		DBErrCount.Inc(1)
		panic(err)
	}
}

func (srv *HttpServer) writeGridFile(filename string,
	shortName string,
	uuid string,
	chunkSize int,
	totalSize int,
	totalPart int,
	offset int,
	index int,
	datas []byte)  {

	srv.EnsureConnected()
	gridFile, err := srv.gridFs.Create(filename)
	if err != nil {
		DBErrCount.Inc(1)
		log.Println(err)
		panic(err)
		return
	}

	gridFile.SetChunkSize(chunkSize)

	gridFile.SetMeta(&struct{
		Index int
		Uuid string
		Filename string
		ChunkSize int
		TotalSize int
		TotalPart int
		Offset int} {
		Uuid:uuid,
		Filename:shortName,
		ChunkSize: chunkSize,
		TotalSize:totalSize,
		TotalPart:totalPart,
		Offset:offset,
		Index:index,
	})
	_,err = gridFile.Write(datas)
	if err != nil{
		DBErrCount.Inc(1)
		panic(err)
	}

	defer gridFile.Close()
}

func (srv *HttpServer)upload(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("panic upload error")
			reqUploadErrCount.Inc(1)
		}
	}()

	reqUploadCount.Inc(1)
	//atomic.AddInt64(&reqUploadCount,1)
	partIndex := req.FormValue(paramPartIndex)
	if len(partIndex) == 0 {
		srv.singleFile(w,req)
		return
	}
	srv.multiFile(w,req)
	return
}

func (srv *HttpServer)ChunksDoneHandler(w http.ResponseWriter, req *http.Request) {

	defer func() {
		if r := recover(); r != nil {
			fmt.Println("panic upload done error")
			reqUploadDoneErrCount.Inc(1)
		}
	}()

	if req.Method != http.MethodPost {
		errorMsg := fmt.Sprintf("Method [%s] is not supported", req.Method)
		http.Error(w, errorMsg, http.StatusMethodNotAllowed)
	}
	reqUploadDoneCount.Inc(1)
	uuid := req.FormValue(paramUuid)
	filename := req.FormValue(paramFileName)

	if uuid == ""  || filename == ""{
		reqUploadDoneParamErrCount.Inc(1)
		return
	}

	var objFile gfsFile
	srv.EnsureConnected()
	err := srv.gridFs.Files.Find(bson.M{ "filename": bson.RegEx{fmt.Sprintf("%s#%s", uuid,filename), "i"}}).One(&objFile)
	//count,err := gridFs.Files.Find(bson.M{ "filename": bson.RegEx{fmt.Sprintf("%s", uuid), "i"}}).Count()
	if err != nil {
		DBErrCount.Inc(1)
		reqUploadDoneErrCount.Inc(1)
	}

	err = srv.gridFs.Files.Update(bson.M{"_id":objFile.Id}, bson.M{"$set": bson.M{ "uploadDate":  bson.Now(), }})
	if err != nil {
		DBErrCount.Inc(1)
		reqUploadDoneErrCount.Inc(1)
	}
	reqUploadDoneOkCount.Inc(1)

}

func (srv *HttpServer)writeUploadResponse(w http.ResponseWriter, err error) {
	uploadResponse := new(UploadResponse)
	if err != nil {
		uploadResponse.Error = err.Error()
	} else {
		uploadResponse.Success = true
	}
	w.Header().Set("Content-Type", "text/plain")
	json.NewEncoder(w).Encode(uploadResponse)
}
<commit_msg>edit<commit_after>package server

import (
	"net/http"
	"fmt"
	"log"
	"os"
	"io/ioutil"
	"strconv"
	"time"
	"gopkg.in/mgo.v2/bson"
	"encoding/json"
	"gopkg.in/mgo.v2"
	"github.com/rcrowley/go-metrics"
	 //"github.com/vrischmann/go-metrics-influxdb"
	_ "net/http/pprof"
)

type UploadResponse struct {
	Success      bool   `json:"success"`
	Error        string `json:"error,omitempty"`
	PreventRetry bool   `json:"preventRetry"`
}

type gfsFile struct {
	Id          interface{} "_id"
	ChunkSize   int         "chunkSize"
	UploadDate  time.Time   "uploadDate"
	Length      int64       ",minsize"
	MD5         string
	Filename    string    ",omitempty"
	ContentType string    "contentType,omitempty"
	Metadata    *bson.Raw ",omitempty"
}

// Chunked request parameters
const (
	paramUuid = "qquuid" // uuid
	paramFile = "qqfile" // file name

	paramPartIndex       = "qqpartindex"      // part index
	paramPartBytesOffset = "qqpartbyteoffset" // part byte offset
	paramTotalFileSize   = "qqtotalfilesize"  // total file size
	paramTotalParts      = "qqtotalparts"     // total parts
	paramFileName        = "qqfilename"       // file name for chunked requests
	paramChunkSize       = "qqchunksize"      // size of the chunks
)

var (
	reqUploadCount = metrics.NewRegisteredCounter("upload.request", metrics.DefaultRegistry)
	reqUploadOkCount = metrics.NewRegisteredCounter("upload.request.ok", metrics.DefaultRegistry)
	reqUploadErrCount = metrics.NewRegisteredCounter("upload.request.error", metrics.DefaultRegistry)
	reqUploadParamErrCount = metrics.NewRegisteredCounter("upload.request.params.error", metrics.DefaultRegistry)

	reqUploadDoneCount = metrics.NewRegisteredCounter("uploaddone.request", metrics.DefaultRegistry)
	reqUploadDoneOkCount = metrics.NewRegisteredCounter("uploaddone.request.ok", metrics.DefaultRegistry)
	reqUploadDoneErrCount = metrics.NewRegisteredCounter("uploaddone.request.error", metrics.DefaultRegistry)
	reqUploadDoneParamErrCount = metrics.NewRegisteredCounter("uploaddone.request.params.error", metrics.DefaultRegistry)

	DBErrCount = metrics.NewRegisteredCounter("db.error", metrics.DefaultRegistry)

)

type HttpServer struct {
	host string
	port int
	dir string
	mongo string
	session *mgo.Session
	gridFs *mgo.GridFS
}

func New(port int,host string,mongo string,dir string) *HttpServer {
	return &HttpServer{
		host:host,
		port:port,
		dir:dir,
		mongo:mongo,
	}
}

func (srv *HttpServer) EnsureConnected(){
	defer func() {
		if r := recover(); r != nil {
			log.Println("ReConnect....")
			//Your reconnect logic here.
			session, err := mgo.Dial(srv.mongo)
			if err != nil {
				log.Println("[grid]mgo.Dial ", err)
				panic(err)
				return
			}
			srv.session = session

			database := session.DB("test")
			srv.gridFs = database.GridFS("uploader")
			log.Println("ReConnect ok")
		}
	}()

	//Ping panics if session is closed. (see mgo.Session.Panic())
	err := srv.session.Ping()
	if err != nil {
		srv.session.Close()
		log.Println("ping:",err)
		DBErrCount.Inc(1)
		panic(err)
	}
}

func (srv *HttpServer) Start() {
	session, err := mgo.Dial(srv.mongo)

	log.Println("dial mongodb:",srv.mongo)
	if err != nil {
		log.Println("[grid]mgo.Dial ", err)
		return
	}
	srv.session = session
	defer srv.session.Close()

	database := session.DB("test")
	srv.gridFs = database.GridFS("uploader")

	http.HandleFunc("/upload",srv.UploadHandler)
	http.HandleFunc("/chunksdone", srv.ChunksDoneHandler)
	http.Handle("/upload/", http.StripPrefix("/upload/", http.HandlerFunc(srv.UploadHandler)))
	http.HandleFunc("/res/", func(w http.ResponseWriter, r *http.Request) {
		http.StripPrefix("/res/", Assets(AssetsOpts{
			Develop:false,
		})).ServeHTTP(w, r)
	})
	http.HandleFunc("/uploads/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, r.URL.Path[1:])
	})
	http.HandleFunc("/metrics",srv.Metrics)
	hostPort := fmt.Sprintf("%s:%d",srv.host, srv.port)
	log.Printf("Initiating server listening at [%s]", hostPort)
	log.Printf("Base upload directory set to [%s]", srv.dir)

	metrics.RegisterRuntimeMemStats(metrics.DefaultRegistry)
	go metrics.CaptureRuntimeMemStats(metrics.DefaultRegistry, 5*time.Second)

	//influxdb.InfluxDB(metrics.DefaultRegistry, 10e9, "127.0.0.1:8086","metrics", "test", "test" )

	log.Fatal(http.ListenAndServe(hostPort, nil))

}

func (srv *HttpServer) Metrics(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("content-type", "application/json")
	b, _ := metrics.DefaultRegistry.(*metrics.StandardRegistry).MarshalJSON()

	w.Write(b)
//	metrics.WriteJSONOnce(metrics.DefaultRegistry,w)
}

func (srv *HttpServer) UploadHandler(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case http.MethodPost:
		srv.upload(w, req)
		return
	case http.MethodDelete:
		srv.delete(w, req)
		return
	}
	errorMsg := fmt.Sprintf("Method [%s] is not supported:", req.Method)
	http.Error(w, errorMsg, http.StatusMethodNotAllowed)
}

func (srv *HttpServer)delete(w http.ResponseWriter, req *http.Request) {
	log.Printf("Delete request received for uuid [%s]", req.URL.Path)
	err := os.RemoveAll(srv.dir + "/" + req.URL.Path)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}
	w.WriteHeader(http.StatusOK)
}

func (srv *HttpServer)singleFile(w http.ResponseWriter, req *http.Request) {
	uuid := req.FormValue(paramUuid)
	if len(uuid) == 0 {
		log.Printf("No uuid received, invalid upload request")
		http.Error(w, "No uuid received", http.StatusBadRequest)
		reqUploadParamErrCount.Inc(1)
		return
	}

	file, headers, err := req.FormFile(paramFile)
	if err != nil {
		log.Println(err)
		srv.writeUploadResponse(w, err)
		reqUploadParamErrCount.Inc(1)
		return
	}
	defer file.Close()

	datas,err := ioutil.ReadAll(file)

	filename := fmt.Sprintf("%s",  headers.Filename)
	totalSize := len(datas)
	totalPart := 1
	offset := 0
	index := 0

	srv.writeGridFile(filename,req.FormValue(paramFileName),uuid,totalSize,totalSize,totalPart,offset,index,datas)
	srv.writeUploadResponse(w, nil)

	reqUploadOkCount.Inc(1)
}

func (srv *HttpServer)multiFile(w http.ResponseWriter, req *http.Request) {
	uuid := req.FormValue(paramUuid)
	if len(uuid) == 0 {
		log.Printf("No uuid received, invalid upload request")
		http.Error(w, "No uuid received", http.StatusBadRequest)
		reqUploadParamErrCount.Inc(1)
		return
	}

	file, _, err := req.FormFile(paramFile)
	if err != nil {
		log.Println(err)
		srv.writeUploadResponse(w, err)
		reqUploadParamErrCount.Inc(1)
		return
	}
	defer file.Close()
	datas,err := ioutil.ReadAll(file)

	chunkSize,err := strconv.Atoi(req.FormValue(paramChunkSize))
	totalSize,err := strconv.Atoi(req.FormValue(paramTotalFileSize))
	index,err := strconv.Atoi(req.FormValue(paramPartIndex))
	srv.writeChunks(index,datas,uuid,chunkSize,totalSize,req.FormValue(paramFileName))

	srv.writeUploadResponse(w, nil)
	reqUploadOkCount.Inc(1)
}

func (srv *HttpServer)getFinalFileID(uuid string,chunkSize int,totalFileSize int,filename string) interface{}{
	srv.EnsureConnected()

	var objFile gfsFile
	err := srv.gridFs.Files.Find(bson.M{ "filename": bson.RegEx{fmt.Sprintf("%s#%s", uuid,filename), "i"}}).One(&objFile)
	if err == nil {
		return objFile.Id
	}
	finalId := bson.NewObjectId()

	objFile = gfsFile {
		Id:finalId,
		UploadDate: bson.Now(),
		Length: int64(totalFileSize),
		ChunkSize: chunkSize,
		Filename:fmt.Sprintf("%s#%s", uuid,filename),
		MD5:uuid,
	}
	err = srv.gridFs.Files.Insert(objFile)
	if err != nil {
		DBErrCount.Inc(1)
		log.Println("Files.Insert",err)
		panic(err)
	}

	return finalId
}

func (srv*HttpServer)writeChunks(index int,datas []byte,uuid string,chunkSize int,totalSize int,filename string)  {
	type gfsChunk struct {
		Id      interface{} "_id"
		FilesId interface{} "files_id"
		N       int
		Data    []byte
	}

	// We may not own the memory of data, so rather than
	// simply copying it, we'll marshal the document ahead of time.
	fileid := srv.getFinalFileID(uuid,chunkSize,totalSize,filename)
	data, err := bson.Marshal(gfsChunk{bson.NewObjectId(), fileid, index, datas})
	if err != nil {
		log.Println(err)
		panic(err)
		return
	}
	srv.gridFs.Chunks.Remove(bson.M{"files_id":fileid,"n":index})
	err = srv.gridFs.Chunks.Insert(bson.Raw{Data: data})

	if err != nil{

		log.Println(err)
		DBErrCount.Inc(1)
		panic(err)
	}
}

func (srv *HttpServer) writeGridFile(filename string,
	shortName string,
	uuid string,
	chunkSize int,
	totalSize int,
	totalPart int,
	offset int,
	index int,
	datas []byte)  {

	srv.EnsureConnected()
	gridFile, err := srv.gridFs.Create(filename)
	if err != nil {
		DBErrCount.Inc(1)
		log.Println(err)
		panic(err)
		return
	}
	defer gridFile.Close()
	gridFile.SetChunkSize(chunkSize)

	gridFile.SetMeta(&struct{
		Index int
		Uuid string
		Filename string
		ChunkSize int
		TotalSize int
		TotalPart int
		Offset int} {
		Uuid:uuid,
		Filename:shortName,
		ChunkSize: chunkSize,
		TotalSize:totalSize,
		TotalPart:totalPart,
		Offset:offset,
		Index:index,
	})
	_,err = gridFile.Write(datas)
	if err != nil{
		DBErrCount.Inc(1)
		panic(err)
	}

}

func (srv *HttpServer)upload(w http.ResponseWriter, req *http.Request) {
	defer func() {

		if r := recover(); r != nil {
			fmt.Println("panic upload error")
			reqUploadErrCount.Inc(1)
		}
	}()

	reqUploadCount.Inc(1)
	//atomic.AddInt64(&reqUploadCount,1)
	partIndex := req.FormValue(paramPartIndex)
	if len(partIndex) == 0 {
		srv.singleFile(w,req)
		return
	}
	srv.multiFile(w,req)
	return
}

func (srv *HttpServer)ChunksDoneHandler(w http.ResponseWriter, req *http.Request) {

	defer func() {

		if r := recover(); r != nil {
			fmt.Println("panic upload done error")
			reqUploadDoneErrCount.Inc(1)
		}
	}()

	if req.Method != http.MethodPost {
		errorMsg := fmt.Sprintf("Method [%s] is not supported", req.Method)
		http.Error(w, errorMsg, http.StatusMethodNotAllowed)
	}
	reqUploadDoneCount.Inc(1)
	uuid := req.FormValue(paramUuid)
	filename := req.FormValue(paramFileName)

	if uuid == ""  || filename == ""{
		reqUploadDoneParamErrCount.Inc(1)
		return
	}

	var objFile gfsFile
	srv.EnsureConnected()
	err := srv.gridFs.Files.Find(bson.M{ "filename": bson.RegEx{fmt.Sprintf("%s#%s", uuid,filename), "i"}}).One(&objFile)
	//count,err := gridFs.Files.Find(bson.M{ "filename": bson.RegEx{fmt.Sprintf("%s", uuid), "i"}}).Count()
	if err != nil {
		DBErrCount.Inc(1)
		reqUploadDoneErrCount.Inc(1)
	}

	err = srv.gridFs.Files.Update(bson.M{"_id":objFile.Id}, bson.M{"$set": bson.M{ "uploadDate":  bson.Now(), }})
	if err != nil {
		DBErrCount.Inc(1)
		reqUploadDoneErrCount.Inc(1)
	}
	reqUploadDoneOkCount.Inc(1)

}

func (srv *HttpServer)writeUploadResponse(w http.ResponseWriter, err error) {
	uploadResponse := new(UploadResponse)
	if err != nil {
		uploadResponse.Error = err.Error()
	} else {
		uploadResponse.Success = true
	}
	w.Header().Set("Content-Type", "text/plain")
	json.NewEncoder(w).Encode(uploadResponse)
}
<|endoftext|>


<commit_before>package server

import (
	"context"
	"html/template"
	"net/http"
	"os"
	"os/signal"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/jingweno/jqplay/config"
	"github.com/jingweno/jqplay/server/middleware"
	"gopkg.in/gin-gonic/gin.v1"
)

func New(c *config.Config) *Server {
	return &Server{c}
}

type Server struct {
	Config *config.Config
}

func (s *Server) Start() error {
	stop := make(chan os.Signal)
	signal.Notify(stop, os.Interrupt)

	db, err := ConnectDB(s.Config.DatabaseURL)
	if err != nil {
		return err
	}

	h := &JQHandler{Config: s.Config, DB: db}

	tmpl := template.New("index.tmpl")
	tmpl.Delims("#{", "}")
	tmpl, err = tmpl.ParseFiles("public/index.tmpl")
	if err != nil {
		log.Fatal(err)
	}

	router := gin.New()
	router.Use(
		middleware.Secure(s.Config.IsProd()),
		middleware.RequestID(),
		middleware.Logger(),
		gin.Recovery(),
	)
	router.SetHTMLTemplate(tmpl)

	router.Static("/js", "public/js")
	router.Static("/css", "public/css")
	router.Static("/images", "public/images")
	router.Static("/fonts", "public/bower_components/bootstrap/dist/fonts")
	router.StaticFile("/worker-xquery.js", "public/bower_components/ace-builds/src-min-noconflict/worker-xquery.js")
	router.StaticFile("/robots.txt", "public/robots.txt")

	router.GET("/", h.handleIndex)
	router.GET("/jq", h.handleJqGet)
	router.POST("/jq", h.handleJqPost)
	router.POST("/s", h.handleJqSharePost)
	router.GET("/s/:id", h.handleJqShareGet)

	srv := &http.Server{
		Addr:         ":" + s.Config.Port,
		ReadTimeout:  28 * time.Second,
		WriteTimeout: 28 * time.Second,
		Handler:      router,
	}

	go func() {
		if err := srv.ListenAndServe(); err != nil {
			log.WithError(err).Fatal("error starting server")
		}
	}()

	<-stop
	log.Println("\nShutting down the server...")
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	return srv.Shutdown(ctx)
}
<commit_msg>Adjust read/write timeout total to be 30 sec<commit_after>package server

import (
	"context"
	"html/template"
	"net/http"
	"os"
	"os/signal"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/jingweno/jqplay/config"
	"github.com/jingweno/jqplay/server/middleware"
	"gopkg.in/gin-gonic/gin.v1"
)

func New(c *config.Config) *Server {
	return &Server{c}
}

type Server struct {
	Config *config.Config
}

func (s *Server) Start() error {
	stop := make(chan os.Signal)
	signal.Notify(stop, os.Interrupt)

	db, err := ConnectDB(s.Config.DatabaseURL)
	if err != nil {
		return err
	}

	h := &JQHandler{Config: s.Config, DB: db}

	tmpl := template.New("index.tmpl")
	tmpl.Delims("#{", "}")
	tmpl, err = tmpl.ParseFiles("public/index.tmpl")
	if err != nil {
		log.Fatal(err)
	}

	router := gin.New()
	router.Use(
		middleware.Secure(s.Config.IsProd()),
		middleware.RequestID(),
		middleware.Logger(),
		gin.Recovery(),
	)
	router.SetHTMLTemplate(tmpl)

	router.Static("/js", "public/js")
	router.Static("/css", "public/css")
	router.Static("/images", "public/images")
	router.Static("/fonts", "public/bower_components/bootstrap/dist/fonts")
	router.StaticFile("/worker-xquery.js", "public/bower_components/ace-builds/src-min-noconflict/worker-xquery.js")
	router.StaticFile("/robots.txt", "public/robots.txt")

	router.GET("/", h.handleIndex)
	router.GET("/jq", h.handleJqGet)
	router.POST("/jq", h.handleJqPost)
	router.POST("/s", h.handleJqSharePost)
	router.GET("/s/:id", h.handleJqShareGet)

	srv := &http.Server{
		Addr:         ":" + s.Config.Port,
		ReadTimeout:  15 * time.Second,
		WriteTimeout: 15 * time.Second,
		Handler:      router,
	}

	go func() {
		if err := srv.ListenAndServe(); err != nil {
			log.WithError(err).Fatal("error starting server")
		}
	}()

	<-stop
	log.Println("\nShutting down the server...")
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	return srv.Shutdown(ctx)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/gophergala2016/3wordgame/validation"
	"net"
)

// Client struct
type Client struct {
	incoming chan Message
	outgoing chan string
	reader *bufio.Reader
	writer *bufio.Writer
	address string
}

type Message struct {
	text string
	address string
}

// Read line by line into the client.incoming
func (client *Client) Read() {
	for {
		line, _ := client.reader.ReadString('\n')
		client.incoming <- Message{
				text: line,
				address: client.address,
			}
	}
}

// Write client outgoing data to the client writer
func (client *Client) Write() {
	for data := range client.outgoing {
		client.writer.WriteString(data)
		client.writer.Flush()
	}
}

// Listen for reads and writes on the client
func (client *Client) Listen() {
	go client.Read()
	go client.Write()
}

// NewClient returns new instance of client.
func NewClient(connection net.Conn) *Client {
	writer := bufio.NewWriter(connection)
	reader := bufio.NewReader(connection)

	address := connection.RemoteAddr().String()

	client := &Client{
		incoming: make(chan Message),
		outgoing: make(chan string),
		reader:   reader,
		writer:   writer,
		address: address,
	}

	client.Listen()

	return client
}

// ChatRoom struct
type ChatRoom struct {
	clients  []*Client
	joins    chan net.Conn
	incoming chan Message
	outgoing chan string
	last_msg_user_address string
}

// Broadcast data to all connected chatRoom.clients
func (chatRoom *ChatRoom) Broadcast(data string) {
	for _, client := range chatRoom.clients {
		client.outgoing <- data
	}
}

// Join attaches a new client to the chatRoom clients
func (chatRoom *ChatRoom) Join(connection net.Conn) {
	client := NewClient(connection)
	chatRoom.clients = append(chatRoom.clients, client)
	go func() {
		for {
			chatRoom.incoming <- <-client.incoming
		}
	}()
}

// Listen to all incoming messages for the chatRoom
func (chatRoom *ChatRoom) Listen() {
	go func() {
		for {
			select {
			case data := <-chatRoom.incoming:
				msg, err := validation.ValidateMsg(data.text)
				if err == nil && chatRoom.last_msg_user_address != data.address {
					chatRoom.Broadcast(msg)
					chatRoom.last_msg_user_address = data.address
				}
			case conn := <-chatRoom.joins:
				chatRoom.Join(conn)
			}
		}
	}()
}

// NewChatRoom factories a ChatRoom instance
func NewChatRoom() *ChatRoom {
	chatRoom := &ChatRoom{
		clients:  make([]*Client, 0),
		joins:    make(chan net.Conn),
		incoming: make(chan Message),
		outgoing: make(chan string),
	}

	chatRoom.Listen()

	return chatRoom
}

func main() {
	var server string
	var port int

	flag.StringVar(&server, "server", "127.0.0.1", "Server host")
	flag.IntVar(&port, "port", 6666, "Server port")
	flag.Parse()

	listener, _ := net.Listen("tcp", fmt.Sprintf("%s:%d", server, port))

	chatRoom := NewChatRoom()

	for {
		conn, _ := listener.Accept()
		chatRoom.joins <- conn
	}
}
<commit_msg>Track full story in the chatroom<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/gophergala2016/3wordgame/validation"
	"net"
)

// Client struct
type Client struct {
	incoming chan Message
	outgoing chan string
	reader *bufio.Reader
	writer *bufio.Writer
	address string
}

type Message struct {
	text string
	address string
}

// Read line by line into the client.incoming
func (client *Client) Read() {
	for {
		line, _ := client.reader.ReadString('\n')
		client.incoming <- Message{
				text: line,
				address: client.address,
			}
	}
}

// Write client outgoing data to the client writer
func (client *Client) Write() {
	for data := range client.outgoing {
		client.writer.WriteString(data)
		client.writer.Flush()
	}
}

// Listen for reads and writes on the client
func (client *Client) Listen() {
	go client.Read()
	go client.Write()
}

// NewClient returns new instance of client.
func NewClient(connection net.Conn) *Client {
	writer := bufio.NewWriter(connection)
	reader := bufio.NewReader(connection)

	address := connection.RemoteAddr().String()

	client := &Client{
		incoming: make(chan Message),
		outgoing: make(chan string),
		reader:   reader,
		writer:   writer,
		address: address,
	}

	client.Listen()

	return client
}

// ChatRoom struct
type ChatRoom struct {
	clients  []*Client
	joins    chan net.Conn
	incoming chan Message
	outgoing chan string
	story string
	last_msg_user_address string
}

// Broadcast data to all connected chatRoom.clients
func (chatRoom *ChatRoom) Broadcast(data string) {
	for _, client := range chatRoom.clients {
		client.outgoing <- data
	}
}

// Join attaches a new client to the chatRoom clients
func (chatRoom *ChatRoom) Join(connection net.Conn) {
	client := NewClient(connection)
	chatRoom.clients = append(chatRoom.clients, client)
	go func() {
		for {
			chatRoom.incoming <- <-client.incoming
		}
	}()
}

// Listen to all incoming messages for the chatRoom
func (chatRoom *ChatRoom) Listen() {
	go func() {
		for {
			select {
			case data := <-chatRoom.incoming:
				msg, err := validation.ValidateMsg(data.text)
				if err == nil && chatRoom.last_msg_user_address != data.address {
					chatRoom.Broadcast(msg)
					chatRoom.story = fmt.Sprintf("%s %s", chatRoom.story, msg)
					chatRoom.last_msg_user_address = data.address
				}
			case conn := <-chatRoom.joins:
				chatRoom.Join(conn)
			}
		}
	}()
}

// NewChatRoom factories a ChatRoom instance
func NewChatRoom() *ChatRoom {
	chatRoom := &ChatRoom{
		clients:  make([]*Client, 0),
		joins:    make(chan net.Conn),
		incoming: make(chan Message),
		outgoing: make(chan string),
	}

	chatRoom.Listen()

	return chatRoom
}

func main() {
	var server string
	var port int

	flag.StringVar(&server, "server", "127.0.0.1", "Server host")
	flag.IntVar(&port, "port", 6666, "Server port")
	flag.Parse()

	listener, _ := net.Listen("tcp", fmt.Sprintf("%s:%d", server, port))

	chatRoom := NewChatRoom()

	for {
		conn, _ := listener.Accept()
		chatRoom.joins <- conn
	}
}
<|endoftext|>


<commit_before>package server

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"strings"
	"sync"

	"emperror.dev/errors"
	"github.com/apex/log"
	"github.com/creasty/defaults"
	"golang.org/x/sync/semaphore"

	"github.com/pterodactyl/wings/config"
	"github.com/pterodactyl/wings/environment"
	"github.com/pterodactyl/wings/environment/docker"
	"github.com/pterodactyl/wings/events"
	"github.com/pterodactyl/wings/remote"
	"github.com/pterodactyl/wings/server/filesystem"
	"github.com/pterodactyl/wings/system"
)

// Server is the high level definition for a server instance being controlled
// by Wings.
type Server struct {
	// Internal mutex used to block actions that need to occur sequentially, such as
	// writing the configuration to the disk.
	sync.RWMutex
	ctx       context.Context
	ctxCancel *context.CancelFunc

	emitterLock  sync.Mutex
	powerLock    *semaphore.Weighted
	throttleOnce sync.Once

	// Maintains the configuration for the server. This is the data that gets returned by the Panel
	// such as build settings and container images.
	cfg    Configuration
	client remote.Client

	// The crash handler for this server instance.
	crasher CrashHandler

	resources   ResourceUsage
	Environment environment.ProcessEnvironment `json:"-"`

	fs *filesystem.Filesystem

	// Events emitted by the server instance.
	emitter *events.EventBus

	// Defines the process configuration for the server instance. This is dynamically
	// fetched from the Pterodactyl Server instance each time the server process is
	// started, and then cached here.
	procConfig *remote.ProcessConfiguration

	// Tracks the installation process for this server and prevents a server from running
	// two installer processes at the same time. This also allows us to cancel a running
	// installation process, for example when a server is deleted from the panel while the
	// installer process is still running.
	installing   *system.AtomicBool
	transferring *system.AtomicBool
	restoring    *system.AtomicBool

	// The console throttler instance used to control outputs.
	throttler *ConsoleThrottler

	// Tracks open websocket connections for the server.
	wsBag       *WebsocketBag
	wsBagLocker sync.Mutex
}

// New returns a new server instance with a context and all of the default
// values set on the struct.
func New(client remote.Client) (*Server, error) {
	ctx, cancel := context.WithCancel(context.Background())
	s := Server{
		ctx:          ctx,
		ctxCancel:    &cancel,
		client:       client,
		installing:   system.NewAtomicBool(false),
		transferring: system.NewAtomicBool(false),
		restoring:    system.NewAtomicBool(false),
	}
	if err := defaults.Set(&s); err != nil {
		return nil, errors.Wrap(err, "server: could not set default values for struct")
	}
	if err := defaults.Set(&s.cfg); err != nil {
		return nil, errors.Wrap(err, "server: could not set defaults for server configuration")
	}
	s.resources.State = system.NewAtomicString(environment.ProcessOfflineState)
	return &s, nil
}

// ID returns the UUID for the server instance.
func (s *Server) ID() string {
	return s.Config().GetUuid()
}

// Cancels the context assigned to this server instance. Assuming background tasks
// are using this server's context for things, all of the background tasks will be
// stopped as a result.
func (s *Server) CtxCancel() {
	if s.ctxCancel != nil {
		(*s.ctxCancel)()
	}
}

// Returns a context instance for the server. This should be used to allow background
// tasks to be canceled if the server is removed. It will only be canceled when the
// application is stopped or if the server gets deleted.
func (s *Server) Context() context.Context {
	return s.ctx
}

// Returns all of the environment variables that should be assigned to a running
// server instance.
func (s *Server) GetEnvironmentVariables() []string {
	out := []string{
		fmt.Sprintf("TZ=%s", config.Get().System.Timezone),
		fmt.Sprintf("STARTUP=%s", s.Config().Invocation),
		fmt.Sprintf("SERVER_MEMORY=%d", s.MemoryLimit()),
		fmt.Sprintf("SERVER_IP=%s", s.Config().Allocations.DefaultMapping.Ip),
		fmt.Sprintf("SERVER_PORT=%d", s.Config().Allocations.DefaultMapping.Port),
	}

eloop:
	for k := range s.Config().EnvVars {
		// Don't allow any environment variables that we have already set above.
		for _, e := range out {
			if strings.HasPrefix(e, strings.ToUpper(k)+"=") {
				continue eloop
			}
		}

		out = append(out, fmt.Sprintf("%s=%s", strings.ToUpper(k), s.Config().EnvVars.Get(k)))
	}

	return out
}

func (s *Server) Log() *log.Entry {
	return log.WithField("server", s.ID())
}

// Sync syncs the state of the server on the Panel with Wings. This ensures that
// we're always using the state of the server from the Panel and allows us to
// not require successful API calls to Wings to do things.
//
// This also means mass actions can be performed against servers on the Panel
// and they will automatically sync with Wings when the server is started.
func (s *Server) Sync() error {
	cfg, err := s.client.GetServerConfiguration(s.Context(), s.ID())
	if err != nil {
		if err := remote.AsRequestError(err); err != nil && err.StatusCode() == http.StatusNotFound {
			return &serverDoesNotExist{}
		}
		return errors.WithStackIf(err)
	}
	return s.SyncWithConfiguration(cfg)
}

func (s *Server) SyncWithConfiguration(cfg remote.ServerConfigurationResponse) error {
	// Update the data structure and persist it to the disk.
	if err := s.UpdateDataStructure(cfg.Settings); err != nil {
		return err
	}

	s.Lock()
	s.procConfig = cfg.ProcessConfiguration
	s.Unlock()

	// Update the disk space limits for the server whenever the configuration
	// for it changes.
	s.fs.SetDiskLimit(s.DiskSpace())

	// If this is a Docker environment we need to sync the stop configuration with it so that
	// the process isn't just terminated when a user requests it be stopped.
	if e, ok := s.Environment.(*docker.Environment); ok {
		s.Log().Debug("syncing stop configuration with configured docker environment")
		e.SetImage(s.Config().Container.Image)
		e.SetStopConfiguration(cfg.ProcessConfiguration.Stop)
	}

	return nil
}

// Reads the log file for a server up to a specified number of bytes.
func (s *Server) ReadLogfile(len int) ([]string, error) {
	return s.Environment.Readlog(len)
}

// Determine if the server is bootable in it's current state or not. This will not
// indicate why a server is not bootable, only if it is.
func (s *Server) IsBootable() bool {
	exists, _ := s.Environment.Exists()

	return exists
}

// Initializes a server instance. This will run through and ensure that the environment
// for the server is setup, and that all of the necessary files are created.
func (s *Server) CreateEnvironment() error {
	// Ensure the data directory exists before getting too far through this process.
	if err := s.EnsureDataDirectoryExists(); err != nil {
		return err
	}

	return s.Environment.Create()
}

// Checks if the server is marked as being suspended or not on the system.
func (s *Server) IsSuspended() bool {
	return s.Config().Suspended
}

func (s *Server) ProcessConfiguration() *remote.ProcessConfiguration {
	s.RLock()
	defer s.RUnlock()

	return s.procConfig
}

// Filesystem returns an instance of the filesystem for this server.
func (s *Server) Filesystem() *filesystem.Filesystem {
	return s.fs
}

// EnsureDataDirectoryExists ensures that the data directory for the server
// instance exists.
func (s *Server) EnsureDataDirectoryExists() error {
	if _, err := os.Lstat(s.fs.Path()); err != nil {
		if os.IsNotExist(err) {
			s.Log().Debug("server: creating root directory and setting permissions")
			if err := os.MkdirAll(s.fs.Path(), 0700); err != nil {
				return errors.WithStack(err)
			}
			if err := s.fs.Chown("/"); err != nil {
				s.Log().WithField("error", err).Warn("server: failed to chown server data directory")
			}
		} else {
			return errors.WrapIf(err, "server: failed to stat server root directory")
		}
	}
	return nil
}

// OnStateChange sets the state of the server internally. This function handles crash detection as
// well as reporting to event listeners for the server.
func (s *Server) OnStateChange() {
	prevState := s.resources.State.Load()

	st := s.Environment.State()
	// Update the currently tracked state for the server.
	s.resources.State.Store(st)

	// Emit the event to any listeners that are currently registered.
	if prevState != s.Environment.State() {
		s.Log().WithField("status", st).Debug("saw server status change event")
		s.Events().Publish(StatusEvent, st)
	}

	// Reset the resource usage to 0 when the process fully stops so that all the UI
	// views in the Panel correctly display 0.
	if st == environment.ProcessOfflineState {
		s.resources.Reset()
		s.emitProcUsage()
	}

	// If server was in an online state, and is now in an offline state we should handle
	// that as a crash event. In that scenario, check the last crash time, and the crash
	// counter.
	//
	// In the event that we have passed the thresholds, don't do anything, otherwise
	// automatically attempt to start the process back up for the user. This is done in a
	// separate thread as to not block any actions currently taking place in the flow
	// that called this function.
	if (prevState == environment.ProcessStartingState || prevState == environment.ProcessRunningState) && s.Environment.State() == environment.ProcessOfflineState {
		s.Log().Info("detected server as entering a crashed state; running crash handler")

		go func(server *Server) {
			if err := server.handleServerCrash(); err != nil {
				if IsTooFrequentCrashError(err) {
					server.Log().Info("did not restart server after crash; occurred too soon after the last")
				} else {
					s.PublishConsoleOutputFromDaemon("Server crash was detected but an error occurred while handling it.")
					server.Log().WithField("error", err).Error("failed to handle server crash")
				}
			}
		}(s)
	}
}

// IsRunning determines if the server state is running or not. This is different
// from the environment state, it is simply the tracked state from this daemon
// instance, and not the response from Docker.
func (s *Server) IsRunning() bool {
	st := s.Environment.State()

	return st == environment.ProcessRunningState || st == environment.ProcessStartingState
}

// APIResponse is a type returned when requesting details about a single server
// instance on Wings. This includes the information needed by the Panel in order
// to show resource utilization and the current state on this system.
type APIResponse struct {
	State         string        `json:"state"`
	IsSuspended   bool          `json:"is_suspended"`
	Utilization   ResourceUsage `json:"utilization"`
	Configuration Configuration `json:"configuration"`
}

// ToAPIResponse returns the server struct as an API object that can be consumed
// by callers.
func (s *Server) ToAPIResponse() APIResponse {
	return APIResponse{
		State:         s.Environment.State(),
		IsSuspended:   s.IsSuspended(),
		Utilization:   s.Proc(),
		Configuration: *s.Config(),
	}
}
<commit_msg>Perhaps don't break _everything_ on people.<commit_after>package server

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"strings"
	"sync"

	"emperror.dev/errors"
	"github.com/apex/log"
	"github.com/creasty/defaults"
	"golang.org/x/sync/semaphore"

	"github.com/pterodactyl/wings/config"
	"github.com/pterodactyl/wings/environment"
	"github.com/pterodactyl/wings/environment/docker"
	"github.com/pterodactyl/wings/events"
	"github.com/pterodactyl/wings/remote"
	"github.com/pterodactyl/wings/server/filesystem"
	"github.com/pterodactyl/wings/system"
)

// Server is the high level definition for a server instance being controlled
// by Wings.
type Server struct {
	// Internal mutex used to block actions that need to occur sequentially, such as
	// writing the configuration to the disk.
	sync.RWMutex
	ctx       context.Context
	ctxCancel *context.CancelFunc

	emitterLock  sync.Mutex
	powerLock    *semaphore.Weighted
	throttleOnce sync.Once

	// Maintains the configuration for the server. This is the data that gets returned by the Panel
	// such as build settings and container images.
	cfg    Configuration
	client remote.Client

	// The crash handler for this server instance.
	crasher CrashHandler

	resources   ResourceUsage
	Environment environment.ProcessEnvironment `json:"-"`

	fs *filesystem.Filesystem

	// Events emitted by the server instance.
	emitter *events.EventBus

	// Defines the process configuration for the server instance. This is dynamically
	// fetched from the Pterodactyl Server instance each time the server process is
	// started, and then cached here.
	procConfig *remote.ProcessConfiguration

	// Tracks the installation process for this server and prevents a server from running
	// two installer processes at the same time. This also allows us to cancel a running
	// installation process, for example when a server is deleted from the panel while the
	// installer process is still running.
	installing   *system.AtomicBool
	transferring *system.AtomicBool
	restoring    *system.AtomicBool

	// The console throttler instance used to control outputs.
	throttler *ConsoleThrottler

	// Tracks open websocket connections for the server.
	wsBag       *WebsocketBag
	wsBagLocker sync.Mutex
}

// New returns a new server instance with a context and all of the default
// values set on the struct.
func New(client remote.Client) (*Server, error) {
	ctx, cancel := context.WithCancel(context.Background())
	s := Server{
		ctx:          ctx,
		ctxCancel:    &cancel,
		client:       client,
		installing:   system.NewAtomicBool(false),
		transferring: system.NewAtomicBool(false),
		restoring:    system.NewAtomicBool(false),
	}
	if err := defaults.Set(&s); err != nil {
		return nil, errors.Wrap(err, "server: could not set default values for struct")
	}
	if err := defaults.Set(&s.cfg); err != nil {
		return nil, errors.Wrap(err, "server: could not set defaults for server configuration")
	}
	s.resources.State = system.NewAtomicString(environment.ProcessOfflineState)
	return &s, nil
}

// ID returns the UUID for the server instance.
func (s *Server) ID() string {
	return s.Config().GetUuid()
}

// Id returns the UUID for the server instance. This function is deprecated
// in favor of Server.ID().
//
// Deprecated
func (s *Server) Id() string {
	return s.ID()
}

// Cancels the context assigned to this server instance. Assuming background tasks
// are using this server's context for things, all of the background tasks will be
// stopped as a result.
func (s *Server) CtxCancel() {
	if s.ctxCancel != nil {
		(*s.ctxCancel)()
	}
}

// Returns a context instance for the server. This should be used to allow background
// tasks to be canceled if the server is removed. It will only be canceled when the
// application is stopped or if the server gets deleted.
func (s *Server) Context() context.Context {
	return s.ctx
}

// Returns all of the environment variables that should be assigned to a running
// server instance.
func (s *Server) GetEnvironmentVariables() []string {
	out := []string{
		fmt.Sprintf("TZ=%s", config.Get().System.Timezone),
		fmt.Sprintf("STARTUP=%s", s.Config().Invocation),
		fmt.Sprintf("SERVER_MEMORY=%d", s.MemoryLimit()),
		fmt.Sprintf("SERVER_IP=%s", s.Config().Allocations.DefaultMapping.Ip),
		fmt.Sprintf("SERVER_PORT=%d", s.Config().Allocations.DefaultMapping.Port),
	}

eloop:
	for k := range s.Config().EnvVars {
		// Don't allow any environment variables that we have already set above.
		for _, e := range out {
			if strings.HasPrefix(e, strings.ToUpper(k)+"=") {
				continue eloop
			}
		}

		out = append(out, fmt.Sprintf("%s=%s", strings.ToUpper(k), s.Config().EnvVars.Get(k)))
	}

	return out
}

func (s *Server) Log() *log.Entry {
	return log.WithField("server", s.ID())
}

// Sync syncs the state of the server on the Panel with Wings. This ensures that
// we're always using the state of the server from the Panel and allows us to
// not require successful API calls to Wings to do things.
//
// This also means mass actions can be performed against servers on the Panel
// and they will automatically sync with Wings when the server is started.
func (s *Server) Sync() error {
	cfg, err := s.client.GetServerConfiguration(s.Context(), s.ID())
	if err != nil {
		if err := remote.AsRequestError(err); err != nil && err.StatusCode() == http.StatusNotFound {
			return &serverDoesNotExist{}
		}
		return errors.WithStackIf(err)
	}
	return s.SyncWithConfiguration(cfg)
}

func (s *Server) SyncWithConfiguration(cfg remote.ServerConfigurationResponse) error {
	// Update the data structure and persist it to the disk.
	if err := s.UpdateDataStructure(cfg.Settings); err != nil {
		return err
	}

	s.Lock()
	s.procConfig = cfg.ProcessConfiguration
	s.Unlock()

	// Update the disk space limits for the server whenever the configuration
	// for it changes.
	s.fs.SetDiskLimit(s.DiskSpace())

	// If this is a Docker environment we need to sync the stop configuration with it so that
	// the process isn't just terminated when a user requests it be stopped.
	if e, ok := s.Environment.(*docker.Environment); ok {
		s.Log().Debug("syncing stop configuration with configured docker environment")
		e.SetImage(s.Config().Container.Image)
		e.SetStopConfiguration(cfg.ProcessConfiguration.Stop)
	}

	return nil
}

// Reads the log file for a server up to a specified number of bytes.
func (s *Server) ReadLogfile(len int) ([]string, error) {
	return s.Environment.Readlog(len)
}

// Determine if the server is bootable in it's current state or not. This will not
// indicate why a server is not bootable, only if it is.
func (s *Server) IsBootable() bool {
	exists, _ := s.Environment.Exists()

	return exists
}

// Initializes a server instance. This will run through and ensure that the environment
// for the server is setup, and that all of the necessary files are created.
func (s *Server) CreateEnvironment() error {
	// Ensure the data directory exists before getting too far through this process.
	if err := s.EnsureDataDirectoryExists(); err != nil {
		return err
	}

	return s.Environment.Create()
}

// Checks if the server is marked as being suspended or not on the system.
func (s *Server) IsSuspended() bool {
	return s.Config().Suspended
}

func (s *Server) ProcessConfiguration() *remote.ProcessConfiguration {
	s.RLock()
	defer s.RUnlock()

	return s.procConfig
}

// Filesystem returns an instance of the filesystem for this server.
func (s *Server) Filesystem() *filesystem.Filesystem {
	return s.fs
}

// EnsureDataDirectoryExists ensures that the data directory for the server
// instance exists.
func (s *Server) EnsureDataDirectoryExists() error {
	if _, err := os.Lstat(s.fs.Path()); err != nil {
		if os.IsNotExist(err) {
			s.Log().Debug("server: creating root directory and setting permissions")
			if err := os.MkdirAll(s.fs.Path(), 0700); err != nil {
				return errors.WithStack(err)
			}
			if err := s.fs.Chown("/"); err != nil {
				s.Log().WithField("error", err).Warn("server: failed to chown server data directory")
			}
		} else {
			return errors.WrapIf(err, "server: failed to stat server root directory")
		}
	}
	return nil
}

// OnStateChange sets the state of the server internally. This function handles crash detection as
// well as reporting to event listeners for the server.
func (s *Server) OnStateChange() {
	prevState := s.resources.State.Load()

	st := s.Environment.State()
	// Update the currently tracked state for the server.
	s.resources.State.Store(st)

	// Emit the event to any listeners that are currently registered.
	if prevState != s.Environment.State() {
		s.Log().WithField("status", st).Debug("saw server status change event")
		s.Events().Publish(StatusEvent, st)
	}

	// Reset the resource usage to 0 when the process fully stops so that all the UI
	// views in the Panel correctly display 0.
	if st == environment.ProcessOfflineState {
		s.resources.Reset()
		s.emitProcUsage()
	}

	// If server was in an online state, and is now in an offline state we should handle
	// that as a crash event. In that scenario, check the last crash time, and the crash
	// counter.
	//
	// In the event that we have passed the thresholds, don't do anything, otherwise
	// automatically attempt to start the process back up for the user. This is done in a
	// separate thread as to not block any actions currently taking place in the flow
	// that called this function.
	if (prevState == environment.ProcessStartingState || prevState == environment.ProcessRunningState) && s.Environment.State() == environment.ProcessOfflineState {
		s.Log().Info("detected server as entering a crashed state; running crash handler")

		go func(server *Server) {
			if err := server.handleServerCrash(); err != nil {
				if IsTooFrequentCrashError(err) {
					server.Log().Info("did not restart server after crash; occurred too soon after the last")
				} else {
					s.PublishConsoleOutputFromDaemon("Server crash was detected but an error occurred while handling it.")
					server.Log().WithField("error", err).Error("failed to handle server crash")
				}
			}
		}(s)
	}
}

// IsRunning determines if the server state is running or not. This is different
// from the environment state, it is simply the tracked state from this daemon
// instance, and not the response from Docker.
func (s *Server) IsRunning() bool {
	st := s.Environment.State()

	return st == environment.ProcessRunningState || st == environment.ProcessStartingState
}

// APIResponse is a type returned when requesting details about a single server
// instance on Wings. This includes the information needed by the Panel in order
// to show resource utilization and the current state on this system.
type APIResponse struct {
	State         string        `json:"state"`
	IsSuspended   bool          `json:"is_suspended"`
	Utilization   ResourceUsage `json:"utilization"`
	Configuration Configuration `json:"configuration"`
}

// ToAPIResponse returns the server struct as an API object that can be consumed
// by callers.
func (s *Server) ToAPIResponse() APIResponse {
	return APIResponse{
		State:         s.Environment.State(),
		IsSuspended:   s.IsSuspended(),
		Utilization:   s.Proc(),
		Configuration: *s.Config(),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"log"
	"os/exec"
	"encoding/json"
	"bytes"
)

func checkError (err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func scriptHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	checkError(err)

	log.Println("METHOD: ", r.Method)
	log.Println("URL: ", r.URL)
	log.Println("FRAGMENT: ", r.URL.Fragment)
	log.Println("PATH: ", r.URL.Path)
	log.Println("BODY: ", r.Body)
	log.Println("URL2: ", r.Form)
	log.Println("FORM: ", r.Form.Get("url"))

	var outMsg bytes.Buffer
	var errMsg bytes.Buffer

	fname := ".." + r.URL.Path
	args, err := json.Marshal(r.Form)
	checkError(err)
	log.Printf("ARGS in Go side: %s", args)

	cmd := exec.Command(fname, string(args))
	cmd.Stdout = &outMsg
	cmd.Stderr = &errMsg

	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	}
	if err := cmd.Wait(); err != nil {
		log.Fatal(err)
	}

	log.Printf("OUTMSG: %s", outMsg.Bytes())
	log.Printf("ERRMSG: %s", errMsg.Bytes())
	fmt.Fprintf(w, string(outMsg.Bytes()))

}

func main() {
	relPath := ".."
	http.HandleFunc("/",         handler)
	http.Handle("/static/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/styles/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/javascript/",  http.FileServer(http.Dir(relPath)))
	http.HandleFunc("/scripts/", scriptHandler)
	err := http.ListenAndServe(":12345", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Minor addition of debugging code<commit_after>package main

import (
	"fmt"
	"net/http"
	"log"
	"os/exec"
	"encoding/json"
	"bytes"
)

func checkError (err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func scriptHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	checkError(err)

	log.Println("METHOD: ", r.Method)
	log.Println("URL: ", r.URL)
	log.Println("FRAGMENT: ", r.URL.Fragment)
	log.Println("PATH: ", r.URL.Path)
	log.Println("BODY: ", r.Body)
	log.Println("URL2: ", r.Form)
	log.Println("FORM: ", r.Form.Get("url"))

	var outMsg bytes.Buffer
	var errMsg bytes.Buffer

	fname := ".." + r.URL.Path
	args, err := json.Marshal(r.Form)
	checkError(err)
	log.Printf("ARGS in Go side: %s", args)

	log.Println("EXECUTING SCRIPT: ", fname)
	cmd := exec.Command(fname, string(args))
	cmd.Stdout = &outMsg
	cmd.Stderr = &errMsg

	if err := cmd.Start(); err != nil {
		log.Fatal("Error Starting Command: ", err)
	}
	if err := cmd.Wait(); err != nil {
		log.Fatal("Error Executing Command: ", err)
	}

	log.Printf("OUTMSG: %s", outMsg.Bytes())
	log.Printf("ERRMSG: %s", errMsg.Bytes())
	fmt.Fprintf(w, string(outMsg.Bytes()))

}

func main() {
	relPath := ".."
	http.HandleFunc("/",         handler)
	http.Handle("/static/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/styles/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/javascript/",  http.FileServer(http.Dir(relPath)))
	http.HandleFunc("/scripts/", scriptHandler)
	err := http.ListenAndServe(":12345", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"html/template"
	"net/http"
	"strconv"
	"strings"
)

func RootHandler(w http.ResponseWriter, r *http.Request, processes []*Process) {
	tmpl, err := template.ParseFiles("templates/root.html")
	if err != nil {
		fmt.Printf("Error parsing template")
	}
	if err := tmpl.Execute(w, processes); err != nil {
		fmt.Printf(err.Error())
		panic(err)
	}
}

func ElectionHandler(w http.ResponseWriter, r *http.Request, processes []*Process) {
	r.ParseForm()
	for key := range r.Form {
		processId, _ := strconv.ParseInt(key, 10, 0)
		processes[processId].God <- &Force{Election: &True}
		fmt.Fprintf(w, "Process %d forcing an election", processId)
	}
}

func LagHandler(w http.ResponseWriter, r *http.Request, processes []*Process) {
	if _, err := template.ParseFiles("templates/lag.html"); err != nil {
		http.Error(w, "Error parsing lag template", http.StatusInternalServerError)
	} else {
		fmt.Fprintf(w, "Adding lag")
	}
}

func NetworkSplitHandler(writer http.ResponseWriter, request *http.Request, processes []*Process) {
	if err := request.ParseForm(); err != nil {
		http.Error(
			writer,
			fmt.Sprintf("Error parsing split parameters. Err: %v", err),
			http.StatusInternalServerError,
		)
	}

	leftStr := request.FormValue("left")
	rightStr := request.FormValue("right")

	leftIdxStrs := strings.Split(leftStr, ",")
	rightIdxStrs := strings.Split(rightStr, ",")

	if len(leftIdxStrs)+len(rightIdxStrs) != len(processes) {
		http.Error(
			writer,
			fmt.Sprintf("Expected each process id to be either on left or right. Left: %v Right %v",
				leftStr, rightStr),
			http.StatusBadRequest,
		)
		return
	}

	leftIdxs := make([]int, len(leftIdxStrs))
	rightIdxs := make([]int, len(rightIdxStrs))

	var err error
	for idx, str := range leftIdxStrs {
		leftIdxs[idx], err = strconv.Atoi(str)
		if err != nil {
			http.Error(
				writer,
				fmt.Sprintf("Bad left input. Received: `%v`", leftStr),
				http.StatusBadRequest,
			)
			return
		}
	}

	for idx, str := range rightIdxStrs {
		rightIdxs[idx], err = strconv.Atoi(str)
		if err != nil {
			http.Error(
				writer,
				fmt.Sprintf("Bad right input. Received: `%v`", rightStr),
				http.StatusBadRequest,
			)
			return
		}
	}

	for _, left := range leftIdxs {
		for _, right := range rightIdxs {
			processes[left].NetworkState.Packetloss[right] = 100
			processes[right].NetworkState.Packetloss[left] = 100
		}
	}
}
<commit_msg>Better error checking<commit_after>package server

import (
	"fmt"
	"html/template"
	"net/http"
	"strconv"
	"strings"
)

func RootHandler(w http.ResponseWriter, r *http.Request, processes []*Process) {
	tmpl, err := template.ParseFiles("templates/root.html")
	if err != nil {
		fmt.Printf("Error parsing template")
	}
	if err := tmpl.Execute(w, processes); err != nil {
		fmt.Printf(err.Error())
		panic(err)
	}
}

func ElectionHandler(w http.ResponseWriter, r *http.Request, processes []*Process) {
	if err := r.ParseForm(); err != nil {
		http.Error(
			w,
			fmt.Sprintf("Error parsing election form. Err: %v", err),
			http.StatusInternalServerError,
		)
	}
	for key := range r.Form { // should only iterate once
		processId, _ := strconv.ParseInt(key, 10, 0)
		processes[processId].God <- &Force{Election: &True}
		fmt.Fprintf(w, "Process %d forcing an election", processId)
	}
}

func LagHandler(w http.ResponseWriter, r *http.Request, processes []*Process) {
	if _, err := template.ParseFiles("templates/lag.html"); err != nil {
		http.Error(w, "Error parsing lag template", http.StatusInternalServerError)
	} else {
		fmt.Fprintf(w, "Adding lag")
	}
}

func NetworkSplitHandler(writer http.ResponseWriter, request *http.Request, processes []*Process) {
	if err := request.ParseForm(); err != nil {
		http.Error(
			writer,
			fmt.Sprintf("Error parsing split parameters. Err: %v", err),
			http.StatusInternalServerError,
		)
	}

	leftStr := request.FormValue("left")
	rightStr := request.FormValue("right")

	leftIdxStrs := strings.Split(leftStr, ",")
	rightIdxStrs := strings.Split(rightStr, ",")

	if len(leftIdxStrs)+len(rightIdxStrs) != len(processes) {
		http.Error(
			writer,
			fmt.Sprintf("Expected each process id to be either on left or right. Left: %v Right %v",
				leftStr, rightStr),
			http.StatusBadRequest,
		)
		return
	}

	leftIdxs := make([]int, len(leftIdxStrs))
	rightIdxs := make([]int, len(rightIdxStrs))

	var err error
	for idx, str := range leftIdxStrs {
		leftIdxs[idx], err = strconv.Atoi(str)
		if err != nil {
			http.Error(
				writer,
				fmt.Sprintf("Bad left input. Received: `%v`", leftStr),
				http.StatusBadRequest,
			)
			return
		}
	}

	for idx, str := range rightIdxStrs {
		rightIdxs[idx], err = strconv.Atoi(str)
		if err != nil {
			http.Error(
				writer,
				fmt.Sprintf("Bad right input. Received: `%v`", rightStr),
				http.StatusBadRequest,
			)
			return
		}
	}

	for _, left := range leftIdxs {
		for _, right := range rightIdxs {
			processes[left].NetworkState.Packetloss[right] = 100
			processes[right].NetworkState.Packetloss[left] = 100
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach 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. See the AUTHORS file
// for names of contributors.
//
// Author: Daniel Theophanes (kardianos@gmail.com)

package cli

import (
	"flag"
	"reflect"
	"strings"

	"github.com/cockroachdb/cockroach/server"

	"github.com/spf13/cobra"
)

var maxResults int64

// pflagValue wraps flag.Value and implements the extra methods of the
// pflag.Value interface.
type pflagValue struct {
	flag.Value
}

func (v pflagValue) Type() string {
	t := reflect.TypeOf(v.Value).Elem()
	return t.Kind().String()
}

func (v pflagValue) IsBoolFlag() bool {
	t := reflect.TypeOf(v.Value).Elem()
	return t.Kind() == reflect.Bool
}

var flagUsage = map[string]string{
	"addr": `
        The host:port to bind for HTTP/RPC traffic.
`,
	"attrs": `
        An ordered, colon-separated list of node attributes. Attributes are
        arbitrary strings specifying topography or machine
        capabilities. Topography might include datacenter designation
        (e.g. "us-west-1a", "us-west-1b", "us-east-1c"). Machine capabilities
        might include specialized hardware or number of cores (e.g. "gpu",
        "x16c"). The relative geographic proximity of two nodes is inferred
        from the common prefix of the attributes list, so topographic
        attributes should be specified first and in the same order for all
        nodes. For example:

          --attrs=us-west-1b,gpu.
`,
	"cache-size": `
        Total size in bytes for caches, shared evenly if there are multiple
        storage devices.
`,
	"certs": `
        Directory containing RSA key and x509 certs. This flag is required if
        --insecure=false.
`,
	"gossip": `
        A comma-separated list of gossip addresses or resolvers for gossip
        bootstrap. Each item in the list has an optional type:
        [type=]<address>. An unspecified type means ip address or dns.
        Type is one of:
        - tcp: (default if type is omitted): plain ip address or hostname,
          or "self" for single-node systems.
        - unix: unix socket
        - lb: RPC load balancer fowarding to an arbitrary node
        - http-lb: HTTP load balancer: we query
          http(s)://<address>/_status/details/local
`,
	"gossip-interval": `
        Approximate interval (time.Duration) for gossiping new information to peers.
`,
	"key-size": `
        Key size in bits for CA/Node/Client certificates.
`,
	"linearizable": `
        Enables linearizable behaviour of operations on this node by making
        sure that no commit timestamp is reported back to the client until all
        other node clocks have necessarily passed it.
`,
	"dev": `
        Runs the node as a standalone in-memory cluster. Useful for developing
        Cockroach itself.
`,
	"insecure": `
        Run over plain HTTP. WARNING: this is strongly discouraged.
`,
	"max-offset": `
        The maximum clock offset for the cluster. Clock offset is measured on
        all node-to-node links and if any node notices it has clock offset in
        excess of --max-offset, it will commit suicide. Setting this value too
        high may decrease transaction performance in the presence of
        contention.
`,
	"metrics-frequency": `
        Adjust the frequency at which the server records its own internal metrics.
`,
	"scan-interval": `
        Adjusts the target for the duration of a single scan through a store's
        ranges. The scan is slowed as necessary to approximately achieve this
        duration.
`,
	"scan-max-idle-time": `
        Adjusts the max idle time of the scanner. This speeds up the scanner on small
        clusters to be more responsive.
`,
	"stores": `
        A comma-separated list of stores, specified by a colon-separated list
        of device attributes followed by '=' and either a filepath for a
        persistent store or an integer size in bytes for an in-memory
        store. Device attributes typically include whether the store is flash
        (ssd), spinny disk (hdd), fusion-io (fio), in-memory (mem); device
        attributes might also include speeds and other specs (7200rpm,
        200kiops, etc.). For example:

          --stores=hdd:7200rpm=/mnt/hda1,ssd=/mnt/ssd01,ssd=/mnt/ssd02,mem=1073741824.
`,
	"max-results": `
        Define the maximum number of results that will be retrieved.
`,
}

func normalizeStdFlagName(s string) string {
	return strings.Replace(s, "_", "-", -1)
}

// initFlags sets the server.Context values to flag values.
// Keep in sync with "server/context.go". Values in Context should be
// settable here.
func initFlags(ctx *server.Context) {
	// Map any flags registered in the standard "flag" package into the
	// top-level cockroach command.
	pf := cockroachCmd.PersistentFlags()
	flag.VisitAll(func(f *flag.Flag) {
		pf.Var(pflagValue{f.Value}, normalizeStdFlagName(f.Name), f.Usage)
	})

	{
		f := initCmd.Flags()
		f.StringVar(&ctx.Stores, "stores", ctx.Stores, flagUsage["stores"])
		if err := initCmd.MarkFlagRequired("stores"); err != nil {
			panic(err)
		}
	}

	{
		f := startCmd.Flags()
		f.BoolVar(&ctx.EphemeralSingleNode, "dev", ctx.EphemeralSingleNode, flagUsage["dev"])

		// Server flags.
		f.StringVar(&ctx.Addr, "addr", ctx.Addr, flagUsage["addr"])
		f.StringVar(&ctx.Attrs, "attrs", ctx.Attrs, flagUsage["attrs"])
		f.StringVar(&ctx.Stores, "stores", ctx.Stores, flagUsage["stores"])
		f.DurationVar(&ctx.MaxOffset, "max-offset", ctx.MaxOffset, flagUsage["max-offset"])
		f.DurationVar(&ctx.MetricsFrequency, "metrics-frequency", ctx.MetricsFrequency,
			flagUsage["metrics-frequency"])

		// Security flags.
		f.StringVar(&ctx.Certs, "certs", ctx.Certs, flagUsage["certs"])
		f.BoolVar(&ctx.Insecure, "insecure", ctx.Insecure, flagUsage["insecure"])

		// Gossip flags.
		f.StringVar(&ctx.GossipBootstrap, "gossip", ctx.GossipBootstrap, flagUsage["gossip"])
		f.DurationVar(&ctx.GossipInterval, "gossip-interval", ctx.GossipInterval,
			flagUsage["gossip-interval"])

		// KV flags.
		f.BoolVar(&ctx.Linearizable, "linearizable", ctx.Linearizable, flagUsage["linearizable"])

		// Engine flags.
		f.Int64Var(&ctx.CacheSize, "cache-size", ctx.CacheSize, flagUsage["cache-size"])
		f.DurationVar(&ctx.ScanInterval, "scan-interval", ctx.ScanInterval, flagUsage["scan-interval"])
		f.DurationVar(&ctx.ScanMaxIdleTime, "scan-max-idle-time", ctx.ScanMaxIdleTime,
			flagUsage["scan-max-idle-time"])

		if err := startCmd.MarkFlagRequired("gossip"); err != nil {
			panic(err)
		}
		if err := startCmd.MarkFlagRequired("stores"); err != nil {
			panic(err)
		}
	}

	{
		f := exterminateCmd.Flags()
		f.StringVar(&ctx.Stores, "stores", ctx.Stores, flagUsage["stores"])
		if err := exterminateCmd.MarkFlagRequired("stores"); err != nil {
			panic(err)
		}
	}

	for _, cmd := range certCmds {
		f := cmd.Flags()
		f.StringVar(&ctx.Certs, "certs", ctx.Certs, flagUsage["certs"])
		f.IntVar(&keySize, "key-size", defaultKeySize, flagUsage["key-size"])
		if err := cmd.MarkFlagRequired("certs"); err != nil {
			panic(err)
		}
		if err := cmd.MarkFlagRequired("key-size"); err != nil {
			panic(err)
		}
	}

	clientCmds := []*cobra.Command{
		sqlShellCmd, kvCmd, rangeCmd,
		acctCmd, permCmd, userCmd, zoneCmd,
		exterminateCmd, quitCmd, /* startCmd is covered above */
	}
	for _, cmd := range clientCmds {
		f := cmd.PersistentFlags()
		f.StringVar(&ctx.Addr, "addr", ctx.Addr, flagUsage["addr"])
		f.BoolVar(&ctx.Insecure, "insecure", ctx.Insecure, flagUsage["insecure"])
		f.StringVar(&ctx.Certs, "certs", ctx.Certs, flagUsage["certs"])
	}

	// Max results flag for scan, reverse scan, and range list.
	for _, cmd := range []*cobra.Command{scanCmd, reverseScanCmd, lsRangesCmd} {
		f := cmd.Flags()
		f.Int64Var(&maxResults, "max-results", 1000, flagUsage["max-results"])
	}
}

func init() {
	initFlags(Context)
}
<commit_msg>Allow `COCKROACH_DEV` to enable dev mode<commit_after>// Copyright 2015 The Cockroach 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. See the AUTHORS file
// for names of contributors.
//
// Author: Daniel Theophanes (kardianos@gmail.com)

package cli

import (
	"flag"
	"os"
	"reflect"
	"strings"

	"github.com/cockroachdb/cockroach/server"

	"github.com/spf13/cobra"
)

var maxResults int64

// pflagValue wraps flag.Value and implements the extra methods of the
// pflag.Value interface.
type pflagValue struct {
	flag.Value
}

func (v pflagValue) Type() string {
	t := reflect.TypeOf(v.Value).Elem()
	return t.Kind().String()
}

func (v pflagValue) IsBoolFlag() bool {
	t := reflect.TypeOf(v.Value).Elem()
	return t.Kind() == reflect.Bool
}

var flagUsage = map[string]string{
	"addr": `
        The host:port to bind for HTTP/RPC traffic.
`,
	"attrs": `
        An ordered, colon-separated list of node attributes. Attributes are
        arbitrary strings specifying topography or machine
        capabilities. Topography might include datacenter designation
        (e.g. "us-west-1a", "us-west-1b", "us-east-1c"). Machine capabilities
        might include specialized hardware or number of cores (e.g. "gpu",
        "x16c"). The relative geographic proximity of two nodes is inferred
        from the common prefix of the attributes list, so topographic
        attributes should be specified first and in the same order for all
        nodes. For example:

          --attrs=us-west-1b,gpu.
`,
	"cache-size": `
        Total size in bytes for caches, shared evenly if there are multiple
        storage devices.
`,
	"certs": `
        Directory containing RSA key and x509 certs. This flag is required if
        --insecure=false.
`,
	"gossip": `
        A comma-separated list of gossip addresses or resolvers for gossip
        bootstrap. Each item in the list has an optional type:
        [type=]<address>. An unspecified type means ip address or dns.
        Type is one of:
        - tcp: (default if type is omitted): plain ip address or hostname,
          or "self" for single-node systems.
        - unix: unix socket
        - lb: RPC load balancer fowarding to an arbitrary node
        - http-lb: HTTP load balancer: we query
          http(s)://<address>/_status/details/local
`,
	"gossip-interval": `
        Approximate interval (time.Duration) for gossiping new information to peers.
`,
	"key-size": `
        Key size in bits for CA/Node/Client certificates.
`,
	"linearizable": `
        Enables linearizable behaviour of operations on this node by making
        sure that no commit timestamp is reported back to the client until all
        other node clocks have necessarily passed it.
`,
	"dev": `
        Runs the node as a standalone in-memory cluster. Useful for developing
        Cockroach itself. Can also be enabled by running from an environment
        with COCKROACH_DEV=1.
`,
	"insecure": `
        Run over plain HTTP. WARNING: this is strongly discouraged.
`,
	"max-offset": `
        The maximum clock offset for the cluster. Clock offset is measured on
        all node-to-node links and if any node notices it has clock offset in
        excess of --max-offset, it will commit suicide. Setting this value too
        high may decrease transaction performance in the presence of
        contention.
`,
	"metrics-frequency": `
        Adjust the frequency at which the server records its own internal metrics.
`,
	"scan-interval": `
        Adjusts the target for the duration of a single scan through a store's
        ranges. The scan is slowed as necessary to approximately achieve this
        duration.
`,
	"scan-max-idle-time": `
        Adjusts the max idle time of the scanner. This speeds up the scanner on small
        clusters to be more responsive.
`,
	"stores": `
        A comma-separated list of stores, specified by a colon-separated list
        of device attributes followed by '=' and either a filepath for a
        persistent store or an integer size in bytes for an in-memory
        store. Device attributes typically include whether the store is flash
        (ssd), spinny disk (hdd), fusion-io (fio), in-memory (mem); device
        attributes might also include speeds and other specs (7200rpm,
        200kiops, etc.). For example:

          --stores=hdd:7200rpm=/mnt/hda1,ssd=/mnt/ssd01,ssd=/mnt/ssd02,mem=1073741824.
`,
	"max-results": `
        Define the maximum number of results that will be retrieved.
`,
}

func normalizeStdFlagName(s string) string {
	return strings.Replace(s, "_", "-", -1)
}

// initFlags sets the server.Context values to flag values.
// Keep in sync with "server/context.go". Values in Context should be
// settable here.
func initFlags(ctx *server.Context) {
	// Map any flags registered in the standard "flag" package into the
	// top-level cockroach command.
	pf := cockroachCmd.PersistentFlags()
	flag.VisitAll(func(f *flag.Flag) {
		pf.Var(pflagValue{f.Value}, normalizeStdFlagName(f.Name), f.Usage)
	})

	{
		f := initCmd.Flags()
		f.StringVar(&ctx.Stores, "stores", ctx.Stores, flagUsage["stores"])
		if err := initCmd.MarkFlagRequired("stores"); err != nil {
			panic(err)
		}
	}

	isEnvRoachDev := os.Getenv("COCKROACH_DEV") == "1"

	{
		f := startCmd.Flags()
		f.BoolVar(&ctx.EphemeralSingleNode, "dev", isEnvRoachDev, flagUsage["dev"])

		// Server flags.
		f.StringVar(&ctx.Addr, "addr", ctx.Addr, flagUsage["addr"])
		f.StringVar(&ctx.Attrs, "attrs", ctx.Attrs, flagUsage["attrs"])
		f.StringVar(&ctx.Stores, "stores", ctx.Stores, flagUsage["stores"])
		f.DurationVar(&ctx.MaxOffset, "max-offset", ctx.MaxOffset, flagUsage["max-offset"])
		f.DurationVar(&ctx.MetricsFrequency, "metrics-frequency", ctx.MetricsFrequency,
			flagUsage["metrics-frequency"])

		// Security flags.
		f.StringVar(&ctx.Certs, "certs", ctx.Certs, flagUsage["certs"])
		f.BoolVar(&ctx.Insecure, "insecure", ctx.Insecure, flagUsage["insecure"])

		// Gossip flags.
		f.StringVar(&ctx.GossipBootstrap, "gossip", ctx.GossipBootstrap, flagUsage["gossip"])
		f.DurationVar(&ctx.GossipInterval, "gossip-interval", ctx.GossipInterval,
			flagUsage["gossip-interval"])

		// KV flags.
		f.BoolVar(&ctx.Linearizable, "linearizable", ctx.Linearizable, flagUsage["linearizable"])

		// Engine flags.
		f.Int64Var(&ctx.CacheSize, "cache-size", ctx.CacheSize, flagUsage["cache-size"])
		f.DurationVar(&ctx.ScanInterval, "scan-interval", ctx.ScanInterval, flagUsage["scan-interval"])
		f.DurationVar(&ctx.ScanMaxIdleTime, "scan-max-idle-time", ctx.ScanMaxIdleTime,
			flagUsage["scan-max-idle-time"])

		if err := startCmd.MarkFlagRequired("gossip"); err != nil {
			panic(err)
		}
		if err := startCmd.MarkFlagRequired("stores"); err != nil {
			panic(err)
		}
	}

	{
		f := exterminateCmd.Flags()
		f.StringVar(&ctx.Stores, "stores", ctx.Stores, flagUsage["stores"])
		if err := exterminateCmd.MarkFlagRequired("stores"); err != nil {
			panic(err)
		}
	}

	for _, cmd := range certCmds {
		f := cmd.Flags()
		f.StringVar(&ctx.Certs, "certs", ctx.Certs, flagUsage["certs"])
		f.IntVar(&keySize, "key-size", defaultKeySize, flagUsage["key-size"])
		if err := cmd.MarkFlagRequired("certs"); err != nil {
			panic(err)
		}
		if err := cmd.MarkFlagRequired("key-size"); err != nil {
			panic(err)
		}
	}

	clientCmds := []*cobra.Command{
		sqlShellCmd, kvCmd, rangeCmd,
		acctCmd, permCmd, userCmd, zoneCmd,
		exterminateCmd, quitCmd, /* startCmd is covered above */
	}
	for _, cmd := range clientCmds {
		f := cmd.PersistentFlags()
		f.StringVar(&ctx.Addr, "addr", ctx.Addr, flagUsage["addr"])
		f.BoolVar(&ctx.Insecure, "insecure", ctx.Insecure, flagUsage["insecure"])
		f.StringVar(&ctx.Certs, "certs", ctx.Certs, flagUsage["certs"])
	}

	// Max results flag for scan, reverse scan, and range list.
	for _, cmd := range []*cobra.Command{scanCmd, reverseScanCmd, lsRangesCmd} {
		f := cmd.Flags()
		f.Int64Var(&maxResults, "max-results", 1000, flagUsage["max-results"])
	}
}

func init() {
	initFlags(Context)
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"net/http"

	log "github.com/sirupsen/logrus"
)

/**
* Main entrance into the server.
 */
func StartServer(serverPort string) {
	http.HandleFunc("/", viewHandler)
	err := http.ListenAndServe(":"+serverPort, nil)
	if err != nil {
		log.Fatal("ERROR: couldn't start server: ", err)
	} else {
		log.Info("Server started successfully")
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-type", "text/plain")

	fmt.Fprintf(w, "Hello world! This was pretty awesome! Hot-reloading rockssss!\n")
}
<commit_msg>lint warning for comment fixed<commit_after>package server

import (
	"fmt"
	"net/http"

	log "github.com/sirupsen/logrus"
)

/**StartServer is the main entrance into the server.*/
func StartServer(serverPort string) {
	http.HandleFunc("/", viewHandler)
	err := http.ListenAndServe(":"+serverPort, nil)
	if err != nil {
		log.Fatal("ERROR: couldn't start server: ", err)
	} else {
		log.Info("Server started successfully")
	}
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-type", "text/plain")

	fmt.Fprintf(w, "Hello world! This was pretty awesome! Hot-reloading rockssss!\n")
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"database/sql"
	"encoding/base64"
	"encoding/gob"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"

	"github.com/gorilla/securecookie"

	_ "github.com/go-sql-driver/mysql"

	"github.com/AscendTech4H/turtleauth"
)

// Credentials which stores google ids.
type Credentials struct {
	Cid     string `json:"client_id"`
	Csecret string `json:"client_secret"`
}

// User is a retrieved and authentiacted google user.
type User struct {
	Sub           string `json:"sub"`
	Name          string `json:"name"`
	GivenName     string `json:"given_name"`
	FamilyName    string `json:"family_name"`
	Profile       string `json:"profile"`
	Picture       string `json:"picture"`
	Email         string `json:"email"`
	EmailVerified string `json:"email_verified"`
	Gender        string `json:"gender"`
}

var cred Credentials
var conf *oauth2.Config
var state string

func init() {

}

func main() {
	log.Println("Starting auth server. . . ")
	var dbs string
	var addr string
	var k string
	var credfile string
	flag.StringVar(&dbs, "db", "/", "Database source string")
	flag.StringVar(&addr, "addr", ":9001", "Server port for authentication")
	flag.StringVar(&k, "key", ".authkey", "Key file")
	flag.StringVar(&credfile, "cred", ".cred", "Google oauth2 credentials file")
	flag.Parse()
	log.Println("Loading keys. . . ")
	d, err := ioutil.ReadFile(k)
	if err != nil {
		panic(err)
	}
	var keys struct {
		HashKey  []byte
		BlockKey []byte
	}
	err = json.Unmarshal(d, &keys)
	if err != nil {
		panic(err)
	}
	sc := securecookie.New(keys.HashKey, keys.BlockKey)
	sc.SetSerializer(securecookie.GobEncoder{})
	log.Println("Loading oauth credentials. . . ")
	file, err := ioutil.ReadFile(credfile)
	if err != nil {
		log.Printf("File error: %v\n", err)
		os.Exit(1)
	}
	json.Unmarshal(file, &cred)
	fmt.Println(cred)

	conf = &oauth2.Config{
		ClientID:     cred.Cid,
		ClientSecret: cred.Csecret,
		RedirectURL:  "https://auth.ascendtech4h.org/auth",
		Scopes: []string{
			"https://www.googleapis.com/auth/userinfo.email", // You have to select your own scope from here -> https://developers.google.com/identity/protocols/googlescopes#google_sign-in
		},
		Endpoint: google.Endpoint,
	}
	log.Println("Connecting to DB. . . ")
	db, err := sql.Open("mysql", "/")
	if err != nil {
		panic(err)
	}
	defer db.Close()
	log.Println("Connected")
	http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("pong"))
	})
	http.HandleFunc("/perm", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
		}
		var ac turtleauth.AuthCheck
		err := gob.NewDecoder(r.Body).Decode(&ac)
		if err != nil {
			http.Error(w, "Decode error", http.StatusInternalServerError)
			return
		}
		if !ac.P.Check() {
			http.Error(w, "Bad PermissionClass", http.StatusBadRequest)
			return
		}
		var uid int
		err = sc.Decode("turtleauth", ac.C.Value, &uid)
		if err != nil {
			http.Error(w, "Cookie error", http.StatusUnauthorized)
			return
		}
		res, err := db.Query(fmt.Sprintf("SELECT * FROM PERM_%s WHERE id=%d", ac.P.Name, uid))
		if err != nil {
			http.Error(w, "SQL Error", http.StatusInternalServerError)
			return
		}
		defer res.Close()
		if res.Next() {
			w.WriteHeader(http.StatusOK)
			w.Write([]byte("Auth successful"))
		} else {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Auth rejected"))
		}
	})
	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
			return
		}
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "Form parse error", http.StatusBadRequest)
			return
		}
		dest := r.FormValue("dest")
		if dest == "" {
			http.Error(w, "Missing destination", http.StatusBadRequest)
			return
		}
		expire := time.Now().Add(5 * time.Minute) //Delete cookie in 5 min
		http.SetCookie(w, &http.Cookie{
			Name:    "authdest",
			Value:   dest,
			Expires: expire,
		})
		key := make([]byte, 32)
		_, err = rand.Read(key)
		if err != nil {
			http.Error(w, "Random number generator failure", http.StatusInternalServerError)
			return
		}
		kstr := base64.StdEncoding.EncodeToString(key)
		cook := &http.Cookie{
			Name:    "state",
			Value:   kstr,
			Expires: expire,
		}
		http.SetCookie(w, cook)
		authaddr := conf.AuthCodeURL(kstr) + "?access_type=offline"
		http.Redirect(w, r, authaddr, http.StatusMovedPermanently)
	})
	http.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
			return
		}
		cook, err := r.Cookie("state")
		if err != nil {
			http.Error(w, "Error getting state cookie", http.StatusBadRequest)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:   "state",
			Value:  "",
			MaxAge: -1, //Delete now
		})
		dest, err := r.Cookie("authdest")
		if err != nil {
			http.Error(w, "Error retrieving authdest cookie", http.StatusBadRequest)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:   "authdest",
			Value:  "",
			MaxAge: -1, //Delete now
		})
		if r.URL.Query().Get("state") != cook.Value {
			http.Error(w, "Unmatched state", http.StatusUnauthorized)
			log.Println(r.URL.Query().Get("state"), cook.Value)
			return
		}
		tok, err := conf.Exchange(oauth2.NoContext, cook.Value)
		if err != nil {
			http.Error(w, "Exchange error", http.StatusUnauthorized)
			log.Printf("Exchange error: %s\n", err.Error())
		}
		client := conf.Client(oauth2.NoContext, tok)
		lookup, err := client.Get("https://www.googleapis.com/oauth2/v3/userinfo")
		if err != nil {
			http.Error(w, "User info request error", http.StatusUnauthorized)
			return
		}
		defer lookup.Body.Close()
		var uinfo User
		err = json.NewDecoder(lookup.Body).Decode(&uinfo)
		if err != nil {
			http.Error(w, "User info request decode error", http.StatusInternalServerError)
			return
		}
		res, err := db.Query("SELECT id FROM users WHERE email=?", uinfo.Email)
		if err != nil {
			http.Error(w, "Database errror", http.StatusInternalServerError)
			log.Printf("Database error: %s\n", err.Error())
			return
		}
		var id int
		if !res.Next() {
			_, err = db.Exec("INSERT INTO users (email) VALUES (?)", uinfo.Email)
			if err != nil {
				http.Error(w, "Database errror", http.StatusInternalServerError)
				log.Printf("Database error: %s\n", err.Error())
				return
			}
			res, err = db.Query("SELECT id FROM users WHERE email=?", uinfo.Email)
			if err != nil {
				http.Error(w, "Database errror", http.StatusInternalServerError)
				log.Printf("Database error: %s\n", err.Error())
				return
			}
		}
		err = res.Scan(&id)
		if err != nil {
			http.Error(w, "Database errror", http.StatusInternalServerError)
			log.Printf("Database error: %s\n", err.Error())
			return
		}
		cstr, err := sc.Encode("turtleauth", &id)
		if err != nil {
			http.Error(w, "Cookie encryption error", http.StatusInternalServerError)
			log.Printf("Cookie encryption error: %s\n", err.Error())
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:  "turtleauth",
			Value: cstr,
		})
		//All good - redirect
		http.Redirect(w, r, dest.Value, http.StatusMovedPermanently)
	})
	log.Println("Starting http server")
	panic(http.ListenAndServe(addr, nil))
}
<commit_msg>Fix query<commit_after>package main

import (
	"crypto/rand"
	"database/sql"
	"encoding/base64"
	"encoding/gob"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"

	"github.com/gorilla/securecookie"

	_ "github.com/go-sql-driver/mysql"

	"github.com/AscendTech4H/turtleauth"
)

// Credentials which stores google ids.
type Credentials struct {
	Cid     string `json:"client_id"`
	Csecret string `json:"client_secret"`
}

// User is a retrieved and authentiacted google user.
type User struct {
	Sub           string `json:"sub"`
	Name          string `json:"name"`
	GivenName     string `json:"given_name"`
	FamilyName    string `json:"family_name"`
	Profile       string `json:"profile"`
	Picture       string `json:"picture"`
	Email         string `json:"email"`
	EmailVerified string `json:"email_verified"`
	Gender        string `json:"gender"`
}

var cred Credentials
var conf *oauth2.Config
var state string

func init() {

}

func main() {
	log.Println("Starting auth server. . . ")
	var dbs string
	var addr string
	var k string
	var credfile string
	flag.StringVar(&dbs, "db", "/", "Database source string")
	flag.StringVar(&addr, "addr", ":9001", "Server port for authentication")
	flag.StringVar(&k, "key", ".authkey", "Key file")
	flag.StringVar(&credfile, "cred", ".cred", "Google oauth2 credentials file")
	flag.Parse()
	log.Println("Loading keys. . . ")
	d, err := ioutil.ReadFile(k)
	if err != nil {
		panic(err)
	}
	var keys struct {
		HashKey  []byte
		BlockKey []byte
	}
	err = json.Unmarshal(d, &keys)
	if err != nil {
		panic(err)
	}
	sc := securecookie.New(keys.HashKey, keys.BlockKey)
	sc.SetSerializer(securecookie.GobEncoder{})
	log.Println("Loading oauth credentials. . . ")
	file, err := ioutil.ReadFile(credfile)
	if err != nil {
		log.Printf("File error: %v\n", err)
		os.Exit(1)
	}
	json.Unmarshal(file, &cred)
	fmt.Println(cred)

	conf = &oauth2.Config{
		ClientID:     cred.Cid,
		ClientSecret: cred.Csecret,
		RedirectURL:  "https://auth.ascendtech4h.org/auth",
		Scopes: []string{
			"https://www.googleapis.com/auth/userinfo.email", // You have to select your own scope from here -> https://developers.google.com/identity/protocols/googlescopes#google_sign-in
		},
		Endpoint: google.Endpoint,
	}
	log.Println("Connecting to DB. . . ")
	db, err := sql.Open("mysql", "/")
	if err != nil {
		panic(err)
	}
	defer db.Close()
	log.Println("Connected")
	http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("pong"))
	})
	http.HandleFunc("/perm", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
		}
		var ac turtleauth.AuthCheck
		err := gob.NewDecoder(r.Body).Decode(&ac)
		if err != nil {
			http.Error(w, "Decode error", http.StatusInternalServerError)
			return
		}
		if !ac.P.Check() {
			http.Error(w, "Bad PermissionClass", http.StatusBadRequest)
			return
		}
		var uid int
		err = sc.Decode("turtleauth", ac.C.Value, &uid)
		if err != nil {
			http.Error(w, "Cookie error", http.StatusUnauthorized)
			return
		}
		res, err := db.Query(fmt.Sprintf("SELECT * FROM PERM_%s WHERE id=%d", ac.P.Name, uid))
		if err != nil {
			http.Error(w, "SQL Error", http.StatusInternalServerError)
			return
		}
		defer res.Close()
		if res.Next() {
			w.WriteHeader(http.StatusOK)
			w.Write([]byte("Auth successful"))
		} else {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte("Auth rejected"))
		}
	})
	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
			return
		}
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "Form parse error", http.StatusBadRequest)
			return
		}
		dest := r.FormValue("dest")
		if dest == "" {
			http.Error(w, "Missing destination", http.StatusBadRequest)
			return
		}
		expire := time.Now().Add(5 * time.Minute) //Delete cookie in 5 min
		http.SetCookie(w, &http.Cookie{
			Name:    "authdest",
			Value:   dest,
			Expires: expire,
		})
		key := make([]byte, 32)
		_, err = rand.Read(key)
		if err != nil {
			http.Error(w, "Random number generator failure", http.StatusInternalServerError)
			return
		}
		kstr := base64.StdEncoding.EncodeToString(key)
		cook := &http.Cookie{
			Name:    "state",
			Value:   kstr,
			Expires: expire,
		}
		http.SetCookie(w, cook)
		authaddr := conf.AuthCodeURL(kstr) + "&access_type=offline"
		http.Redirect(w, r, authaddr, http.StatusMovedPermanently)
	})
	http.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			http.Error(w, "Incorrect request method", http.StatusMethodNotAllowed)
			return
		}
		cook, err := r.Cookie("state")
		if err != nil {
			http.Error(w, "Error getting state cookie", http.StatusBadRequest)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:   "state",
			Value:  "",
			MaxAge: -1, //Delete now
		})
		dest, err := r.Cookie("authdest")
		if err != nil {
			http.Error(w, "Error retrieving authdest cookie", http.StatusBadRequest)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:   "authdest",
			Value:  "",
			MaxAge: -1, //Delete now
		})
		if r.URL.Query().Get("state") != cook.Value {
			http.Error(w, "Unmatched state", http.StatusUnauthorized)
			log.Println(r.URL.Query().Get("state"), cook.Value)
			return
		}
		tok, err := conf.Exchange(oauth2.NoContext, cook.Value)
		if err != nil {
			http.Error(w, "Exchange error", http.StatusUnauthorized)
			log.Printf("Exchange error: %s\n", err.Error())
		}
		client := conf.Client(oauth2.NoContext, tok)
		lookup, err := client.Get("https://www.googleapis.com/oauth2/v3/userinfo")
		if err != nil {
			http.Error(w, "User info request error", http.StatusUnauthorized)
			return
		}
		defer lookup.Body.Close()
		var uinfo User
		err = json.NewDecoder(lookup.Body).Decode(&uinfo)
		if err != nil {
			http.Error(w, "User info request decode error", http.StatusInternalServerError)
			return
		}
		res, err := db.Query("SELECT id FROM users WHERE email=?", uinfo.Email)
		if err != nil {
			http.Error(w, "Database errror", http.StatusInternalServerError)
			log.Printf("Database error: %s\n", err.Error())
			return
		}
		var id int
		if !res.Next() {
			_, err = db.Exec("INSERT INTO users (email) VALUES (?)", uinfo.Email)
			if err != nil {
				http.Error(w, "Database errror", http.StatusInternalServerError)
				log.Printf("Database error: %s\n", err.Error())
				return
			}
			res, err = db.Query("SELECT id FROM users WHERE email=?", uinfo.Email)
			if err != nil {
				http.Error(w, "Database errror", http.StatusInternalServerError)
				log.Printf("Database error: %s\n", err.Error())
				return
			}
		}
		err = res.Scan(&id)
		if err != nil {
			http.Error(w, "Database errror", http.StatusInternalServerError)
			log.Printf("Database error: %s\n", err.Error())
			return
		}
		cstr, err := sc.Encode("turtleauth", &id)
		if err != nil {
			http.Error(w, "Cookie encryption error", http.StatusInternalServerError)
			log.Printf("Cookie encryption error: %s\n", err.Error())
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:  "turtleauth",
			Value: cstr,
		})
		//All good - redirect
		http.Redirect(w, r, dest.Value, http.StatusMovedPermanently)
	})
	log.Println("Starting http server")
	panic(http.ListenAndServe(addr, nil))
}
<|endoftext|>