<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/fasthttp"
	"github.com/ziutek/telnet"
)

type fusionResponse struct {
	// Page   int      `json:"page"`
	API_Rooms []fusionRoom
}

type fusionRoom struct {
	RoomID   string
	RoomName string
	Symbols  []fusionSymbol
}

type fusionSymbol struct
	ProcessorName string
	ConnectInfo   string
}

type room struct {
	Hostname string
	Address  string
}

func checkErr(err error) {
	if err != nil {
		panic(err) // Don't forget your towel
	}
}

func health(c echo.Context) error {
	return c.String(http.StatusOK, "Uh, we had a slight weapons malfunction, but uh... everything's perfectly all right now. We're fine. We're all fine here now, thank you. How are you?")
}

func getTelnetOutput(address string, port string, command string) string {
	t, err := telnet.Dial("tcp", address+":"+port)
	checkErr(err)

	t.SetUnixWriteMode(true) // Convert any '\n' (LF) to '\r\n' (CR LF)

	command = command + "\nhostname" // Send two commands so we get a second prompt to use as a delimiter
	buf := make([]byte, len(command)+1)
	copy(buf, command)
	buf[len(command)] = '\n'
	_, err = t.Write(buf)
	checkErr(err)

	t.SkipUntil("TSW-750>") // Skip to the first prompt delimiter
	var output []byte
	output, err = t.ReadUntil("TSW-750>") // Read until the second prompt delimiter (provided by sending two commands in sendCommand)
	checkErr(err)

	t.Close() // Close the telnet session

	output = output[:len(output)-10] // Ghetto trim the prompt off the response

	return string(output)
}

func fusionRequest(requestType string, url string) []byte {
	client := &http.Client{}
	req, err := http.NewRequest(requestType, url, nil)
	checkErr(err)

	req.Header.Add("Content-Type", "application/json")
	resp, err := client.Do(req)
	checkErr(err)

	body, err := ioutil.ReadAll(resp.Body)
	checkErr(err)

	return body
}

func getRooms(c echo.Context) error {
	response := fusionRequest("GET", "http://lazyeye.byu.edu/fusion/apiservice/rooms/")
	return c.String(http.StatusOK, string(response)) // MAKE SURE YOU HAVE THE TRAILING SLASH
}

func getRoomByName(c echo.Context) error {
	// Get the room's ID from its name
	response := fusionRequest("GET", "http://lazyeye.byu.edu/fusion/apiservice/rooms/?search="+c.Param("room"))
	rooms := fusionResponse{}
	err := json.Unmarshal(response, &rooms)
	checkErr(err)

	// Get info about the room using its ID
	response = fusionRequest("GET", "http://lazyeye.byu.edu/fusion/apiservice/rooms/"+rooms.API_Rooms[0].RoomID)
	rooms = fusionResponse{}
	err = json.Unmarshal(response, &rooms)
	checkErr(err)

	hostname := rooms.API_Rooms[0].Symbols[0].ProcessorName
	address := rooms.API_Rooms[0].Symbols[0].ConnectInfo

	roomResponse := room{Hostname: hostname, Address: address}

	jsonResponse, _ := json.Marshal(roomResponse)
	return c.String(http.StatusOK, string(jsonResponse))
}

func main() {
	port := ":8000"
	e := echo.New()

	// Echo doesn't like doing things "magically" which means it won't auto-redirect endpoints without a trailing slash to one with a trailing slash (and vice versa) which is why endpoints are duplicated
	e.Get("/health", health)
	e.Get("/health/", health)

	e.Get("/rooms", getRooms)
	e.Get("/rooms/", getRooms)
	e.Get("/rooms/:room", getRoomByName)
	e.Get("/rooms/:room/", getRoomByName)
	// e.Get("/buildings", ...)
	// e.Get("/buildings/:building", ...)
	// e.Get("/buildings/:building/room", ...)
	// e.Get("/buildings/:building/rooms/:room", ...)
	// e.Get("/buildings/:building/rooms/:room/signals", ...)
	// e.Get("/buildings/:building/rooms/:room/signals/:signal", ...)
	//
	// e.Post("/rooms", ...)
	// e.Post("/buildings", ...)
	// e.Post("/buildings/:building/rooms/:room/signals", ...)
	//
	// e.Put("/rooms/:room", ...)
	// e.Put("/buildings/:building", ...)
	// e.Put("/buildings/:building/rooms/:room", ...)
	// e.Put("/buildings/:building/rooms/:room/signals/:signal", ...)
	//
	// e.Delete("/rooms/:room", ...)
	// e.Delete("/buildings/:building", ...)
	// e.Delete("/buildings/:building/rooms/:room", ...)
	// e.Delete("/buildings/:building/rooms/:room/signals/:signal", ...)

	fmt.Printf("AV API is listening on %s\n", port)
	e.Run(fasthttp.New(port))
}
<commit_msg>Fixing the thing I broke for Jenkins testing<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/fasthttp"
	"github.com/ziutek/telnet"
)

type fusionResponse struct {
	// Page   int      `json:"page"`
	API_Rooms []fusionRoom
}

type fusionRoom struct {
	RoomID   string
	RoomName string
	Symbols  []fusionSymbol
}

type fusionSymbol struct {
	ProcessorName string
	ConnectInfo   string
}

type room struct {
	Hostname string
	Address  string
}

func checkErr(err error) {
	if err != nil {
		panic(err) // Don't forget your towel
	}
}

func health(c echo.Context) error {
	return c.String(http.StatusOK, "Uh, we had a slight weapons malfunction, but uh... everything's perfectly all right now. We're fine. We're all fine here now, thank you. How are you?")
}

func getTelnetOutput(address string, port string, command string) string {
	t, err := telnet.Dial("tcp", address+":"+port)
	checkErr(err)

	t.SetUnixWriteMode(true) // Convert any '\n' (LF) to '\r\n' (CR LF)

	command = command + "\nhostname" // Send two commands so we get a second prompt to use as a delimiter
	buf := make([]byte, len(command)+1)
	copy(buf, command)
	buf[len(command)] = '\n'
	_, err = t.Write(buf)
	checkErr(err)

	t.SkipUntil("TSW-750>") // Skip to the first prompt delimiter
	var output []byte
	output, err = t.ReadUntil("TSW-750>") // Read until the second prompt delimiter (provided by sending two commands in sendCommand)
	checkErr(err)

	t.Close() // Close the telnet session

	output = output[:len(output)-10] // Ghetto trim the prompt off the response

	return string(output)
}

func fusionRequest(requestType string, url string) []byte {
	client := &http.Client{}
	req, err := http.NewRequest(requestType, url, nil)
	checkErr(err)

	req.Header.Add("Content-Type", "application/json")
	resp, err := client.Do(req)
	checkErr(err)

	body, err := ioutil.ReadAll(resp.Body)
	checkErr(err)

	return body
}

func getRooms(c echo.Context) error {
	response := fusionRequest("GET", "http://lazyeye.byu.edu/fusion/apiservice/rooms/")
	return c.String(http.StatusOK, string(response)) // MAKE SURE YOU HAVE THE TRAILING SLASH
}

func getRoomByName(c echo.Context) error {
	// Get the room's ID from its name
	response := fusionRequest("GET", "http://lazyeye.byu.edu/fusion/apiservice/rooms/?search="+c.Param("room"))
	rooms := fusionResponse{}
	err := json.Unmarshal(response, &rooms)
	checkErr(err)

	// Get info about the room using its ID
	response = fusionRequest("GET", "http://lazyeye.byu.edu/fusion/apiservice/rooms/"+rooms.API_Rooms[0].RoomID)
	rooms = fusionResponse{}
	err = json.Unmarshal(response, &rooms)
	checkErr(err)

	hostname := rooms.API_Rooms[0].Symbols[0].ProcessorName
	address := rooms.API_Rooms[0].Symbols[0].ConnectInfo

	roomResponse := room{Hostname: hostname, Address: address}

	jsonResponse, _ := json.Marshal(roomResponse)
	return c.String(http.StatusOK, string(jsonResponse))
}

func main() {
	port := ":8000"
	e := echo.New()

	// Echo doesn't like doing things "magically" which means it won't auto-redirect endpoints without a trailing slash to one with a trailing slash (and vice versa) which is why endpoints are duplicated
	e.Get("/health", health)
	e.Get("/health/", health)

	e.Get("/rooms", getRooms)
	e.Get("/rooms/", getRooms)
	e.Get("/rooms/:room", getRoomByName)
	e.Get("/rooms/:room/", getRoomByName)
	// e.Get("/buildings", ...)
	// e.Get("/buildings/:building", ...)
	// e.Get("/buildings/:building/room", ...)
	// e.Get("/buildings/:building/rooms/:room", ...)
	// e.Get("/buildings/:building/rooms/:room/signals", ...)
	// e.Get("/buildings/:building/rooms/:room/signals/:signal", ...)
	//
	// e.Post("/rooms", ...)
	// e.Post("/buildings", ...)
	// e.Post("/buildings/:building/rooms/:room/signals", ...)
	//
	// e.Put("/rooms/:room", ...)
	// e.Put("/buildings/:building", ...)
	// e.Put("/buildings/:building/rooms/:room", ...)
	// e.Put("/buildings/:building/rooms/:room/signals/:signal", ...)
	//
	// e.Delete("/rooms/:room", ...)
	// e.Delete("/buildings/:building", ...)
	// e.Delete("/buildings/:building/rooms/:room", ...)
	// e.Delete("/buildings/:building/rooms/:room/signals/:signal", ...)

	fmt.Printf("AV API is listening on %s\n", port)
	e.Run(fasthttp.New(port))
}
<|endoftext|>


<commit_before>package fbot

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"
)

// Handler returns the handler to use for incoming messages from the
// Facebook Messenger Platform.
func Handler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "GET":
			verifyToken(w, r)
		case "POST":
			receiveMessage(w, r)
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	}
}

func verifyToken(w http.ResponseWriter, r *http.Request) {
	if r.FormValue("hub.verify_token") == config.VerifyToken {
		w.Write([]byte(r.FormValue("hub.challenge")))
	} else {
		w.Write([]byte("Error; wrong verify token"))
	}
}

type response struct {
	Entries []entry `json:"entry"`
}

type entry struct {
	Events []Event `json:"messaging"`
}

func receiveMessage(w http.ResponseWriter, r *http.Request) {
	if r.Body == nil {
		http.Error(w, "Error reading empty response body", http.StatusBadRequest)

		return
	}

	defer r.Body.Close()

	message, err := ioutil.ReadAll(r.Body)

	if err != nil {
		http.Error(w, "Error reading response body", http.StatusInternalServerError)

		return
	}

	xHubSignature := r.Header.Get("x-hub-signature")

	if xHubSignature == "" || !strings.HasPrefix(xHubSignature, "sha1=") {
		http.Error(w, "Error getting integrity signature", http.StatusBadRequest)

		return
	}

	xHubSignature = xHubSignature[5:] // Remove "sha1=" prefix.

	if ok := verifySignature([]byte(xHubSignature), message); !ok {
		http.Error(w, "Error checking message integrity", http.StatusBadRequest)

		return
	}

	var res response

	err = json.Unmarshal(message, &res)

	if err != nil {
		http.Error(w, "Error parsing response body format", http.StatusBadRequest)

		return
	}

	dispatchEvents(res.Entries)
}

func verifySignature(signature, message []byte) bool {
	mac := hmac.New(sha1.New, []byte(config.AppSecret))
	mac.Write(message)

	expectedSignature := mac.Sum(nil)

	return hmac.Equal(expectedSignature, hexSignature(signature))
}

func hexSignature(signature []byte) []byte {
	s := make([]byte, 20)

	hex.Decode(s, signature)

	return s
}

func dispatchEvents(entries []entry) {
	for _, entry := range entries {
		for _, event := range entry.Events {
			dispatchEvent(event)
		}
	}
}

func dispatchEvent(event Event) {
	var eventName string

	if event.Message != nil {
		eventName = "message"
	}

	if eventName != "" {
		trigger(eventName, event)
	}
}
<commit_msg>Fix message dispatch to consider postback and message-delivery.<commit_after>package fbot

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"
)

// Handler returns the handler to use for incoming messages from the
// Facebook Messenger Platform.
func Handler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "GET":
			verifyToken(w, r)
		case "POST":
			receiveMessage(w, r)
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	}
}

func verifyToken(w http.ResponseWriter, r *http.Request) {
	if r.FormValue("hub.verify_token") == config.VerifyToken {
		w.Write([]byte(r.FormValue("hub.challenge")))
	} else {
		w.Write([]byte("Error; wrong verify token"))
	}
}

type response struct {
	Entries []entry `json:"entry"`
}

type entry struct {
	Events []Event `json:"messaging"`
}

func receiveMessage(w http.ResponseWriter, r *http.Request) {
	if r.Body == nil {
		http.Error(w, "Error reading empty response body", http.StatusBadRequest)

		return
	}

	defer r.Body.Close()

	message, err := ioutil.ReadAll(r.Body)

	if err != nil {
		http.Error(w, "Error reading response body", http.StatusInternalServerError)

		return
	}

	xHubSignature := r.Header.Get("x-hub-signature")

	if xHubSignature == "" || !strings.HasPrefix(xHubSignature, "sha1=") {
		http.Error(w, "Error getting integrity signature", http.StatusBadRequest)

		return
	}

	xHubSignature = xHubSignature[5:] // Remove "sha1=" prefix.

	if ok := verifySignature([]byte(xHubSignature), message); !ok {
		http.Error(w, "Error checking message integrity", http.StatusBadRequest)

		return
	}

	var res response

	err = json.Unmarshal(message, &res)

	if err != nil {
		http.Error(w, "Error parsing response body format", http.StatusBadRequest)

		return
	}

	dispatchEvents(res.Entries)
}

func verifySignature(signature, message []byte) bool {
	mac := hmac.New(sha1.New, []byte(config.AppSecret))
	mac.Write(message)

	expectedSignature := mac.Sum(nil)

	return hmac.Equal(expectedSignature, hexSignature(signature))
}

func hexSignature(signature []byte) []byte {
	s := make([]byte, 20)

	hex.Decode(s, signature)

	return s
}

func dispatchEvents(entries []entry) {
	for _, entry := range entries {
		for _, event := range entry.Events {
			dispatchEvent(event)
		}
	}
}

func dispatchEvent(event Event) {
	if eventName := selectEvent(event); eventName != "" {
		trigger(eventName, event)
	}
}

func selectEvent(event Event) string {
	switch {
	case event.Message != nil:
		return "message"
	case event.Delivery != nil:
		return "delivery"
	case event.Postback != nil:
		return "postback"
	default:
		return ""
	}
}
<|endoftext|>


<commit_before>package ma

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"log"
	"net/http"
	"strings"
)

var (
	Sessions   = make(map[string]bool)
	AllowedIPs = make(map[string]bool)
	closeCh    = make(chan int)
	server     *Server
)

const (
	RemoteUrlPath = "/remote"
	DefaultPort   = 8000
)

type Server struct {
	Host      string
	Port      int
	IsTls     bool
	IsRunning bool
}

type ServerConfig struct {
	Host string
	Port int
	NoTls bool
}

func NewServer(c *ServerConfig) (error, *Server) {
	s := &Server{c.Host, c.Port, true, false}
	if c.NoTls {
		s.IsTls = false
	}
	if s.Host == "" {
		s.Host = "localhost"
	}
	if s.Port == 0 {
		s.Port = DefaultPort
	}
	server = s
	return nil, server
}

// return absolute url of base path, like
// https://10.12.196.11:8080/
func (s *Server) GetBaseUrl() (string) {
	if s.Host == "" || s.Port == 0 {
		panic("server is not setup")
	}
	proto := "https"
	if !s.IsTls {
		proto = "http"
	}
	return fmt.Sprintf("%s://%s:%d/", proto, s.Host, s.Port)
}

//set defaults for server

func (s *Server) Start() {
	defer func() { s.IsRunning = false }()
	addr := fmt.Sprintf("%s:%d", s.Host, s.Port)
	http.Handle(RemoteUrlPath, websocket.Handler(remoteHandler))
	log.Printf("listen on %s\n", addr)
	err := http.ListenAndServeTLS(addr, "cert.pem", "key.pem", nil)
	if err != nil {
		panic("ListentAndServer: " + err.Error())
	}
}

func isWebsocketAllowed(ws *websocket.Conn) bool {
	addr := ws.Request().RemoteAddr
	t := strings.Split(addr, ":")
	if len(t) != 2 {
		log.Printf("ERROR: %s is wrong formated", addr)
		return false
	}
	return AllowedIPs[t[0]]

}

// handle websocket connection from remote host
func remoteHandler(ws *websocket.Conn) {
	// please note, ws will be closed after return in this func
	log.Printf("got websocket conn from %v\n", ws.Request().RemoteAddr)
	if !isWebsocketAllowed(ws) {
		log.Printf("WARN: websocket from %s is not allowed", ws.Request().RemoteAddr)
		websocket.Message.Send(ws, NOTOK)
		return
	}
	websocket.Message.Send(ws, OK)
	<-closeCh
}
<commit_msg>minor refactoring, initial /config handler<commit_after>package ma

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"log"
	"net/http"
	"strings"
)

var (
	Sessions   = make(map[string]bool)
	AllowedIPs = make(map[string]bool)
	closeCh    = make(chan int)
	server     *Server
)

const (
	RemoteUrlPath = "/remote"
	DefaultPort   = 8000
	ConfigUrlPath    = "/config"
)

type Server struct {
	Host      string
	Port      int
	IsTls     bool
	IsRunning bool
}

type ServerConfig struct {
	Host  string
	Port  int
	NoTls bool
}

func NewServer(c *ServerConfig) (error, *Server) {
	s := &Server{c.Host, c.Port, true, false}
	if c.NoTls {
		s.IsTls = false
	}
	if s.Host == "" {
		s.Host = "localhost"
	}
	if s.Port == 0 {
		s.Port = DefaultPort
	}
	server = s
	return nil, server
}

// return absolute url of base path, like
// https://10.12.196.11:8080
func (s *Server) GetBaseUrl() string {
	if s.Host == "" || s.Port == 0 {
		panic("server is not setup")
	}
	proto := "https"
	if !s.IsTls {
		proto = "http"
	}
	return fmt.Sprintf("%s://%s:%d", proto, s.Host, s.Port)
}

func ConfigHandler(rw http.ResponseWriter, req *http.Request) {
	rw.Write([]byte(OK))
}

func (s *Server) Start() {
	defer func() { s.IsRunning = false }()
	addr := fmt.Sprintf("%s:%d", s.Host, s.Port)
	http.Handle(RemoteUrlPath, websocket.Handler(remoteHandler))
	http.HandleFunc(ConfigUrlPath, ConfigHandler)
	log.Printf("listen on %s\n", addr)
	err := http.ListenAndServeTLS(addr, "cert.pem", "key.pem", nil)
	if err != nil {
		panic("ListentAndServer: " + err.Error())
	}
}

func isAgentAllowed(ws *websocket.Conn) bool {
	addr := ws.Request().RemoteAddr
	t := strings.Split(addr, ":")
	if len(t) != 2 {
		log.Printf("ERROR: %s is wrong formated", addr)
		return false
	}
	return AllowedIPs[t[0]]

}

// handle websocket connection from remote host
func remoteHandler(ws *websocket.Conn) {
	// please note, ws will be closed after return in this func
	log.Printf("got websocket conn from %v\n", ws.Request().RemoteAddr)
	if !isAgentAllowed(ws) {
		log.Printf("WARN: websocket from %s is not allowed", ws.Request().RemoteAddr)
		websocket.Message.Send(ws, NOTOK)
		return
	}
	websocket.Message.Send(ws, OK)
	<-closeCh
}
<|endoftext|>


<commit_before>package main

import (
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
  "github.com/codegangsta/martini"
  "encoding/json"
  "net/http"
)

func main() {
  m := martini.Classic()

  db, err := sql.Open("mysql", "root@127.0.0.1/ops")
  if err != nil { panic(err) }
  defer db.Close()

  m.Map(db)

  //All fake data
  schoolOne := School{
    Id: 1,
    Name: "Millard North High School",
    CountyId: 1,
    DistrictId: 1,
    Latitude: 41.31027811,
    Longitude: -96.146874,
  }
  schoolTwo := School{
    Id: 2,
    Name: "Millard South High School",
    CountyId: 1,
    DistrictId: 1,
    Latitude: 41.31027811,
    Longitude: -96.146874,
  }
  schools := []School{schoolOne, schoolTwo}

  entry2012 := DistrictYear{
    EnrollmentSize: 15,
    District: District{
      Id: 15,
      Name: "OPS",
      Latitude: 41.31027811,
      Longitude: -96.146874,
    },
  }

  schoolYearOne := SchoolYear{
    EnrollmentSize: 55,
    School: schoolOne,
  }
  district66 := SchoolsByYear{
    Year: "2012-2013",
    Schools: []SchoolYear{
      schoolYearOne,
    },
  }

  allDistricts := []DistrictsByYear{
    DistrictsByYear{
      Year: "2012-2013",
      Districts: []DistrictYear{entry2012},
    },
    DistrictsByYear{
      Year: "2011-2012",
      Districts: []DistrictYear{entry2012},
    },
  }

  edisonElementary := SchoolWithEnrollment{
    School: schoolOne,
    EnrollmentByYear: []EnrollmentByYear{
      EnrollmentByYear{
        Year: "2012-2013",
        Teachers: 55,
        Students: 3000,
        GradeEnrollment: []GradeEnrollment{
          GradeEnrollment{
            Grade: "6th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "5th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "4th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "3th",
            Enrollment: 544,
          },
        },
      },
      EnrollmentByYear{
        Year: "2011-2012",
        Teachers: 55,
        Students: 3000,
        GradeEnrollment: []GradeEnrollment{
          GradeEnrollment{
            Grade: "6th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "5th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "4th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "3th",
            Enrollment: 544,
          },
        },
      },
    },
  }

  //Routes
  m.Get("/schools", func(res http.ResponseWriter) string {
    return render(res, schools)
  })

  m.Get("/districts", func(res http.ResponseWriter) string {
    return render(res, allDistricts)
  })

  m.Get("/district/:id", func(res http.ResponseWriter) string {
    return render(res, district66)
  })

  m.Get("/school/:id", func(res http.ResponseWriter, params martini.Params) string {
    return render(res, edisonElementary)
  })

  m.Run()
}

func render(res http.ResponseWriter, data interface{}) string {
  thing, err := json.Marshal(data)
  if err != nil { panic(err) }
  return asJson(res, thing)
}

func asJson(res http.ResponseWriter, data []byte) string {
  res.Header().Set("Content-Type", "application/json")
  res.Header().Set("Access-Control-Allow-Origin", "*")
  return string(data[:])
}

//Structs
type School struct {
  Id          int64
  Name        string `sql:"size:255"`
  CountyId    int64
  DistrictId  int64
  Latitude    float64
  Longitude   float64
}

type SchoolsByYear struct {
  Year        string
  Schools     []SchoolYear
}

type SchoolYear struct {
  EnrollmentSize  int64
  School          School
}

type District struct {
  Id              int64
  Name            string
  Latitude        float64
  Longitude       float64
}

type SchoolWithEnrollment struct {
  School            School
  EnrollmentByYear  []EnrollmentByYear
}

type EnrollmentByYear struct {
  Year          string
  Teachers      int64
  Students      int64
  GradeEnrollment []GradeEnrollment
}

type GradeEnrollment struct {
  Grade         string
  Enrollment    int64
}

type DistrictsByYear struct {
  Year      string
  Districts []DistrictYear
}

type DistrictYear struct {
  EnrollmentSize  int64
  District        District
}
<commit_msg>Update server.go<commit_after>package main

import (
  "database/sql"
  _ "github.com/go-sql-driver/mysql"
  "github.com/codegangsta/martini"
  "encoding/json"
  "net/http"
)

func main() {
  m := martini.Classic()

  db, err := sql.Open("mysql", "root@127.0.0.1/ops")
  if err != nil { panic(err) }
  defer db.Close()

  m.Map(db)

  //All fake data
  schoolOne := School{
    Id: 1,
    Name: "Millard North High School",
    CountyId: 1,
    DistrictId: 1,
    Latitude: 41.31027811,
    Longitude: -96.146874,
  }
  schoolTwo := School{
    Id: 2,
    Name: "Millard South High School",
    CountyId: 1,
    DistrictId: 1,
    Latitude: 41.31027811,
    Longitude: -96.146874,
  }
  schools := []School{schoolOne, schoolTwo}

  entry2012 := DistrictYear{
    EnrollmentSize: 15,
    District: District{
      Id: 15,
      Name: "OPS",
      Latitude: 41.31027811,
      Longitude: -96.146874,
    },
  }

  schoolYearOne := SchoolYear{
    EnrollmentSize: 55,
    School: schoolOne,
  }
  district66 := SchoolsByYear{
    Year: "2012-2013",
    Schools: []SchoolYear{
      schoolYearOne,
    },
  }

  allDistricts := []DistrictsByYear{
    DistrictsByYear{
      Year: "2012-2013",
      Districts: []DistrictYear{entry2012},
    },
    DistrictsByYear{
      Year: "2011-2012",
      Districts: []DistrictYear{entry2012},
    },
  }

  millardNorth := SchoolWithEnrollment{
    School: schoolOne,
    EnrollmentByYear: []EnrollmentByYear{
      EnrollmentByYear{
        Year: "2012-2013",
        Teachers: 55,
        Students: 3000,
        GradeEnrollment: []GradeEnrollment{
          GradeEnrollment{
            Grade: "6th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "5th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "4th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "3th",
            Enrollment: 544,
          },
        },
      },
      EnrollmentByYear{
        Year: "2011-2012",
        Teachers: 55,
        Students: 3000,
        GradeEnrollment: []GradeEnrollment{
          GradeEnrollment{
            Grade: "6th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "5th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "4th",
            Enrollment: 544,
          },
          GradeEnrollment{
            Grade: "3th",
            Enrollment: 544,
          },
        },
      },
    },
  }

    millardSouth := SchoolWithEnrollment{
    School: schoolTwo,
    EnrollmentByYear: []EnrollmentByYear{
      EnrollmentByYear{
        Year: "2012-2013",
        Teachers: 82,
        Students: 4200,
        GradeEnrollment: []GradeEnrollment{
          GradeEnrollment{
            Grade: "6th",
            Enrollment: 678,
          },
          GradeEnrollment{
            Grade: "5th",
            Enrollment: 234,
          },
          GradeEnrollment{
            Grade: "4th",
            Enrollment: 410,
          },
          GradeEnrollment{
            Grade: "3th",
            Enrollment: 167,
          },
        },
      },
      EnrollmentByYear{
        Year: "2011-2012",
        Teachers: 56,
        Students: 3000,
        GradeEnrollment: []GradeEnrollment{
          GradeEnrollment{
            Grade: "6th",
            Enrollment: 500,
          },
          GradeEnrollment{
            Grade: "5th",
            Enrollment: 412,
          },
          GradeEnrollment{
            Grade: "4th",
            Enrollment: 180,
          },
          GradeEnrollment{
            Grade: "3th",
            Enrollment: 333,
          },
        },
      },
    },
  }

  var schoolsWithEnroll = map[string]SchoolWithEnrollment{
    "1": millardNorth,
    "2": millardSouth,
}

  //Routes
  m.Get("/schools", func(res http.ResponseWriter) string {
    return render(res, schools)
  })

  m.Get("/districts", func(res http.ResponseWriter) string {
    return render(res, allDistricts)
  })

  m.Get("/district/:id", func(res http.ResponseWriter) string {
    return render(res, district66)
  })

  m.Get("/school/:id", func(res http.ResponseWriter, params martini.Params) string {
    return render(res, schoolsWithEnroll[params["id"]])
  })

  m.Run()
}

func render(res http.ResponseWriter, data interface{}) string {
  thing, err := json.Marshal(data)
  if err != nil { panic(err) }
  return asJson(res, thing)
}

func asJson(res http.ResponseWriter, data []byte) string {
  res.Header().Set("Content-Type", "application/json")
  res.Header().Set("Access-Control-Allow-Origin", "*")
  return string(data[:])
}

//Structs
type School struct {
  Id          int64
  Name        string `sql:"size:255"`
  CountyId    int64
  DistrictId  int64
  Latitude    float64
  Longitude   float64
}

type SchoolsByYear struct {
  Year        string
  Schools     []SchoolYear
}

type SchoolYear struct {
  EnrollmentSize  int64
  School          School
}

type District struct {
  Id              int64
  Name            string
  Latitude        float64
  Longitude       float64
}

type SchoolWithEnrollment struct {
  School            School
  EnrollmentByYear  []EnrollmentByYear
}

type EnrollmentByYear struct {
  Year          string
  Teachers      int64
  Students      int64
  GradeEnrollment []GradeEnrollment
}

type GradeEnrollment struct {
  Grade         string
  Enrollment    int64
}

type DistrictsByYear struct {
  Year      string
  Districts []DistrictYear
}

type DistrictYear struct {
  EnrollmentSize  int64
  District        District
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/j-keck/arping"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/signal"
	"runtime"
	"time"
)

var leases DHCPLeases

func server() {
	log.Println("startup -  version: ", VERSION)

	if *verboseFlag {
		verboseLog = log.New(os.Stdout, "", log.LstdFlags)
	} else {
		verboseLog = log.New(ioutil.Discard, "", 0)
	}

	//
	// init listeners
	//
	dhcpLeaseChan := make(chan *DHCPLease)
	go dhcpListener(dhcpLeaseChan)

	clientChan := make(chan []byte)
	go clientListener(clientChan)

	//
	// init cleanup old leases routine
	//
	var clearExpiredLeasesTickerChan <-chan time.Time
	var clearOfflineHostsTickerChan <-chan time.Time

	if !*expireBasedFlag {
		log.Printf("enable active check - arping every: %s\n", *cleanupLeaseTimerFlag)
		if hasPermission, err := hasRawSocketPermission(); hasPermission {
			clearOfflineHostsTickerChan = time.NewTicker(cleanupLeaseTimer).C
			// clear one time on startup
			clearOfflineHosts()
		} else {
			log.Printf("enable active check failed: '%s' -  fallback to passive mode\n", err)
			*expireBasedFlag = true
		}
	}

	if *expireBasedFlag {
		timer := cleanupLeaseTimer
		if leaseExpiredDuration < timer {
			timer = leaseExpiredDuration / 2
		}
		log.Printf("enable expired based - check timer: %s, expire duation: %s\n", timer, leaseExpiredDuration)
		clearExpiredLeasesTickerChan = time.NewTicker(timer).C
		// clear on time on startup
		clearExpiredLeases()
	}

	//
	// if persistent leases are enable, load saved leases
	//
	if *keepLeasesOverRestartFlag {
		var err error
		leases, err = loadLeases()
		logOnError(err, "unable to load leases - start with emtpy leases")
	}

	//
	// init CTRL-C - catcher
	//
	go func() {
		sigchan := make(chan os.Signal, 1)
		signal.Notify(sigchan, os.Interrupt)
		// block
		<-sigchan
		shutdown()
	}()

	//
	// main loop
	//
	for {
		select {
		case cmd := <-clientChan:
			switch string(cmd) {
			case "shutdown":
				shutdown()

			case "clearLeases":
				log.Println("clear leases")
				leases.Clear()
				clientChan <- []byte("done")

			case "leases":
				j, err := json.Marshal(leases)
				panicOnError(err)
				clientChan <- j

			case "version":
				clientChan <- []byte(VERSION)

			case "mode":
				if *expireBasedFlag {
					clientChan <- []byte("passive")
				} else {
					clientChan <- []byte("active")
				}
			}

		case l := <-dhcpLeaseChan:
			log.Printf("new DHCP Lease: '%s'\n", l.String())
			leases.UpdateOrAdd(l)

		case <-clearExpiredLeasesTickerChan:
			clearExpiredLeases()

		case <-clearOfflineHostsTickerChan:
			clearOfflineHosts()
		}
	}
}

func shutdown() {
	if *keepLeasesOverRestartFlag {
		log.Println("save leases")
		logOnError(saveLeases(), "unable to save leases")
	}

	log.Println("shutdown")
	closeListener()
	os.Exit(0)
}

func clearExpiredLeases() {
	verboseLog.Println("check expired leases")
	leases.Foreach(func(l *DHCPLease) {
		if time.Now().After(l.Expire) {
			log.Printf("expired: '%s'\n", l.String())
			leases.Delete(l)
		}
	})
}
func clearOfflineHosts() {
	verboseLog.Println("arping hosts")
	pingHosts()

	leases.Foreach(func(l *DHCPLease) {
		if l.MissedPings > *missedPingsThresholdFlag {
			log.Printf("remove lease: '%s'\n", l.String())
			leases.Delete(l)
		}
	})
}

func pingHosts() {
	leases.Foreach(func(l *DHCPLease) {
		if _, _, err := arping.Ping(net.ParseIP(l.IP)); err == arping.ErrTimeout {
			l.MissedPings++
			verboseLog.Printf("%s is offline\n", l.String())
		} else if err != nil {
			log.Printf("unable to execute ping: '%s'\n", err.Error())
		} else {
			verboseLog.Printf("%s is online", l.String())
			l.MissedPings = 0
		}
	})
}
func hasRawSocketPermission() (bool, error) {
	var localIP net.IP

	// find any local ip
	addrs, err := net.InterfaceAddrs()
	panicOnError(err)

	for _, addr := range addrs {
		if ipNet, ok := addr.(*net.IPNet); ok {
			if ipNet.IP.IsLoopback() {
				continue
			}

			localIP = ipNet.IP
			break
		}
	}

	// arping any local ip results always in timeout
	verboseLog.Printf("arping '%s' to check permission\n", localIP.String())
	if _, _, err = arping.Ping(localIP); err == arping.ErrTimeout {
		return true, nil
	}

	return false, err
}

func saveLeases() error {
	j, err := json.Marshal(leases)
	if err != nil {
		return err
	}

	path := leasesPersistenceFilePath()

	verboseLog.Printf("save leases under %s\n", path)
	return ioutil.WriteFile(path, []byte(j), 0644)
}

func loadLeases() (DHCPLeases, error) {
	var leases DHCPLeases

	path := leasesPersistenceFilePath()

	verboseLog.Printf("load saved leases from %s\n", path)

	if _, err := os.Stat(path); os.IsNotExist(err) {
		return leases, fmt.Errorf("no persistence file found under %s\n", path)
	}

	b, err := ioutil.ReadFile(path)
	if err != nil {
		return leases, err
	}

	err = json.Unmarshal(b, &leases)
	return leases, err
}

func leasesPersistenceFilePath() string {

	var basePath string
	if runtime.GOOS == "windows" {
		basePath = os.Getenv("APPDATA")
	} else {
		basePath = "/var/lib/lsleases"
	}
	return fmt.Sprintf("%s/lsleases.json", basePath)
}
<commit_msg>reword log msg<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/j-keck/arping"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/signal"
	"runtime"
	"time"
)

var leases DHCPLeases

func server() {
	log.Println("startup -  version: ", VERSION)

	if *verboseFlag {
		verboseLog = log.New(os.Stdout, "", log.LstdFlags)
	} else {
		verboseLog = log.New(ioutil.Discard, "", 0)
	}

	//
	// init listeners
	//
	dhcpLeaseChan := make(chan *DHCPLease)
	go dhcpListener(dhcpLeaseChan)

	clientChan := make(chan []byte)
	go clientListener(clientChan)

	//
	// init cleanup old leases routine
	//
	var clearExpiredLeasesTickerChan <-chan time.Time
	var clearOfflineHostsTickerChan <-chan time.Time

	if !*expireBasedFlag {
		log.Printf("enable active check - arping every: %s\n", *cleanupLeaseTimerFlag)
		if hasPermission, err := hasRawSocketPermission(); hasPermission {
			clearOfflineHostsTickerChan = time.NewTicker(cleanupLeaseTimer).C
			// clear one time on startup
			clearOfflineHosts()
		} else {
			log.Printf("enable active check failed: '%s' -  fallback to passive mode\n", err)
			*expireBasedFlag = true
		}
	}

	if *expireBasedFlag {
		timer := cleanupLeaseTimer
		if leaseExpiredDuration < timer {
			timer = leaseExpiredDuration / 2
		}
		log.Printf("enable expired based - check timer: %s, expire duation: %s\n", timer, leaseExpiredDuration)
		clearExpiredLeasesTickerChan = time.NewTicker(timer).C
		// clear on time on startup
		clearExpiredLeases()
	}

	//
	// if persistent leases are enable, load saved leases
	//
	if *keepLeasesOverRestartFlag {
		var err error
		leases, err = loadLeases()
		logOnError(err, "unable to load leases - start with emtpy leases")
	}

	//
	// init CTRL-C - catcher
	//
	go func() {
		sigchan := make(chan os.Signal, 1)
		signal.Notify(sigchan, os.Interrupt)
		// block
		<-sigchan
		shutdown()
	}()

	//
	// main loop
	//
	for {
		select {
		case cmd := <-clientChan:
			switch string(cmd) {
			case "shutdown":
				shutdown()

			case "clearLeases":
				log.Println("clear leases")
				leases.Clear()
				clientChan <- []byte("done")

			case "leases":
				j, err := json.Marshal(leases)
				panicOnError(err)
				clientChan <- j

			case "version":
				clientChan <- []byte(VERSION)

			case "mode":
				if *expireBasedFlag {
					clientChan <- []byte("passive")
				} else {
					clientChan <- []byte("active")
				}
			}

		case l := <-dhcpLeaseChan:
			log.Printf("new DHCP Lease: '%s'\n", l.String())
			leases.UpdateOrAdd(l)

		case <-clearExpiredLeasesTickerChan:
			clearExpiredLeases()

		case <-clearOfflineHostsTickerChan:
			clearOfflineHosts()
		}
	}
}

func shutdown() {
	if *keepLeasesOverRestartFlag {
		log.Println("save leases")
		logOnError(saveLeases(), "unable to save leases")
	}

	log.Println("shutdown")
	closeListener()
	os.Exit(0)
}

func clearExpiredLeases() {
	verboseLog.Println("check expired leases")
	leases.Foreach(func(l *DHCPLease) {
		if time.Now().After(l.Expire) {
			log.Printf("expired: '%s'\n", l.String())
			leases.Delete(l)
		}
	})
}
func clearOfflineHosts() {
	verboseLog.Println("check offline hosts per ping")
	pingHosts()

	leases.Foreach(func(l *DHCPLease) {
		if l.MissedPings > *missedPingsThresholdFlag {
			log.Printf("remove lease: '%s'\n", l.String())
			leases.Delete(l)
		}
	})
}

func pingHosts() {
	leases.Foreach(func(l *DHCPLease) {
		if _, _, err := arping.Ping(net.ParseIP(l.IP)); err == arping.ErrTimeout {
			l.MissedPings++
			verboseLog.Printf("%s is offline\n", l.String())
		} else if err != nil {
			log.Printf("unable to execute ping: '%s'\n", err.Error())
		} else {
			verboseLog.Printf("%s is online", l.String())
			l.MissedPings = 0
		}
	})
}
func hasRawSocketPermission() (bool, error) {
	var localIP net.IP

	// find any local ip
	addrs, err := net.InterfaceAddrs()
	panicOnError(err)

	for _, addr := range addrs {
		if ipNet, ok := addr.(*net.IPNet); ok {
			if ipNet.IP.IsLoopback() {
				continue
			}

			localIP = ipNet.IP
			break
		}
	}

	// arping any local ip results always in timeout
	verboseLog.Printf("arping '%s' to check permission\n", localIP.String())
	if _, _, err = arping.Ping(localIP); err == arping.ErrTimeout {
		return true, nil
	}

	return false, err
}

func saveLeases() error {
	j, err := json.Marshal(leases)
	if err != nil {
		return err
	}

	path := leasesPersistenceFilePath()

	verboseLog.Printf("save leases under %s\n", path)
	return ioutil.WriteFile(path, []byte(j), 0644)
}

func loadLeases() (DHCPLeases, error) {
	var leases DHCPLeases

	path := leasesPersistenceFilePath()

	verboseLog.Printf("load saved leases from %s\n", path)

	if _, err := os.Stat(path); os.IsNotExist(err) {
		return leases, fmt.Errorf("no persistence file found under %s\n", path)
	}

	b, err := ioutil.ReadFile(path)
	if err != nil {
		return leases, err
	}

	err = json.Unmarshal(b, &leases)
	return leases, err
}

func leasesPersistenceFilePath() string {

	var basePath string
	if runtime.GOOS == "windows" {
		basePath = os.Getenv("APPDATA")
	} else {
		basePath = "/var/lib/lsleases"
	}
	return fmt.Sprintf("%s/lsleases.json", basePath)
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Boise State University All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/http/httputil"
	"net/http/pprof"
	"net/url"
	"os"
	"os/signal"
	"path"
	"sort"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
)

const (
	// defaultNotebook is used if the request doesn't specify a docker image.
	defaultNotebook = "jupyter/minimal-notebook"
)

func init() {
	log.SetFlags(log.Lshortfile | log.LstdFlags)
}

// TODO(kyle): embed or add to notebookServer?
type serverConfig struct {
	ContainerLifetime time.Duration `json:"container_lifetime"`
	EnablePProf       bool          `json:"enable_pprof"`
	ImageRegexp       string        `json:"image_regexp"`
	MaxContainers     int           `json:"max_containers"`
	HTTPRedirect      bool          `json:"http_redirect"`
	Port              string        `json:"port"`
	TLSCert           string        `json:"tls_cert"`
	TLSKey            string        `json:"tls_key"`
}

var defaultConfig = serverConfig{
	ContainerLifetime: defaultContainerLifetime,
	ImageRegexp:       allImageMatch,
	MaxContainers:     defaultMaxContainers,
}

// notebookServer handles the http tasks for the temporary notebooks.
type notebookServer struct {
	// pool manages the containers
	pool *notebookPool
	// token is the generated random auth token
	token string
	// mux routes http traffic
	mux *ServeMux
	// embed a server
	*http.Server
	// httpRedirect determines whether http redirects to https
	httpRedirect bool
	// TLS certificate path
	tlsCert string
	// TLS private key path
	tlsKey string
}

// readConfig reads json config from r
func readConfig(r io.Reader) (serverConfig, error) {
	sc := defaultConfig
	err := json.NewDecoder(r).Decode(&sc)
	return sc, err
}

// newNotebookServer initializes a server and owned resources, using a
// configuration if supplied.
func newNotebookServer(config string) (*notebookServer, error) {
	sc := defaultConfig
	if config != "" {
		fin, err := os.Open(config)
		if err != nil {
			return nil, err
		}
		sc, err = readConfig(fin)
		if err != nil {
			return nil, err
		}
	}
	p, err := newNotebookPool(sc.ImageRegexp, sc.MaxContainers, sc.ContainerLifetime)
	if err != nil {
		return nil, err
	}
	tkn := newHash(defaultHashSize)
	srv := &notebookServer{}
	srv.pool = p
	srv.token = tkn
	srv.pool.token = tkn
	srv.Server = &http.Server{
		Addr: sc.Port,
	}
	//srv.mux = http.NewServeMux()
	srv.mux = new(ServeMux)
	srv.mux.HandleFunc("/list", srv.listImages)
	srv.mux.HandleFunc("/new", srv.newNotebookHandler)
	srv.mux.HandleFunc("/status", srv.statusHandler)
	if sc.EnablePProf {
		srv.mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
		srv.mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
		srv.mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
		srv.mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
	}
	srv.Handler = srv.mux

	srv.tlsCert = sc.TLSCert
	srv.tlsKey = sc.TLSKey
	srv.httpRedirect = sc.HTTPRedirect

	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt)
	go func() {
		<-quit
		log.Println("Shutting down server...")
		err := srv.pool.releaseContainers(true)
		if err != nil {
			log.Print(err)
		}
		os.Exit(0)
	}()
	go func() {
		for {
			select {
			case p := <-srv.pool.deregisterMux:
				log.Printf("deregistering %s from mux", p)
				srv.mux.Deregister(p)
			}
		}
	}()
	return srv, nil
}

// statusHandler checks the status of a single container, returning 200 if it
// is running, another value otherwise.
func (srv *notebookServer) statusHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	id := r.FormValue("container")
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	filter := filters.NewArgs()
	if id != "" {
		filter.Add("id", id)
	}
	opts := types.ContainerListOptions{
		Filters: filter,
	}
	containers, err := cli.ContainerList(context.Background(), opts)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if len(containers) < 1 {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	state := containers[0].State
	log.Printf("container %s state: %s", id, state)
	if state == "running" {
		w.WriteHeader(http.StatusOK)
		return
	}
	if _, ok := r.Form["ping"]; ok {
		var tmpnb *tempNotebook
		for _, v := range srv.pool.containerMap {
			if v.id == id {
				tmpnb = v
				break
			}
		}
		if tmpnb == nil {
			log.Printf("couldn't find container in containerMap: %s", id)
			w.WriteHeader(http.StatusNotFound)
			return
		}
		pingURL := url.URL{
			Host: fmt.Sprintf(":%d", tmpnb.port),
			Path: path.Join("/book", tmpnb.hash),
		}
		resp, err := http.Get(pingURL.String())
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		resp.Body.Close()
		w.WriteHeader(resp.StatusCode)
		return
	}
	w.WriteHeader(http.StatusNotFound)
}

func (srv *notebookServer) newNotebookHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		log.Print(err)
		return
	}

	var imageName = r.FormValue("image")
	if imageName == "" {
		imageName = defaultNotebook
	}

	if _, ok := srv.pool.availableImages[imageName]; !ok {
		http.Error(w, fmt.Sprintf("invalid image name: %s", imageName), http.StatusBadRequest)
		log.Printf("invalid image name: %s", imageName)
		return
	}

	_, pull := r.Form["pull"]

	tmpnb, err := srv.pool.newNotebook(imageName, pull)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		log.Print(err)
		return
	}

	proxyURL := url.URL{
		Scheme: "http",
		Host:   fmt.Sprintf("localhost:%d", tmpnb.port),
	}
	log.Printf("reverse proxy URL: %s", proxyURL.String())

	proxy := &httputil.ReverseProxy{
		Director: func(r *http.Request) {
			r.URL.Scheme = proxyURL.Scheme
			r.URL.Host = proxyURL.Host
		},
	}
	handlerPath := path.Join("/book", tmpnb.hash) + "/"
	log.Printf("handler: %s", handlerPath)
	srv.mux.HandleFunc(handlerPath, func(w http.ResponseWriter, r *http.Request) {
		tmpnb.lastAccessed = time.Now()
		log.Printf("%s [%s] %s [%s]", r.RemoteAddr, r.Method, r.RequestURI, r.UserAgent())
		if isWebsocket(r) {
			log.Print("proxying to websocket handler")
			f := websocketProxy(fmt.Sprintf(":%d", tmpnb.port))
			f.ServeHTTP(w, r)
			return
		}
		proxy.ServeHTTP(w, r)
	})
	// FIXME(kyle): check for valid connection on the tmpnb port
	time.Sleep(time.Second)
	handlerURL := url.URL{}
	handlerURL.Path = handlerPath
	q := url.Values{}
	q.Set("token", srv.token)
	handlerURL.RawQuery = q.Encode()
	fmt.Fprintln(w, "")
	fmt.Fprintf(w, `<a href="%s">wait a tick, then click</a>`, handlerURL.String())
	fmt.Fprintln(w, "")
}

// listImages lists html links to the different docker images.
func (srv *notebookServer) listImages(w http.ResponseWriter, r *http.Request) {
	page := `
  <!DOCTYPE HTML>

    {{range . -}}

{{.}}

    {{end -}}

  `

	tmpl, err := template.New("").Parse(page)
	if err != nil {
		log.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	images := []string{}
	for k := range srv.pool.availableImages {
		images = append(images, k)
	}
	sort.Slice(images, func(i, j int) bool {
		return images[i] < images[j]
	})
	err = tmpl.Execute(w, images)
	if err != nil {
		log.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// Start starts the http/s listener.
func (srv *notebookServer) Start() {
	if srv.tlsCert != "" && srv.tlsKey != "" {
		if srv.httpRedirect {
			httpServer := http.Server{}
			httpMux := http.NewServeMux()
			httpMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
				u := *r.URL
				u.Scheme = "https"
				u.Host = r.Host
				r.ParseForm()
				u.RawPath = r.Form.Encode()
				http.Redirect(w, r, u.String(), http.StatusPermanentRedirect)
			})
			httpServer.Handler = httpMux
			go func() {
				log.Fatal(httpServer.ListenAndServe())
			}()
		}
		log.Fatal(srv.ListenAndServeTLS(srv.tlsCert, srv.tlsKey))
	} else {
		log.Fatal(srv.ListenAndServe())
	}
}

func main() {
	cfg := ""
	if len(os.Args) > 1 {
		cfg = os.Args[1]
	}
	srv, err := newNotebookServer(cfg)
	if err != nil {
		log.Fatal(err)
	}
	srv.Start()
}
<commit_msg>add h1<commit_after>// Copyright (c) 2017, Boise State University All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"net/http/httputil"
	"net/http/pprof"
	"net/url"
	"os"
	"os/signal"
	"path"
	"sort"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
)

const (
	// defaultNotebook is used if the request doesn't specify a docker image.
	defaultNotebook = "jupyter/minimal-notebook"
)

func init() {
	log.SetFlags(log.Lshortfile | log.LstdFlags)
}

// TODO(kyle): embed or add to notebookServer?
type serverConfig struct {
	ContainerLifetime time.Duration `json:"container_lifetime"`
	EnablePProf       bool          `json:"enable_pprof"`
	ImageRegexp       string        `json:"image_regexp"`
	MaxContainers     int           `json:"max_containers"`
	HTTPRedirect      bool          `json:"http_redirect"`
	Port              string        `json:"port"`
	TLSCert           string        `json:"tls_cert"`
	TLSKey            string        `json:"tls_key"`
}

var defaultConfig = serverConfig{
	ContainerLifetime: defaultContainerLifetime,
	ImageRegexp:       allImageMatch,
	MaxContainers:     defaultMaxContainers,
}

// notebookServer handles the http tasks for the temporary notebooks.
type notebookServer struct {
	// pool manages the containers
	pool *notebookPool
	// token is the generated random auth token
	token string
	// mux routes http traffic
	mux *ServeMux
	// embed a server
	*http.Server
	// httpRedirect determines whether http redirects to https
	httpRedirect bool
	// TLS certificate path
	tlsCert string
	// TLS private key path
	tlsKey string
}

// readConfig reads json config from r
func readConfig(r io.Reader) (serverConfig, error) {
	sc := defaultConfig
	err := json.NewDecoder(r).Decode(&sc)
	return sc, err
}

// newNotebookServer initializes a server and owned resources, using a
// configuration if supplied.
func newNotebookServer(config string) (*notebookServer, error) {
	sc := defaultConfig
	if config != "" {
		fin, err := os.Open(config)
		if err != nil {
			return nil, err
		}
		sc, err = readConfig(fin)
		if err != nil {
			return nil, err
		}
	}
	p, err := newNotebookPool(sc.ImageRegexp, sc.MaxContainers, sc.ContainerLifetime)
	if err != nil {
		return nil, err
	}
	tkn := newHash(defaultHashSize)
	srv := &notebookServer{}
	srv.pool = p
	srv.token = tkn
	srv.pool.token = tkn
	srv.Server = &http.Server{
		Addr: sc.Port,
	}
	//srv.mux = http.NewServeMux()
	srv.mux = new(ServeMux)
	srv.mux.HandleFunc("/list", srv.listImages)
	srv.mux.HandleFunc("/new", srv.newNotebookHandler)
	srv.mux.HandleFunc("/status", srv.statusHandler)
	if sc.EnablePProf {
		srv.mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
		srv.mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
		srv.mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
		srv.mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
	}
	srv.Handler = srv.mux

	srv.tlsCert = sc.TLSCert
	srv.tlsKey = sc.TLSKey
	srv.httpRedirect = sc.HTTPRedirect

	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt)
	go func() {
		<-quit
		log.Println("Shutting down server...")
		err := srv.pool.releaseContainers(true)
		if err != nil {
			log.Print(err)
		}
		os.Exit(0)
	}()
	go func() {
		for {
			select {
			case p := <-srv.pool.deregisterMux:
				log.Printf("deregistering %s from mux", p)
				srv.mux.Deregister(p)
			}
		}
	}()
	return srv, nil
}

// statusHandler checks the status of a single container, returning 200 if it
// is running, another value otherwise.
func (srv *notebookServer) statusHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	id := r.FormValue("container")
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	filter := filters.NewArgs()
	if id != "" {
		filter.Add("id", id)
	}
	opts := types.ContainerListOptions{
		Filters: filter,
	}
	containers, err := cli.ContainerList(context.Background(), opts)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if len(containers) < 1 {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	state := containers[0].State
	log.Printf("container %s state: %s", id, state)
	if state == "running" {
		w.WriteHeader(http.StatusOK)
		return
	}
	if _, ok := r.Form["ping"]; ok {
		var tmpnb *tempNotebook
		for _, v := range srv.pool.containerMap {
			if v.id == id {
				tmpnb = v
				break
			}
		}
		if tmpnb == nil {
			log.Printf("couldn't find container in containerMap: %s", id)
			w.WriteHeader(http.StatusNotFound)
			return
		}
		pingURL := url.URL{
			Host: fmt.Sprintf(":%d", tmpnb.port),
			Path: path.Join("/book", tmpnb.hash),
		}
		resp, err := http.Get(pingURL.String())
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		resp.Body.Close()
		w.WriteHeader(resp.StatusCode)
		return
	}
	w.WriteHeader(http.StatusNotFound)
}

func (srv *notebookServer) newNotebookHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		log.Print(err)
		return
	}

	var imageName = r.FormValue("image")
	if imageName == "" {
		imageName = defaultNotebook
	}

	if _, ok := srv.pool.availableImages[imageName]; !ok {
		http.Error(w, fmt.Sprintf("invalid image name: %s", imageName), http.StatusBadRequest)
		log.Printf("invalid image name: %s", imageName)
		return
	}

	_, pull := r.Form["pull"]

	tmpnb, err := srv.pool.newNotebook(imageName, pull)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		log.Print(err)
		return
	}

	proxyURL := url.URL{
		Scheme: "http",
		Host:   fmt.Sprintf("localhost:%d", tmpnb.port),
	}
	log.Printf("reverse proxy URL: %s", proxyURL.String())

	proxy := &httputil.ReverseProxy{
		Director: func(r *http.Request) {
			r.URL.Scheme = proxyURL.Scheme
			r.URL.Host = proxyURL.Host
		},
	}
	handlerPath := path.Join("/book", tmpnb.hash) + "/"
	log.Printf("handler: %s", handlerPath)
	srv.mux.HandleFunc(handlerPath, func(w http.ResponseWriter, r *http.Request) {
		tmpnb.lastAccessed = time.Now()
		log.Printf("%s [%s] %s [%s]", r.RemoteAddr, r.Method, r.RequestURI, r.UserAgent())
		if isWebsocket(r) {
			log.Print("proxying to websocket handler")
			f := websocketProxy(fmt.Sprintf(":%d", tmpnb.port))
			f.ServeHTTP(w, r)
			return
		}
		proxy.ServeHTTP(w, r)
	})
	// FIXME(kyle): check for valid connection on the tmpnb port
	time.Sleep(time.Second)
	handlerURL := url.URL{}
	handlerURL.Path = handlerPath
	q := url.Values{}
	q.Set("token", srv.token)
	handlerURL.RawQuery = q.Encode()
	fmt.Fprintln(w, "")
	fmt.Fprintf(w, `<a href="%s">wait a tick, then click</a>`, handlerURL.String())
	fmt.Fprintln(w, "")
}

// listImages lists html links to the different docker images.
func (srv *notebookServer) listImages(w http.ResponseWriter, r *http.Request) {
	page := `
  <!DOCTYPE HTML>

Boise State University Code Lab

    {{range . -}}

{{.}}

    {{end -}}

  `

	tmpl, err := template.New("").Parse(page)
	if err != nil {
		log.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	images := []string{}
	for k := range srv.pool.availableImages {
		images = append(images, k)
	}
	sort.Slice(images, func(i, j int) bool {
		return images[i] < images[j]
	})
	err = tmpl.Execute(w, images)
	if err != nil {
		log.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// Start starts the http/s listener.
func (srv *notebookServer) Start() {
	if srv.tlsCert != "" && srv.tlsKey != "" {
		if srv.httpRedirect {
			httpServer := http.Server{}
			httpMux := http.NewServeMux()
			httpMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
				u := *r.URL
				u.Scheme = "https"
				u.Host = r.Host
				r.ParseForm()
				u.RawPath = r.Form.Encode()
				http.Redirect(w, r, u.String(), http.StatusPermanentRedirect)
			})
			httpServer.Handler = httpMux
			go func() {
				log.Fatal(httpServer.ListenAndServe())
			}()
		}
		log.Fatal(srv.ListenAndServeTLS(srv.tlsCert, srv.tlsKey))
	} else {
		log.Fatal(srv.ListenAndServe())
	}
}

func main() {
	cfg := ""
	if len(os.Args) > 1 {
		cfg = os.Args[1]
	}
	srv, err := newNotebookServer(cfg)
	if err != nil {
		log.Fatal(err)
	}
	srv.Start()
}
<|endoftext|>


<commit_before>package usrv

import (
	"log"
	"sync"

	"io/ioutil"

	"golang.org/x/net/context"
)

// Context keys.
const (
	CtxCurEndpoint = "cur_endpoint"
)

// An RPC server implementation.
type Server struct {

	// The root server context.
	ctx context.Context

	// A context-provided method for shutting down pending requests.
	cancel context.CancelFunc

	// The list of registered endpoints.
	endpoints []Endpoint

	// The transport used for handling requests.
	transport Transport

	// This waitgroup tracks ongoing requests so we can
	// properly drain them before terminating the server.
	pending sync.WaitGroup

	// Server event listeners.
	eventListeners []chan ServerEvent

	// transport close listener
	closeNotifChan chan error

	// The logger for server messages.
	Logger *log.Logger
}

// Create a new server with default settings. One or more ServerOption functional arguments
// may be specified to override defaults.
func NewServer(transport Transport, options ...ServerOption) (*Server, error) {
	ctx, cancel := context.WithCancel(context.Background())

	// Create server with default settings
	server := &Server{
		ctx:            ctx,
		cancel:         cancel,
		endpoints:      make([]Endpoint, 0),
		transport:      transport,
		Logger:         log.New(ioutil.Discard, "", log.LstdFlags),
		eventListeners: make([]chan ServerEvent, 0),
	}

	return server, server.SetOption(options...)
}

// Set one or more ServerOptions.
func (srv *Server) SetOption(options ...ServerOption) error {
	// Apply any options
	for _, opt := range options {
		if err := opt(srv); err != nil {
			return err
		}
	}

	return nil
}

// Register a handler for requests to RPC endpoint identified by path. One
// or more EndpointOption functional arguments may be specified to further
// customize the endpoint by applying for example middleware.
func (srv *Server) Handle(path string, handler Handler, options ...EndpointOption) error {
	ep := Endpoint{path, handler}

	// Apply any options
	for _, opt := range options {
		err := opt(&ep)
		if err != nil {
			return err
		}
	}

	// Append to endpoint list
	srv.endpoints = append(srv.endpoints, ep)
	srv.emitEvent(EvtRegistered, ep.Name)

	return nil
}

// Dial the registered transport provider and then call Serve()
// to start processing incoming RPC requests.
func (srv *Server) ListenAndServe() error {
	var err error

	// Allocate and register close listener
	srv.closeNotifChan = make(chan error, 1)
	srv.transport.NotifyClose(srv.closeNotifChan)

	// Connect to transport
	err = srv.transport.Dial()
	if err != nil {
		return err
	}

	return srv.serve()
}

// Bind each defined endpoint to the transport and start processing incoming requests.
// This function will block until Close() is invoked.
func (srv *Server) serve() error {

	for _, ep := range srv.endpoints {

		binding, err := srv.transport.Bind(ServerBinding, ep.Name)
		if err != nil {
			srv.Close()
			return err
		}

		go func(binding *Binding, ep Endpoint) {
			srv.serveEndpoint(binding, &ep)
		}(binding, ep)
	}

	srv.emitEvent(EvtStarted, "")

	select {
	case <-srv.ctx.Done():
		// server is shutting down
	case _, normalShutdown := <-srv.closeNotifChan:
		if normalShutdown {
			srv.Logger.Printf("Transport shutdown complete")
			break
		}

		// We lost our connection. We should attempt to reconnect and rebind
		srv.emitEvent(EvtTransportReset, "")
		srv.Logger.Printf("Transport connection reset; attempting to reconnect")
		err := srv.ListenAndServe()
		if err != nil {
			srv.Logger.Panic(err)
		}
	}

	return nil
}

// Implement a message processing loop for a bound endpoint. A separate go-routine will
// be spawned for each incoming message. This function exit if the server is closed or
// the transport connection is lost.
func (srv *Server) serveEndpoint(binding *Binding, ep *Endpoint) {
	srv.emitEvent(EvtServing, ep.Name)

	for {
		select {
		case trMsg, ok := <-binding.Messages:
			if !ok {
				srv.Logger.Printf("Lost transport binding for endpoint %s", ep.Name)
				return
			}

			// Spawn go-routing to handle request
			srv.pending.Add(1)
			go func() {
				defer srv.pending.Done()

				// Create a new context with our endpoint name
				rctx := context.WithValue(srv.ctx, CtxCurEndpoint, ep.Name)

				// Serve the request and flush the response writer
				ep.Handler.Serve(rctx, trMsg.ResponseWriter, trMsg.Message)

				err := trMsg.ResponseWriter.Close()
				if err != nil {
					srv.Logger.Printf("Error flushing response: %s", err.Error())
				}

			}()
		}
	}
}

// Shutdown the server. This function will unbind any bound endpoints and block until
// any pending requests have been drained.
func (srv *Server) Close() {

	srv.emitEvent(EvtStopping, "")
	srv.Logger.Printf("Server shutdown in progress; waiting for pending requests to drain\n")

	// Stop processing further requests and shutdown transport
	srv.cancel()

	// Drain pending requests
	srv.pending.Wait()

	srv.emitEvent(EvtStopped, "")
	srv.Logger.Printf("Server shutdown complete\n")
}

// Emit server event in non-blocking mode. If the event cannot be sent to a
// registered listener channel then that particular listener will be skipped.
func (srv *Server) emitEvent(evtType EventType, endpoint string) {
	event := ServerEvent{
		Type:     evtType,
		Endpoint: endpoint,
	}

	for _, listener := range srv.eventListeners {
		select {
		case listener <- event:
			// Wrote event
		default:
			// Skip
		}
	}
}
<commit_msg>Check reason for close notification<commit_after>package usrv

import (
	"log"
	"sync"

	"io/ioutil"

	"github.com/achilleasa/usrv-service-adapters"
	"golang.org/x/net/context"
)

// Context keys.
const (
	CtxCurEndpoint = "cur_endpoint"
)

// An RPC server implementation.
type Server struct {

	// The root server context.
	ctx context.Context

	// A context-provided method for shutting down pending requests.
	cancel context.CancelFunc

	// The list of registered endpoints.
	endpoints []Endpoint

	// The transport used for handling requests.
	transport Transport

	// This waitgroup tracks ongoing requests so we can
	// properly drain them before terminating the server.
	pending sync.WaitGroup

	// Server event listeners.
	eventListeners []chan ServerEvent

	// transport close listener
	closeNotifChan chan error

	// The logger for server messages.
	Logger *log.Logger
}

// Create a new server with default settings. One or more ServerOption functional arguments
// may be specified to override defaults.
func NewServer(transport Transport, options ...ServerOption) (*Server, error) {
	ctx, cancel := context.WithCancel(context.Background())

	// Create server with default settings
	server := &Server{
		ctx:            ctx,
		cancel:         cancel,
		endpoints:      make([]Endpoint, 0),
		transport:      transport,
		Logger:         log.New(ioutil.Discard, "", log.LstdFlags),
		eventListeners: make([]chan ServerEvent, 0),
	}

	return server, server.SetOption(options...)
}

// Set one or more ServerOptions.
func (srv *Server) SetOption(options ...ServerOption) error {
	// Apply any options
	for _, opt := range options {
		if err := opt(srv); err != nil {
			return err
		}
	}

	return nil
}

// Register a handler for requests to RPC endpoint identified by path. One
// or more EndpointOption functional arguments may be specified to further
// customize the endpoint by applying for example middleware.
func (srv *Server) Handle(path string, handler Handler, options ...EndpointOption) error {
	ep := Endpoint{path, handler}

	// Apply any options
	for _, opt := range options {
		err := opt(&ep)
		if err != nil {
			return err
		}
	}

	// Append to endpoint list
	srv.endpoints = append(srv.endpoints, ep)
	srv.emitEvent(EvtRegistered, ep.Name)

	return nil
}

// Dial the registered transport provider and then call Serve()
// to start processing incoming RPC requests.
func (srv *Server) ListenAndServe() error {
	var err error

	// Allocate and register close listener
	srv.closeNotifChan = make(chan error, 1)
	srv.transport.NotifyClose(srv.closeNotifChan)

	// Connect to transport
	err = srv.transport.Dial()
	if err != nil {
		return err
	}

	return srv.serve()
}

// Bind each defined endpoint to the transport and start processing incoming requests.
// This function will block until Close() is invoked.
func (srv *Server) serve() error {

	for _, ep := range srv.endpoints {

		binding, err := srv.transport.Bind(ServerBinding, ep.Name)
		if err != nil {
			srv.Close()
			return err
		}

		go func(binding *Binding, ep Endpoint) {
			srv.serveEndpoint(binding, &ep)
		}(binding, ep)
	}

	srv.emitEvent(EvtStarted, "")

	select {
	case <-srv.ctx.Done():
		// server is shutting down
	case reason := <-srv.closeNotifChan:
		if reason == adapters.ErrConnectionClosed {
			srv.Logger.Printf("Transport shutdown complete")
			break
		}

		// We lost our connection. We should attempt to reconnect and rebind
		srv.emitEvent(EvtTransportReset, "")
		srv.Logger.Printf("Transport connection reset; attempting to reconnect")
		err := srv.ListenAndServe()
		if err != nil {
			srv.Logger.Panic(err)
		}
	}

	return nil
}

// Implement a message processing loop for a bound endpoint. A separate go-routine will
// be spawned for each incoming message. This function exit if the server is closed or
// the transport connection is lost.
func (srv *Server) serveEndpoint(binding *Binding, ep *Endpoint) {
	srv.emitEvent(EvtServing, ep.Name)

	for {
		select {
		case trMsg, ok := <-binding.Messages:
			if !ok {
				srv.Logger.Printf("Lost transport binding for endpoint %s", ep.Name)
				return
			}

			// Spawn go-routing to handle request
			srv.pending.Add(1)
			go func() {
				defer srv.pending.Done()

				// Create a new context with our endpoint name
				rctx := context.WithValue(srv.ctx, CtxCurEndpoint, ep.Name)

				// Serve the request and flush the response writer
				ep.Handler.Serve(rctx, trMsg.ResponseWriter, trMsg.Message)

				err := trMsg.ResponseWriter.Close()
				if err != nil {
					srv.Logger.Printf("Error flushing response: %s", err.Error())
				}

			}()
		}
	}
}

// Shutdown the server. This function will unbind any bound endpoints and block until
// any pending requests have been drained.
func (srv *Server) Close() {

	srv.emitEvent(EvtStopping, "")
	srv.Logger.Printf("Server shutdown in progress; waiting for pending requests to drain\n")

	// Stop processing further requests and shutdown transport
	srv.cancel()

	// Drain pending requests
	srv.pending.Wait()

	srv.emitEvent(EvtStopped, "")
	srv.Logger.Printf("Server shutdown complete\n")
}

// Emit server event in non-blocking mode. If the event cannot be sent to a
// registered listener channel then that particular listener will be skipped.
func (srv *Server) emitEvent(evtType EventType, endpoint string) {
	event := ServerEvent{
		Type:     evtType,
		Endpoint: endpoint,
	}

	for _, listener := range srv.eventListeners {
		select {
		case listener <- event:
			// Wrote event
		default:
			// Skip
		}
	}
}
<|endoftext|>


<commit_before>package air

import (
	"net"
	"sync"
	"time"

	"github.com/valyala/fasthttp"
)

type (
	// Server defines the interface for HTTP server.
	Server interface {
		// SetHandler sets the handler for the HTTP server.
		SetHandler(Handler)

		// SetLogger sets the logger for the HTTP server.
		SetLogger(Logger)

		// Start starts the HTTP server.
		Start() error
	}

	fastServer struct {
		*fasthttp.Server
		config  Config
		handler Handler
		logger  Logger
		pool    *pool
	}

	// Config defines fasthttp config.
	Config struct {
		Address      string        // TCP address to listen on.
		Listener     net.Listener  // Custom `net.Listener`. If set, server accepts connections on it.
		TLSCertFile  string        // TLS certificate file path.
		TLSKeyFile   string        // TLS key file path.
		ReadTimeout  time.Duration // Maximum duration before timing out read of the request.
		WriteTimeout time.Duration // Maximum duration before timing out write of the response.
	}

	// Handler defines an interface to server HTTP requests via `ServeHTTP(Request, Response)`
	// function.
	Handler interface {
		ServeHTTP(Request, Response)
	}

	// FastHandlerFunc is an adapter to allow the use of `func(Request, Response)` as
	// an HTTP handler.
	FastHandlerFunc func(Request, Response)

	pool struct {
		request        sync.Pool
		response       sync.Pool
		requestHeader  sync.Pool
		responseHeader sync.Pool
		uri            sync.Pool
	}
)

// ServeHTTP serves HTTP request.
func (h FastHandlerFunc) ServeHTTP(req Request, res Response) {
	h(req, res)
}

// NewServer returns `Server` with provided listen address.
func NewServer(addr string) Server {
	c := Config{Address: addr}
	return WithConfig(c)
}

// NewServerWithTLS returns `Server` with provided TLS config.
func NewServerWithTLS(addr, certFile, keyFile string) Server {
	c := Config{
		Address:     addr,
		TLSCertFile: certFile,
		TLSKeyFile:  keyFile,
	}
	return WithConfig(c)
}

// NewServerWithConfig returns `Server` with provided config.
func NewServerWithConfig(c Config) Server {
	s := &fastServer{
		Server: new(fasthttp.Server),
		config: c,
		logger: NewLogger("air"),
	}
	s.pool = &pool{
		request: sync.Pool{
			New: func() interface{} {
				return &fastRequest{logger: s.logger}
			},
		},
		response: sync.Pool{
			New: func() interface{} {
				return &fastResponse{logger: s.logger}
			},
		},
		requestHeader: sync.Pool{
			New: func() interface{} {
				return &fastRequestHeader{}
			},
		},
		responseHeader: sync.Pool{
			New: func() interface{} {
				return &fastResponseHeader{}
			},
		},
		uri: sync.Pool{
			New: func() interface{} {
				return &fastURI{}
			},
		},
	}
	s.handler = FastHandlerFunc(func(req Request, res Response) {
		s.logger.Error("handler not set, use `SetHandler()` to set it.")
	})
	s.ReadTimeout = c.ReadTimeout
	s.WriteTimeout = c.WriteTimeout
	s.Handler = s.ServeHTTP
	return s
}

func (s *fastServer) SetHandler(h Handler) {
	s.handler = h
}

func (s *fastServer) SetLogger(l Logger) {
	s.logger = l
}

func (s *fastServer) Start() error {
	if s.config.Listener == nil {
		return s.startDefaultListener()
	}
	return s.startCustomListener()

}

func (s *fastServer) startDefaultListener() error {
	c := s.config
	if c.TLSCertFile != "" && c.TLSKeyFile != "" {
		return s.ListenAndServeTLS(c.Address, c.TLSCertFile, c.TLSKeyFile)
	}
	return s.ListenAndServe(c.Address)
}

func (s *fastServer) startCustomListener() error {
	c := s.config
	if c.TLSCertFile != "" && c.TLSKeyFile != "" {
		return s.ServeTLS(c.Listener, c.TLSCertFile, c.TLSKeyFile)
	}
	return s.Serve(c.Listener)
}

func (s *fastServer) ServeHTTP(c *fasthttp.RequestCtx) {
	// Request
	req := s.pool.request.Get().(*fastRequest)
	reqHdr := s.pool.requestHeader.Get().(*fastRequestHeader)
	reqURI := s.pool.uri.Get().(*fastURI)
	reqHdr.reset(&c.Request.Header)
	reqURI.reset(c.URI())
	req.reset(c, reqHdr, reqURI)

	// Response
	res := s.pool.response.Get().(*fastResponse)
	resHdr := s.pool.responseHeader.Get().(*fastResponseHeader)
	resHdr.reset(&c.Response.Header)
	res.reset(c, resHdr)

	s.handler.ServeHTTP(req, res)

	// Return to pool
	s.pool.request.Put(req)
	s.pool.requestHeader.Put(reqHdr)
	s.pool.uri.Put(reqURI)
	s.pool.response.Put(res)
	s.pool.responseHeader.Put(resHdr)
}

// FastWrapHandler wraps `fasthttp.RequestHandler` into `HandlerFunc`.
func FastWrapHandler(h fasthttp.RequestHandler) HandlerFunc {
	return func(c Context) error {
		req := c.Request().(*fastRequest)
		res := c.Response().(*fastResponse)
		ctx := req.RequestCtx
		h(ctx)
		res.status = ctx.Response.StatusCode()
		res.size = int64(ctx.Response.Header.ContentLength())
		return nil
	}
}

// FastWrapGas wraps `func(fasthttp.RequestHandler) fasthttp.RequestHandler`
// into `GasFunc`
func FastWrapGas(m func(fasthttp.RequestHandler) fasthttp.RequestHandler) GasFunc {
	return func(next HandlerFunc) HandlerFunc {
		return func(c Context) (err error) {
			req := c.Request().(*fastRequest)
			res := c.Response().(*fastResponse)
			ctx := req.RequestCtx
			m(func(ctx *fasthttp.RequestCtx) {
				next(c)
			})(ctx)
			res.status = ctx.Response.StatusCode()
			res.size = int64(ctx.Response.Header.ContentLength())
			return
		}
	}
}
<commit_msg>Fix NewServer bugs<commit_after>package air

import (
	"net"
	"sync"
	"time"

	"github.com/valyala/fasthttp"
)

type (
	// Server defines the interface for HTTP server.
	Server interface {
		// SetHandler sets the handler for the HTTP server.
		SetHandler(Handler)

		// SetLogger sets the logger for the HTTP server.
		SetLogger(Logger)

		// Start starts the HTTP server.
		Start() error
	}

	fastServer struct {
		*fasthttp.Server
		config  Config
		handler Handler
		logger  Logger
		pool    *pool
	}

	// Config defines fasthttp config.
	Config struct {
		Address      string        // TCP address to listen on.
		Listener     net.Listener  // Custom `net.Listener`. If set, server accepts connections on it.
		TLSCertFile  string        // TLS certificate file path.
		TLSKeyFile   string        // TLS key file path.
		ReadTimeout  time.Duration // Maximum duration before timing out read of the request.
		WriteTimeout time.Duration // Maximum duration before timing out write of the response.
	}

	// Handler defines an interface to server HTTP requests via `ServeHTTP(Request, Response)`
	// function.
	Handler interface {
		ServeHTTP(Request, Response)
	}

	// FastHandlerFunc is an adapter to allow the use of `func(Request, Response)` as
	// an HTTP handler.
	FastHandlerFunc func(Request, Response)

	pool struct {
		request        sync.Pool
		response       sync.Pool
		requestHeader  sync.Pool
		responseHeader sync.Pool
		uri            sync.Pool
	}
)

// ServeHTTP serves HTTP request.
func (h FastHandlerFunc) ServeHTTP(req Request, res Response) {
	h(req, res)
}

// NewServer returns `Server` with provided listen address.
func NewServer(addr string) Server {
	c := Config{Address: addr}
	return NewServerWithConfig(c)
}

// NewServerWithTLS returns `Server` with provided TLS config.
func NewServerWithTLS(addr, certFile, keyFile string) Server {
	c := Config{
		Address:     addr,
		TLSCertFile: certFile,
		TLSKeyFile:  keyFile,
	}
	return NewServerWithConfig(c)
}

// NewServerWithConfig returns `Server` with provided config.
func NewServerWithConfig(c Config) Server {
	s := &fastServer{
		Server: new(fasthttp.Server),
		config: c,
		logger: NewLogger("air"),
	}
	s.pool = &pool{
		request: sync.Pool{
			New: func() interface{} {
				return &fastRequest{logger: s.logger}
			},
		},
		response: sync.Pool{
			New: func() interface{} {
				return &fastResponse{logger: s.logger}
			},
		},
		requestHeader: sync.Pool{
			New: func() interface{} {
				return &fastRequestHeader{}
			},
		},
		responseHeader: sync.Pool{
			New: func() interface{} {
				return &fastResponseHeader{}
			},
		},
		uri: sync.Pool{
			New: func() interface{} {
				return &fastURI{}
			},
		},
	}
	s.handler = FastHandlerFunc(func(req Request, res Response) {
		s.logger.Error("handler not set, use `SetHandler()` to set it.")
	})
	s.ReadTimeout = c.ReadTimeout
	s.WriteTimeout = c.WriteTimeout
	s.Handler = s.ServeHTTP
	return s
}

func (s *fastServer) SetHandler(h Handler) {
	s.handler = h
}

func (s *fastServer) SetLogger(l Logger) {
	s.logger = l
}

func (s *fastServer) Start() error {
	if s.config.Listener == nil {
		return s.startDefaultListener()
	}
	return s.startCustomListener()

}

func (s *fastServer) startDefaultListener() error {
	c := s.config
	if c.TLSCertFile != "" && c.TLSKeyFile != "" {
		return s.ListenAndServeTLS(c.Address, c.TLSCertFile, c.TLSKeyFile)
	}
	return s.ListenAndServe(c.Address)
}

func (s *fastServer) startCustomListener() error {
	c := s.config
	if c.TLSCertFile != "" && c.TLSKeyFile != "" {
		return s.ServeTLS(c.Listener, c.TLSCertFile, c.TLSKeyFile)
	}
	return s.Serve(c.Listener)
}

func (s *fastServer) ServeHTTP(c *fasthttp.RequestCtx) {
	// Request
	req := s.pool.request.Get().(*fastRequest)
	reqHdr := s.pool.requestHeader.Get().(*fastRequestHeader)
	reqURI := s.pool.uri.Get().(*fastURI)
	reqHdr.reset(&c.Request.Header)
	reqURI.reset(c.URI())
	req.reset(c, reqHdr, reqURI)

	// Response
	res := s.pool.response.Get().(*fastResponse)
	resHdr := s.pool.responseHeader.Get().(*fastResponseHeader)
	resHdr.reset(&c.Response.Header)
	res.reset(c, resHdr)

	s.handler.ServeHTTP(req, res)

	// Return to pool
	s.pool.request.Put(req)
	s.pool.requestHeader.Put(reqHdr)
	s.pool.uri.Put(reqURI)
	s.pool.response.Put(res)
	s.pool.responseHeader.Put(resHdr)
}

// FastWrapHandler wraps `fasthttp.RequestHandler` into `HandlerFunc`.
func FastWrapHandler(h fasthttp.RequestHandler) HandlerFunc {
	return func(c Context) error {
		req := c.Request().(*fastRequest)
		res := c.Response().(*fastResponse)
		ctx := req.RequestCtx
		h(ctx)
		res.status = ctx.Response.StatusCode()
		res.size = int64(ctx.Response.Header.ContentLength())
		return nil
	}
}

// FastWrapGas wraps `func(fasthttp.RequestHandler) fasthttp.RequestHandler`
// into `GasFunc`
func FastWrapGas(m func(fasthttp.RequestHandler) fasthttp.RequestHandler) GasFunc {
	return func(next HandlerFunc) HandlerFunc {
		return func(c Context) (err error) {
			req := c.Request().(*fastRequest)
			res := c.Response().(*fastResponse)
			ctx := req.RequestCtx
			m(func(ctx *fasthttp.RequestCtx) {
				next(c)
			})(ctx)
			res.status = ctx.Response.StatusCode()
			res.size = int64(ctx.Response.Header.ContentLength())
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"crypto/x509"
	"net"
)

// Listener accepts connections from devices.
type Listener struct {
	debug    bool
	listener *net.Listener
}

// Listen creates a listener with the given PEM encoded X.509 certificate and the private key on the local network address laddr.
// Debug mode logs all server activity.
func Listen(cert, priv []byte, laddr string, debug bool) (*Listener, error) {
	c, err := x509.ParseCertificate(cert)
	p, err := x509.ParsePKCS1PrivateKey(priv)
	if err != nil {
		return nil, err
	}

	pool := x509.NewCertPool()
	pool.AddCert(c)

	tlsCert := tls.Certificate{
		Certificate: [][]byte{cert},
		PrivateKey:  p,
	}

	config := tls.Config{
		Certificates: []tls.Certificate{tlsCert},
		ClientCAs:    pool,
	}

	listener, err := tls.Listen("tcp", laddr, &config)
	if err != nil {
		return nil, err
	}

	return &Listener{
		debug:    debug,
		listener: &listener,
	}, nil
}
<commit_msg>add default laddr<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"log"
	"net"
)

// Listener accepts connections from devices.
type Listener struct {
	debug    bool
	listener *net.Listener
}

// Listen creates a listener with the given PEM encoded X.509 certificate and the private key on the local network address laddr.
// Debug mode logs all server activity.
func Listen(cert, priv []byte, laddr string, debug bool) (*Listener, error) {
	c, err := x509.ParseCertificate(cert)
	p, err := x509.ParsePKCS1PrivateKey(priv)
	if err != nil {
		return nil, fmt.Errorf("failed to parse the certificate or the private key with error: %v", err)
	}

	pool := x509.NewCertPool()
	pool.AddCert(c)

	tlsCert := tls.Certificate{
		Certificate: [][]byte{cert},
		PrivateKey:  p,
	}

	config := tls.Config{
		Certificates: []tls.Certificate{tlsCert},
		ClientCAs:    pool,
	}

	if laddr == "" {
		laddr = "0.0.0.0:443"
	}

	listener, err := tls.Listen("tcp", laddr, &config)
	if err != nil {
		return nil, err
	}

	if debug {
		log.Printf("New CCS connection established with XMPP parameters: %+v\n", c)
	}

	return &Listener{
		debug:    debug,
		listener: &listener,
	}, nil
}
<|endoftext|>


<commit_before>package http

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"time"
)

var ErrLog *log.Logger

var HTTP_METHOD = map[string]string{
	"GET": "GET",
	//	"POST":    "POST",
	//	"HEAD":    "HEAD",
	//	"PUT":     "PUT",
	//	"TRACE":   "TRACE",
	"OPTIONS": "OPTIONS",
	//	"DELETE":  "DELETE",
}

type Address struct {
	Host string
	Port string
}

func NewAddress(addr_str string) *Address {
	addr := new(Address)
	addr_strs := strings.Split(addr_str, ":")
	if len(addr_strs) == 2 {
		addr.Host, addr.Port = addr_strs[0], addr_strs[1]
	}
	return addr
}

func (this *Address) String() string {
	return this.Host + ":" + this.Port
}

type Server struct {
	Addr             *Address
	Routes           *Route
	AllowHttpMethods []string
}

var DEFAULT_SERVER *Server
var ViewsTemplFiles map[string]string
var AppPath string
var ViewPath string

func Serve(addr string) {
	DEFAULT_SERVER.Addr = NewAddress(addr)
	ln, err := net.Listen("tcp", DEFAULT_SERVER.Addr.String())
	if err != nil {
		panic(err)
	}
	defer ln.Close()

	log.Printf("<<<Server Accepting on Port %s>>>\n", DEFAULT_SERVER.Addr.Port)
	for {
		conn, err := ln.Accept()
		if err != nil {
			ErrLog.Println(err)
		}
		go handleConnection(conn)
	}
}

func init() {
	log.SetFlags(0)
	log.Println(CYEAM_LOG)
	errlogFile, logErr := os.OpenFile("error.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)

	if logErr != nil {
		fmt.Println("Fail to find", "error.log", " start Failed")
	}

	ErrLog = log.New(errlogFile, "", log.LstdFlags|log.Llongfile)

	DEFAULT_SERVER = new(Server)

	AppPath, _ = filepath.Abs(filepath.Dir(os.Args[0]))
	ViewPath = AppPath + "/views"
	ViewsTemplFiles = make(map[string]string)

	views, _ := ioutil.ReadDir(ViewPath)
	for _, view := range views {
		ViewsTemplFiles[view.Name()] = ViewPath + "/" + view.Name()
	}

	DEFAULT_SERVER.Routes = NewRoute()

	Router("/", "OPTIONS", &Controller{}, "Option")
	Router("/favicon.ico", "GET", &Controller{}, "Favicon")
}

func handleConnection(conn net.Conn) {
	serve_time := time.Now()

	defer conn.Close()

	ctx := NewContext()
	ctx.ReqAddr = NewAddress(conn.RemoteAddr().String())
	ctx.Req = NewRequest()
	ctx.Resp = NewResponse()

	for {
		buf := make([]byte, 1024)
		reqLen, err := conn.Read(buf)
		if reqLen > 0 && err == nil {
			ctx.Req.Raw.Write(buf)
			if reqLen < len(buf) && err == nil {
				break
			}
		} else if err == io.EOF {
			return
		} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
			break
		} else {
			ErrLog.Println("Error to read message because of ", err, reqLen)
			ctx.Resp.StatusCode = StatusInternalServerError
			break
		}
	}

	ctx.Req.Init()
	if ctx.Req.Headers.Get(HTTP_HEAD_X_FORWARDED_FOR) != "" {
		ctx.ReqAddr = NewAddress(ctx.Req.Headers.Get(HTTP_HEAD_X_FORWARDED_FOR))
	}
	ctx.Resp.Proto = ctx.Req.Proto
	if DEFAULT_SERVER.Routes.routes[ctx.Req.Method][ctx.Req.Url.Path] != nil {
		DEFAULT_SERVER.Routes.routes[ctx.Req.Method][ctx.Req.Url.Path].ServeHTTP(ctx)
	} else {
		if _, exists := HTTP_METHOD[ctx.Req.Method]; !exists {
			ctx.Resp.StatusCode = StatusMethodNotAllowed
		} else {
			ctx.Resp.StatusCode = StatusNotFound
		}
	}

	if ctx.Resp.StatusCode == StatusNotFound {
		ctx.Resp.Body = DEFAULT_ERROR_PAGE
	}
	ctx.Resp.Headers.Add(HTTP_HEAD_DATE, serve_time.Format(time.RFC1123))
	ctx.Resp.Headers.Add(HTTP_HEAD_CONTENTLENGTH, fmt.Sprintf("%d", len(ctx.Resp.Body)))

	buffers := bytes.Buffer{}
	buffers.WriteString(fmt.Sprintf("%s %d %s\r\n", ctx.Resp.Proto, ctx.Resp.StatusCode, StatusText(ctx.Resp.StatusCode)))
	for k, v := range ctx.Resp.Headers {
		for _, vv := range v {
			buffers.WriteString(fmt.Sprintf("%s: %s\r\n", k, vv))
		}
	}
	buffers.WriteString(CRLF)
	buffers.WriteString(ctx.Resp.Body)
	_, err := conn.Write(buffers.Bytes())
	if err != nil {
		ErrLog.Println(err)
	}
	//	ctx.elapse = time.Now().Sub(serve_time)
	log.Println(ctx.Req.Headers.Get(HTTP_HEAD_X_FORWARDED_FOR), fmt.Sprintf(LOG_CONTEXT, ctx.ReqAddr.String(), "-", serve_time.Format(LOG_TIME_FORMAT), ctx.Req.Method, ctx.Req.Url.RawPath, ctx.Req.Proto, ctx.Resp.StatusCode, len(ctx.Req.Body), "-", ctx.Req.UserAgent, 0))
}

func Router(path string, method string, ctrl ControllerIfac, methodName string) {
	if _, exists := HTTP_METHOD[method]; !exists {
		ErrLog.Println("Method not allowed", method, path, methodName)
		return
	}
	handler := new(Handle)
	handler.ctrl = ctrl
	handler.methodName = methodName
	handler.fn = reflect.ValueOf(handler.ctrl).MethodByName(handler.methodName)
	DEFAULT_SERVER.Routes.routes[method][path] = handler
}

var DEFAULT_ERROR_PAGE = ""
<commit_msg>addr fix<commit_after>package http

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"time"
)

var ErrLog *log.Logger

var HTTP_METHOD = map[string]string{
	"GET": "GET",
	//	"POST":    "POST",
	//	"HEAD":    "HEAD",
	//	"PUT":     "PUT",
	//	"TRACE":   "TRACE",
	"OPTIONS": "OPTIONS",
	//	"DELETE":  "DELETE",
}

type Address struct {
	Host string
	Port string
}

func NewAddress(addr_str string) *Address {
	addr := new(Address)
	addr_strs := strings.Split(addr_str, ":")
	if len(addr_strs) == 2 {
		addr.Host, addr.Port = addr_strs[0], addr_strs[1]
	} else {
		addr.Host = addr_str
	}
	return addr
}

func (this *Address) String() string {
	return this.Host + ":" + this.Port
}

type Server struct {
	Addr             *Address
	Routes           *Route
	AllowHttpMethods []string
}

var DEFAULT_SERVER *Server
var ViewsTemplFiles map[string]string
var AppPath string
var ViewPath string

func Serve(addr string) {
	DEFAULT_SERVER.Addr = NewAddress(addr)
	ln, err := net.Listen("tcp", DEFAULT_SERVER.Addr.String())
	if err != nil {
		panic(err)
	}
	defer ln.Close()

	log.Printf("<<<Server Accepting on Port %s>>>\n", DEFAULT_SERVER.Addr.Port)
	for {
		conn, err := ln.Accept()
		if err != nil {
			ErrLog.Println(err)
		}
		go handleConnection(conn)
	}
}

func init() {
	log.SetFlags(0)
	log.Println(CYEAM_LOG)
	errlogFile, logErr := os.OpenFile("error.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)

	if logErr != nil {
		fmt.Println("Fail to find", "error.log", " start Failed")
	}

	ErrLog = log.New(errlogFile, "", log.LstdFlags|log.Llongfile)

	DEFAULT_SERVER = new(Server)

	AppPath, _ = filepath.Abs(filepath.Dir(os.Args[0]))
	ViewPath = AppPath + "/views"
	ViewsTemplFiles = make(map[string]string)

	views, _ := ioutil.ReadDir(ViewPath)
	for _, view := range views {
		ViewsTemplFiles[view.Name()] = ViewPath + "/" + view.Name()
	}

	DEFAULT_SERVER.Routes = NewRoute()

	Router("/", "OPTIONS", &Controller{}, "Option")
	Router("/favicon.ico", "GET", &Controller{}, "Favicon")
}

func handleConnection(conn net.Conn) {
	serve_time := time.Now()

	defer conn.Close()

	ctx := NewContext()
	ctx.ReqAddr = NewAddress(conn.RemoteAddr().String())
	ctx.Req = NewRequest()
	ctx.Resp = NewResponse()

	for {
		buf := make([]byte, 1024)
		reqLen, err := conn.Read(buf)
		if reqLen > 0 && err == nil {
			ctx.Req.Raw.Write(buf)
			if reqLen < len(buf) && err == nil {
				break
			}
		} else if err == io.EOF {
			return
		} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
			break
		} else {
			ErrLog.Println("Error to read message because of ", err, reqLen)
			ctx.Resp.StatusCode = StatusInternalServerError
			break
		}
	}

	ctx.Req.Init()
	if ctx.Req.Headers.Get(HTTP_HEAD_X_FORWARDED_FOR) != "" {
		ctx.ReqAddr = NewAddress(ctx.Req.Headers.Get(HTTP_HEAD_X_FORWARDED_FOR))
	}
	ctx.Resp.Proto = ctx.Req.Proto
	if DEFAULT_SERVER.Routes.routes[ctx.Req.Method][ctx.Req.Url.Path] != nil {
		DEFAULT_SERVER.Routes.routes[ctx.Req.Method][ctx.Req.Url.Path].ServeHTTP(ctx)
	} else {
		if _, exists := HTTP_METHOD[ctx.Req.Method]; !exists {
			ctx.Resp.StatusCode = StatusMethodNotAllowed
		} else {
			ctx.Resp.StatusCode = StatusNotFound
		}
	}

	if ctx.Resp.StatusCode == StatusNotFound {
		ctx.Resp.Body = DEFAULT_ERROR_PAGE
	}
	ctx.Resp.Headers.Add(HTTP_HEAD_DATE, serve_time.Format(time.RFC1123))
	ctx.Resp.Headers.Add(HTTP_HEAD_CONTENTLENGTH, fmt.Sprintf("%d", len(ctx.Resp.Body)))

	buffers := bytes.Buffer{}
	buffers.WriteString(fmt.Sprintf("%s %d %s\r\n", ctx.Resp.Proto, ctx.Resp.StatusCode, StatusText(ctx.Resp.StatusCode)))
	for k, v := range ctx.Resp.Headers {
		for _, vv := range v {
			buffers.WriteString(fmt.Sprintf("%s: %s\r\n", k, vv))
		}
	}
	buffers.WriteString(CRLF)
	buffers.WriteString(ctx.Resp.Body)
	_, err := conn.Write(buffers.Bytes())
	if err != nil {
		ErrLog.Println(err)
	}
	//	ctx.elapse = time.Now().Sub(serve_time)
	log.Println(ctx.Req.Headers.Get(HTTP_HEAD_X_FORWARDED_FOR), fmt.Sprintf(LOG_CONTEXT, ctx.ReqAddr.String(), "-", serve_time.Format(LOG_TIME_FORMAT), ctx.Req.Method, ctx.Req.Url.RawPath, ctx.Req.Proto, ctx.Resp.StatusCode, len(ctx.Req.Body), "-", ctx.Req.UserAgent, 0))
}

func Router(path string, method string, ctrl ControllerIfac, methodName string) {
	if _, exists := HTTP_METHOD[method]; !exists {
		ErrLog.Println("Method not allowed", method, path, methodName)
		return
	}
	handler := new(Handle)
	handler.ctrl = ctrl
	handler.methodName = methodName
	handler.fn = reflect.ValueOf(handler.ctrl).MethodByName(handler.methodName)
	DEFAULT_SERVER.Routes.routes[method][path] = handler
}

var DEFAULT_ERROR_PAGE = ""
<|endoftext|>