<commit_before>package agent

import (
	"bytes"
	"fmt"
	"github.com/armon/go-metrics"
	"github.com/hashicorp/serf/serf"
	"io"
	"log"
	"os/exec"
	"runtime"
	"strings"
	"time"
)

const (
	windows = "windows"
)

// invokeEventScript will execute the given event script with the given
// event. Depending on the event, the semantics of how data are passed
// are a bit different. For all events, the SERF_EVENT environmental
// variable is the type of the event. For user events, the SERF_USER_EVENT
// environmental variable is also set, containing the name of the user
// event that was fired.
//
// In all events, data is passed in via stdin to faciliate piping. See
// the various stdin functions below for more information.
func invokeEventScript(logger *log.Logger, script string, self serf.Member, event serf.Event) error {
	defer metrics.MeasureSince([]string{"agent", "invoke", script}, time.Now())
	var output bytes.Buffer

	// Determine the shell invocation based on OS
	var shell, flag string
	if runtime.GOOS == windows {
		shell = "cmd"
		flag = "/C"
	} else {
		shell = "/bin/sh"
		flag = "-c"
	}

	cmd := exec.Command(shell, flag, script)
	cmd.Args[0] = "serf-event"
	cmd.Env = append(cmd.Env,
		"SERF_EVENT="+event.EventType().String(),
		"SERF_SELF_NAME="+self.Name,
		"SERF_SELF_ROLE="+self.Tags["role"],
	)
	cmd.Stderr = &output
	cmd.Stdout = &output

	// Add all the tags
	for name, val := range self.Tags {
		tag_env := fmt.Sprintf("SERF_TAG_%s=%s", strings.ToUpper(name), val)
		cmd.Env = append(cmd.Env, tag_env)
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}

	switch e := event.(type) {
	case serf.MemberEvent:
		go memberEventStdin(logger, stdin, &e)
	case serf.UserEvent:
		cmd.Env = append(cmd.Env, "SERF_USER_EVENT="+e.Name)
		cmd.Env = append(cmd.Env, fmt.Sprintf("SERF_USER_LTIME=%d", e.LTime))
		go userEventStdin(logger, stdin, &e)
	default:
		return fmt.Errorf("Unknown event type: %s", event.EventType().String())
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	err = cmd.Wait()
	logger.Printf("[DEBUG] Event '%s' script output: %s",
		event.EventType().String(), output.String())

	if err != nil {
		return err
	}

	return nil
}

// eventClean cleans a value to be a parameter in an event line.
func eventClean(v string) string {
	v = strings.Replace(v, "\t", "\\t", -1)
	v = strings.Replace(v, "\n", "\\n", -1)
	return v
}

// Sends data on stdin for a member event.
//
// The format for the data is unix tool friendly, separated by whitespace
// and newlines. The structure of each line for any member event is:
// "NAME    ADDRESS    ROLE    TAGS" where the whitespace is actually tabs.
// The name and role are cleaned so that newlines and tabs are replaced
// with "\n" and "\t" respectively.
func memberEventStdin(logger *log.Logger, stdin io.WriteCloser, e *serf.MemberEvent) {
	defer stdin.Close()
	for _, member := range e.Members {
		// Format the tags as tag1=v1,tag2=v2,...
		var tagPairs []string
		for name, value := range member.Tags {
			tagPairs = append(tagPairs, fmt.Sprintf("%s=%s", name, value))
		}
		tags := strings.Join(tagPairs, ",")

		// Send the entire line
		_, err := stdin.Write([]byte(fmt.Sprintf(
			"%s\t%s\t%s\t%s\n",
			eventClean(member.Name),
			member.Addr.String(),
			eventClean(member.Tags["role"]),
			eventClean(tags))))
		if err != nil {
			return
		}
	}
}

// Sends data on stdin for a user event. The stdin simply contains the
// payload (if any) of the event.
// Most shells read implementations need a newline, force it to be there
func userEventStdin(logger *log.Logger, stdin io.WriteCloser, e *serf.UserEvent) {
	defer stdin.Close()

	// Append a newline to payload if missing
	payload := e.Payload
	if len(payload) > 0 && payload[len(payload)-1] != '\n' {
		payload = append(payload, '\n')
	}

	if _, err := stdin.Write(payload); err != nil {
		logger.Printf("[ERR] Error writing user event payload: %s", err)
		return
	}
}
<commit_msg>Copy cmd.Env fron os.Environ() to inherit from the OS environment. Fixes #142.<commit_after>package agent

import (
	"bytes"
	"fmt"
	"github.com/armon/go-metrics"
	"github.com/hashicorp/serf/serf"
	"io"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"
)

const (
	windows = "windows"
)

// invokeEventScript will execute the given event script with the given
// event. Depending on the event, the semantics of how data are passed
// are a bit different. For all events, the SERF_EVENT environmental
// variable is the type of the event. For user events, the SERF_USER_EVENT
// environmental variable is also set, containing the name of the user
// event that was fired.
//
// In all events, data is passed in via stdin to faciliate piping. See
// the various stdin functions below for more information.
func invokeEventScript(logger *log.Logger, script string, self serf.Member, event serf.Event) error {
	defer metrics.MeasureSince([]string{"agent", "invoke", script}, time.Now())
	var output bytes.Buffer

	// Determine the shell invocation based on OS
	var shell, flag string
	if runtime.GOOS == windows {
		shell = "cmd"
		flag = "/C"
	} else {
		shell = "/bin/sh"
		flag = "-c"
	}

	cmd := exec.Command(shell, flag, script)
	cmd.Args[0] = "serf-event"
	cmd.Env = append(os.Environ(),
		"SERF_EVENT="+event.EventType().String(),
		"SERF_SELF_NAME="+self.Name,
		"SERF_SELF_ROLE="+self.Tags["role"],
	)
	cmd.Stderr = &output
	cmd.Stdout = &output

	// Add all the tags
	for name, val := range self.Tags {
		tag_env := fmt.Sprintf("SERF_TAG_%s=%s", strings.ToUpper(name), val)
		cmd.Env = append(cmd.Env, tag_env)
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}

	switch e := event.(type) {
	case serf.MemberEvent:
		go memberEventStdin(logger, stdin, &e)
	case serf.UserEvent:
		cmd.Env = append(cmd.Env, "SERF_USER_EVENT="+e.Name)
		cmd.Env = append(cmd.Env, fmt.Sprintf("SERF_USER_LTIME=%d", e.LTime))
		go userEventStdin(logger, stdin, &e)
	default:
		return fmt.Errorf("Unknown event type: %s", event.EventType().String())
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	err = cmd.Wait()
	logger.Printf("[DEBUG] Event '%s' script output: %s",
		event.EventType().String(), output.String())

	if err != nil {
		return err
	}

	return nil
}

// eventClean cleans a value to be a parameter in an event line.
func eventClean(v string) string {
	v = strings.Replace(v, "\t", "\\t", -1)
	v = strings.Replace(v, "\n", "\\n", -1)
	return v
}

// Sends data on stdin for a member event.
//
// The format for the data is unix tool friendly, separated by whitespace
// and newlines. The structure of each line for any member event is:
// "NAME    ADDRESS    ROLE    TAGS" where the whitespace is actually tabs.
// The name and role are cleaned so that newlines and tabs are replaced
// with "\n" and "\t" respectively.
func memberEventStdin(logger *log.Logger, stdin io.WriteCloser, e *serf.MemberEvent) {
	defer stdin.Close()
	for _, member := range e.Members {
		// Format the tags as tag1=v1,tag2=v2,...
		var tagPairs []string
		for name, value := range member.Tags {
			tagPairs = append(tagPairs, fmt.Sprintf("%s=%s", name, value))
		}
		tags := strings.Join(tagPairs, ",")

		// Send the entire line
		_, err := stdin.Write([]byte(fmt.Sprintf(
			"%s\t%s\t%s\t%s\n",
			eventClean(member.Name),
			member.Addr.String(),
			eventClean(member.Tags["role"]),
			eventClean(tags))))
		if err != nil {
			return
		}
	}
}

// Sends data on stdin for a user event. The stdin simply contains the
// payload (if any) of the event.
// Most shells read implementations need a newline, force it to be there
func userEventStdin(logger *log.Logger, stdin io.WriteCloser, e *serf.UserEvent) {
	defer stdin.Close()

	// Append a newline to payload if missing
	payload := e.Payload
	if len(payload) > 0 && payload[len(payload)-1] != '\n' {
		payload = append(payload, '\n')
	}

	if _, err := stdin.Write(payload); err != nil {
		logger.Printf("[ERR] Error writing user event payload: %s", err)
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/go-ozzo/ozzo-dbx"
	"github.com/go-ozzo/ozzo-routing"
	"github.com/go-ozzo/ozzo-routing/auth"
	"github.com/go-ozzo/ozzo-routing/content"
	"github.com/go-ozzo/ozzo-routing/cors"
	_ "github.com/lib/pq"
	"github.com/Zhanat87/go/apis"
	"github.com/Zhanat87/go/app"
	"github.com/Zhanat87/go/daos"
	"github.com/Zhanat87/go/errors"
	"github.com/Zhanat87/go/services"
	"os"
)

func main() {
	// load application configurations
	if err := app.LoadConfig("./config"); err != nil {
		panic(fmt.Errorf("Invalid application configuration: %s", err))
	}

	// load error messages
	if err := errors.LoadMessages(app.Config.ErrorFile); err != nil {
		panic(fmt.Errorf("Failed to read the error message file: %s", err))
	}

	// create the logger
	logger := logrus.New()

	// connect to the database
	db, err := dbx.MustOpen("postgres", app.Config.GetDSN())
	if err != nil {
		var variables string
		for _, e := range os.Environ() {
			variables += e + "\r\n"
		}
		panic(err + "\r\ndsn: " + app.Config.GetDSN() + "\r\n" + variables)
	}
	db.LogFunc = logger.Infof

	// wire up API routing
	http.Handle("/", buildRouter(logger, db))

	// start the server
	address := fmt.Sprintf(":%v", app.Config.ServerPort)
	logger.Infof("server %v is started at %v\n", app.Version, address)
	panic(http.ListenAndServe(address, nil))
}

func buildRouter(logger *logrus.Logger, db *dbx.DB) *routing.Router {
	router := routing.New()

	router.To("GET,HEAD", "/ping", func(c *routing.Context) error {
		c.Abort()  // skip all other middlewares/handlers
		return c.Write("OK " + app.Version)
	})

	router.To("GET,HEAD", "/test", func(c *routing.Context) error {
		c.Abort()  // skip all other middlewares/handlers
		return c.Write("test7")
	})

	router.Use(
		app.Init(logger),
		content.TypeNegotiator(content.JSON),
		cors.Handler(cors.Options{
			AllowOrigins: "*",
			AllowHeaders: "*",
			AllowMethods: "*",
		}),
		app.Transactional(db),
	)

	rg := router.Group("/v1")

	rg.Post("/auth", apis.Auth(app.Config.JWTSigningKey))
	rg.Use(auth.JWT(app.Config.JWTVerificationKey, auth.JWTOptions{
		SigningMethod: app.Config.JWTSigningMethod,
		TokenHandler:  apis.JWTHandler,
	}))

	artistDAO := daos.NewArtistDAO()
	apis.ServeArtistResource(rg, services.NewArtistService(artistDAO))

	albumDAO := daos.NewAlbumDAO()
	apis.ServeAlbumResource(rg, services.NewAlbumService(albumDAO))

	// wire up more resource APIs here

	return router
}
<commit_msg>updates<commit_after>package main

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	//"github.com/go-ozzo/ozzo-dbx"
	"github.com/go-ozzo/ozzo-routing"
	//"github.com/go-ozzo/ozzo-routing/auth"
	//"github.com/go-ozzo/ozzo-routing/content"
	//"github.com/go-ozzo/ozzo-routing/cors"
	//_ "github.com/lib/pq"
	//"github.com/Zhanat87/go/apis"
	"github.com/Zhanat87/go/app"
	//"github.com/Zhanat87/go/daos"
	"github.com/Zhanat87/go/errors"
	//"github.com/Zhanat87/go/services"
	"os"
)

func main() {
	// load application configurations
	if err := app.LoadConfig("./config"); err != nil {
		panic(fmt.Errorf("Invalid application configuration: %s", err))
	}

	// load error messages
	if err := errors.LoadMessages(app.Config.ErrorFile); err != nil {
		panic(fmt.Errorf("Failed to read the error message file: %s", err))
	}

	// create the logger
	logger := logrus.New()

	// connect to the database
	//db, err := dbx.MustOpen("postgres", app.Config.GetDSN())
	//if err != nil {
	//	panic(err)
	//}
	//db.LogFunc = logger.Infof
	//
	//// wire up API routing
	//http.Handle("/", buildRouter(logger, db))
	http.Handle("/", buildRouter(logger))

	// start the server
	address := fmt.Sprintf(":%v", app.Config.ServerPort)
	logger.Infof("server %v is started at %v\n", app.Version, address)
	panic(http.ListenAndServe(address, nil))
}

func buildRouter(logger *logrus.Logger) *routing.Router {
//func buildRouter(logger *logrus.Logger, db *dbx.DB) *routing.Router {
	router := routing.New()

	router.To("GET,HEAD", "/ping", func(c *routing.Context) error {
		c.Abort()  // skip all other middlewares/handlers
		return c.Write("OK " + app.Version)
	})

	router.To("GET,HEAD", "/test", func(c *routing.Context) error {
		c.Abort()  // skip all other middlewares/handlers
		var variables string
		for _, e := range os.Environ() {
			variables += e + "\r\n"
		}
		return c.Write(variables)
	})

	//router.Use(
	//	app.Init(logger),
	//	content.TypeNegotiator(content.JSON),
	//	cors.Handler(cors.Options{
	//		AllowOrigins: "*",
	//		AllowHeaders: "*",
	//		AllowMethods: "*",
	//	}),
	//	app.Transactional(db),
	//)
	//
	//rg := router.Group("/v1")
	//
	//rg.Post("/auth", apis.Auth(app.Config.JWTSigningKey))
	//rg.Use(auth.JWT(app.Config.JWTVerificationKey, auth.JWTOptions{
	//	SigningMethod: app.Config.JWTSigningMethod,
	//	TokenHandler:  apis.JWTHandler,
	//}))
	//
	//artistDAO := daos.NewArtistDAO()
	//apis.ServeArtistResource(rg, services.NewArtistService(artistDAO))
	//
	//albumDAO := daos.NewAlbumDAO()
	//apis.ServeAlbumResource(rg, services.NewAlbumService(albumDAO))

	// wire up more resource APIs here

	return router
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/martini"

	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"
)

type ChanRequest struct {
	name     string
	response chan chan int
}

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

	var requestChan = make(chan ChanRequest)

	go func(req chan ChanRequest) {
		var channels = make(map[string]chan int, 10)

		for {
			r := <-req

			queue, ok := channels[r.name]
			if ok {
				r.response <- queue
			} else {
				fmt.Println("adding new channel")
				newchan := make(chan int, 10)
				channels[r.name] = newchan
				r.response <- newchan
			}
		}
	}(requestChan)

	m.Get("/queue/:name", func(res http.ResponseWriter, params martini.Params) {
		response := make(chan chan int)
		request := ChanRequest{name: params["name"], response: response}
		requestChan <- request
		queue := <-response

		// read from the queue with a 30 second timeout
		select {
		case val := <-queue:
			fmt.Fprintln(res, val)
		case <-time.After(time.Second * 30):
			res.WriteHeader(408)
			fmt.Fprintln(res, "TIMEOUT")
		}

	})
	m.Post("/queue/:name", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
		response := make(chan chan int)
		request := ChanRequest{name: params["name"], response: response}
		requestChan <- request
		queue := <-response

		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			fmt.Fprintln(res, err)
		}

		intval, err := strconv.Atoi(string(body))
		if err != nil {
			fmt.Fprintln(res, err)
		}

		// send to the queue unless it's full
		select {
		case queue <- intval:
		default:
			res.WriteHeader(406)
			fmt.Fprintln(res, "QUEUE FULL")
		}
	})
	m.Run()
}
<commit_msg>int -> string in queue, clean up http errors<commit_after>package main

import (
	"github.com/codegangsta/martini"

	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

type Queue chan string

type ChanRequest struct {
	name     string
	response chan Queue
}

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

	var requestChan = make(chan ChanRequest)

	go func(req chan ChanRequest) {
		var channels = make(map[string]Queue, 10)

		for {
			r := <-req

			queue, ok := channels[r.name]
			if ok {
				r.response <- queue
			} else {
				newchan := make(Queue, 10)
				channels[r.name] = newchan
				r.response <- newchan
			}
		}
	}(requestChan)

	m.Get("/queue/:name", func(res http.ResponseWriter, params martini.Params) {
		request := ChanRequest{name: params["name"], response: make(chan Queue)}
		requestChan <- request
		queue := <-request.response
		close(request.response)

		// read from the queue with a 30 second timeout
		select {
		case val := <-queue:
			fmt.Fprintln(res, val)
		case <-time.After(time.Second * 30):
			http.Error(res, "Timeout", http.StatusRequestTimeout)
		}

	})
	m.Post("/queue/:name", func(res http.ResponseWriter, req *http.Request, params martini.Params) {
		request := ChanRequest{name: params["name"], response: make(chan Queue)}
		requestChan <- request
		queue := <-request.response
		close(request.response)

		body, err := ioutil.ReadAll(req.Body)
		if err != nil {
			http.Error(res, "Unable to read request body: "+err.Error(), http.StatusInternalServerError)
			return
		}

		// send to the queue unless it's full
		select {
		case queue <- string(body):
		default:
			http.Error(res, "Queue Full", http.StatusNotAcceptable)
		}
	})
	m.Run()
}
<|endoftext|>


<commit_before>package gopher

import (
  "bufio"
  "net"
  "net/url"
)

type Server struct {
  Address string
  handler func(net.Conn, *Request)
}

func NewServer(address string) *Server {
  return &Server{
    Address: address,
  }
}

func (server *Server) URL() *url.URL {
  return &url.URL{
    Host: server.Address,
    Scheme: "gopher",
  }
}

func (server *Server) ListenAndServe(handler func(net.Conn, *Request)) (error) {
  listener, err := net.Listen("tcp", server.Address)
  if err != nil {
    return err
  }

  server.handler = handler

  return server.Serve(listener)
}

func (server *Server) Serve(listener net.Listener) (err error) {
  for {
    err = server.Accept(listener)
    if err != nil {
      return err
    }
  }
}

func (server *Server) Accept(listener net.Listener) (error) {
  connection, err := listener.Accept()
  if err == nil {
    go server.HandleConnection(connection)
  }

  return err
}

func (server *Server) HandleConnection(connection net.Conn) {
  request, err := server.ConnectRequest(connection)
  if err == nil {
    defer connection.Close()
    server.HandleRequest(connection, request)
  }
}

func (server *Server) ConnectRequest(connection net.Conn) (*Request, error) {
  return ReadRequest(bufio.NewReader(connection))
}

func (server *Server) HandleRequest(connection net.Conn, request *Request) {
  server.handler(connection, request)
}
<commit_msg>Add logical control break for server<commit_after>package gopher

import (
  "bufio"
  "net"
  "net/url"
)

type Server struct {
  Address string
  handler func(net.Conn, *Request)
}

func NewServer(address string) *Server {
  return &Server{
    Address: address,
  }
}

func (server *Server) URL() *url.URL {
  return &url.URL{
    Host: server.Address,
    Scheme: "gopher",
  }
}

func (server *Server) ListenAndServe(handler func(net.Conn, *Request)) (error) {
  listener, err := net.Listen("tcp", server.Address)
  if err != nil {
    return err
  }

  server.handler = handler

  return server.Serve(listener)
}

func (server *Server) Serve(listener net.Listener) (err error) {
  for {
    err = server.Accept(listener)
    if err != nil {
      break
    }
  }
  return err
}

func (server *Server) Accept(listener net.Listener) (error) {
  connection, err := listener.Accept()
  if err == nil {
    go server.HandleConnection(connection)
  }

  return err
}

func (server *Server) HandleConnection(connection net.Conn) {
  request, err := server.ConnectRequest(connection)
  if err == nil {
    defer connection.Close()
    server.HandleRequest(connection, request)
  }
}

func (server *Server) ConnectRequest(connection net.Conn) (*Request, error) {
  return ReadRequest(bufio.NewReader(connection))
}

func (server *Server) HandleRequest(connection net.Conn, request *Request) {
  server.handler(connection, request)
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/gocraft/web"
	_ "github.com/lib/pq"
	"net/http"
	"os"
	"time"
)

var (
	insertNotification *sql.Stmt
	db                 *sql.DB
)

type Context struct {
}

type Notification struct {
	SubscriptionId string `json:"subscription_id"`
	ObjectId       string `json:"object_id"`
	Object         string `json:"object"`
	ChangedAspect  string `json:"changed_aspect"`
	TimeChanged    int64  `json:"time"`
}

func (c *Context) VerifyInstagram(rw web.ResponseWriter, req *web.Request) {
	req.ParseForm()
	vals, ok := req.Form["hub.challenge"]
	if !ok || len(vals) == 0 {
		fmt.Fprint(rw, "Challenge not found: ", vals)
		return
	}
	challenge := vals[0]
	fmt.Fprint(rw, challenge)
}

func (c *Context) Ping(rw web.ResponseWriter, req *web.Request) {
	err := db.Ping()
	checkErr(err)
	fmt.Fprint(rw, "pong")
}

func process(n Notification) {
	err := insertNotification.QueryRow(n.SubscriptionId, n.ObjectId, n.ObjectId, n.ChangedAspect, time.Unix(n.TimeChanged, 0)).Scan(&sql.NullInt64{})
	if err != nil {
		fmt.Printf("Failed on insert: %s\n", err)
	}
}

func (c *Context) ReceiveNotifications(rw web.ResponseWriter, req *web.Request) {
	notifications := make([]Notification, 0)
	decoder := json.NewDecoder(req.Body)
	decoder.Decode(&notifications)
	for _, n := range notifications {
		go process(n)
	}
}

func getEnvOrDefault(key, def string) string {
	env := os.Getenv(key)
	if len(env) == 0 {
		env = def
	}
	return env
}

func initDb() {
	dbhost := getEnvOrDefault("SPOTO_DB_HOST", "localhost")
	cs := fmt.Sprintf("user=spoto password=%s dbname=spoto sslmode=disable host=%s", "otops", dbhost)
	var err error
	db, err = sql.Open("postgres", cs)
	checkErr(err)
	insertNotification, err = db.Prepare("INSERT INTO \"notifications\" (subscription_id, iid, object, changed_aspect, changed_time) VALUES((SELECT id from subscriptions where subscription_id=$1),$2,$3,$4,$5) returning id;")
	checkErr(err)
}

func main() {
	initDb()
	defer db.Close()
	router := web.New(Context{}).
		Middleware(web.LoggerMiddleware).
		Middleware(web.ShowErrorsMiddleware).
		Get("/insta", (*Context).VerifyInstagram).
		Get("/ping", (*Context).Ping).
		Post("/insta", (*Context).ReceiveNotifications)
	host := getEnvOrDefault("SPOTO_HOST", "localhost")
	port := getEnvOrDefault("SPOTO_PORT", "3000")
	bindTo := fmt.Sprintf("%s:%s", host, port)
	http.ListenAndServe(bindTo, router)
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>Add basic tests<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/gocraft/web"
	_ "github.com/lib/pq"
	"net/http"
	"os"
	"time"
)

type Pinger interface {
	Ping() error
}

var (
	insertNotification *sql.Stmt
	pinger             Pinger
)

type Context struct {
}

type Notification struct {
	SubscriptionId string `json:"subscription_id"`
	ObjectId       string `json:"object_id"`
	Object         string `json:"object"`
	ChangedAspect  string `json:"changed_aspect"`
	TimeChanged    int64  `json:"time"`
}

func (c *Context) VerifyInstagram(rw web.ResponseWriter, req *web.Request) {
	req.ParseForm()
	vals, ok := req.Form["hub.challenge"]
	if !ok || len(vals) == 0 {
		fmt.Fprint(rw, "Challenge not found: ", vals)
		return
	}
	challenge := vals[0]
	fmt.Fprint(rw, challenge)
}

func (c *Context) Ping(rw web.ResponseWriter, req *web.Request) {
	err := pinger.Ping()
	if err != nil {
		http.Error(rw, "Ping failed", 500)
	}
	fmt.Fprint(rw, "pong")
}

func process(n Notification) {
	err := insertNotification.QueryRow(n.SubscriptionId, n.ObjectId, n.ObjectId, n.ChangedAspect, time.Unix(n.TimeChanged, 0)).Scan(&sql.NullInt64{})
	if err != nil {
		fmt.Printf("Failed on insert: %s\n", err)
	}
}

func (c *Context) ReceiveNotifications(rw web.ResponseWriter, req *web.Request) {
	notifications := make([]Notification, 0)
	decoder := json.NewDecoder(req.Body)
	decoder.Decode(&notifications)
	for _, n := range notifications {
		go process(n)
	}
}

func getEnvOrDefault(key, def string) string {
	env := os.Getenv(key)
	if len(env) == 0 {
		env = def
	}
	return env
}

func initDb() *sql.DB {
	dbhost := getEnvOrDefault("SPOTO_DB_HOST", "localhost")
	cs := fmt.Sprintf("user=spoto password=%s dbname=spoto sslmode=disable host=%s", "otops", dbhost)
	var err error
	db, err := sql.Open("postgres", cs)
	checkErr(err)
	insertNotification, err = db.Prepare("INSERT INTO \"notifications\" (subscription_id, iid, object, changed_aspect, changed_time) VALUES((SELECT id from subscriptions where subscription_id=$1),$2,$3,$4,$5) returning id;")
	checkErr(err)
	pinger = db
	return db
}

func initRouter(logger, fatal bool) http.Handler {
	ctx := web.New(Context{})
	if logger {
		ctx = ctx.Middleware(web.LoggerMiddleware)
	}
	if fatal {
		ctx = ctx.Middleware(web.ShowErrorsMiddleware)
	}
	return ctx.Get("/insta", (*Context).VerifyInstagram).
		Get("/ping", (*Context).Ping).
		Post("/insta", (*Context).ReceiveNotifications)
}

func main() {
	db := initDb()
	defer db.Close()

	host := getEnvOrDefault("SPOTO_HOST", "localhost")
	port := getEnvOrDefault("SPOTO_PORT", "3000")
	bindTo := fmt.Sprintf("%s:%s", host, port)

	router := initRouter(true, true)
	http.ListenAndServe(bindTo, router)
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package revealgo

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"
)

type Server struct {
	port int
}

type ServerParam struct {
	Path          string
	Theme         string
	OriginalTheme bool
	Transition    string
}

func (server *Server) Serve(param ServerParam) {
	port := 3000
	if server.port > 0 {
		port = server.port
	}
	fmt.Printf("accepting connections at http://*:%d/\n", port)
	http.Handle("/", &rootHandler{param: param})
	http.Handle("/revealjs/", &assetHandler{assetPath: "assets"})
	if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

type assetHandler struct {
	assetPath string
}

func (h *assetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	filepath := h.assetPath + r.URL.Path
	data, err := Asset(filepath)
	if err != nil {
		http.NotFound(w, r)
		return
	}
	w = setResponse(w, filepath, data)
}

type rootHandler struct {
	param ServerParam
}

func (h *rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	urlPath := r.URL.Path
	path, err := filepath.Rel("./", "."+urlPath)
	if err != nil {
		log.Fatalf("error:%v", err)
	}
	_, err = os.Stat(path)
	if err == nil {
		data, err := ioutil.ReadFile(path)
		if err == nil {
			w = setResponse(w, path, data)
			return
		}
	}

	data, err := Asset("assets/templates/slide.html")
	if err != nil {
		log.Printf("error:%v", err)
		http.NotFound(w, r)
		return
	}
	tmpl := template.New("slide template")
	tmpl.Parse(string(data))
	if err != nil {
		log.Printf("error:%v", err)
		http.NotFound(w, r)
		return
	}
	err = tmpl.Execute(w, h.param)
	if err != nil {
		log.Fatalf("error:%v", err)
	}
}

func detectContentType(path string, data []byte) string {
	if strings.HasSuffix(path, ".css") {
		return "text/css"
	} else if strings.HasSuffix(path, ".js") {
		return "application/javascript"
	}
	return http.DetectContentType(data)
}

func setResponse(w http.ResponseWriter, path string, data []byte) http.ResponseWriter {
	mimeType := detectContentType(path, data)
	w.Header().Set("Content-Type", mimeType)
	w.Header().Set("Content-Length", strconv.Itoa(len(data)))
	if _, err := w.Write(data); err != nil {
		log.Fatal("unable to write data.")
	}
	return w
}
<commit_msg>add svg suport<commit_after>package revealgo

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"
)

type Server struct {
	port int
}

type ServerParam struct {
	Path          string
	Theme         string
	OriginalTheme bool
	Transition    string
}

func (server *Server) Serve(param ServerParam) {
	port := 3000
	if server.port > 0 {
		port = server.port
	}
	fmt.Printf("accepting connections at http://*:%d/\n", port)
	http.Handle("/", &rootHandler{param: param})
	http.Handle("/revealjs/", &assetHandler{assetPath: "assets"})
	if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

type assetHandler struct {
	assetPath string
}

func (h *assetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	filepath := h.assetPath + r.URL.Path
	data, err := Asset(filepath)
	if err != nil {
		http.NotFound(w, r)
		return
	}
	w = setResponse(w, filepath, data)
}

type rootHandler struct {
	param ServerParam
}

func (h *rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	urlPath := r.URL.Path
	path, err := filepath.Rel("./", "."+urlPath)
	if err != nil {
		log.Fatalf("error:%v", err)
	}
	_, err = os.Stat(path)
	if err == nil {
		data, err := ioutil.ReadFile(path)
		if err == nil {
			w = setResponse(w, path, data)
			return
		}
	}

	data, err := Asset("assets/templates/slide.html")
	if err != nil {
		log.Printf("error:%v", err)
		http.NotFound(w, r)
		return
	}
	tmpl := template.New("slide template")
	tmpl.Parse(string(data))
	if err != nil {
		log.Printf("error:%v", err)
		http.NotFound(w, r)
		return
	}
	err = tmpl.Execute(w, h.param)
	if err != nil {
		log.Fatalf("error:%v", err)
	}
}

func detectContentType(path string, data []byte) string {
	switch {
	case strings.HasSuffix(path, ".css"):
		return "text/css"
	case strings.HasSuffix(path, ".js"):
		return "application/javascript"
	case strings.HasSuffix(path, ".svg"):
		return "image/svg+xml"
	}
	return http.DetectContentType(data)
}

func setResponse(w http.ResponseWriter, path string, data []byte) http.ResponseWriter {
	mimeType := detectContentType(path, data)
	w.Header().Set("Content-Type", mimeType)
	w.Header().Set("Content-Length", strconv.Itoa(len(data)))
	if _, err := w.Write(data); err != nil {
		log.Fatal("unable to write data.")
	}
	return w
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/jessemillar/stalks/accessors"
	"github.com/jessemillar/stalks/controllers"
	"github.com/jessemillar/stalks/helpers"
	"github.com/robfig/cron"
	"github.com/zenazn/goji"
)

func main() {
	// Construct the dsn used for the database
	dsn := os.Getenv("STALKS_DB_USER") + ":" + os.Getenv("STALKS_DB_PASS") + "@tcp(" + os.Getenv("STALKS_DB_HOST") + ":" + os.Getenv("STALKS_DB_PORT") + ")/" + os.Getenv("STALKS_DB_NAME")

	// Construct a new AccessorGroup and connects it to the database
	ag := new(accessors.AccessorGroup)
	ag.ConnectToDB("mysql", dsn)

	// Constructs a new ControllerGroup and gives it the AccessorGroup
	cg := new(controllers.ControllerGroup)
	cg.Accessors = ag

	c := cron.New()
	c.AddFunc("0 0 14 * * 1-5", func() { // Run at 2:00pm MST Monday through Friday
		helpers.Webhook(helpers.ReportLeaders(ag))
	})
	c.Start()

	goji.Get("/health", cg.Health)
	goji.Get("/leaderboard", cg.ReportLeaders)
	goji.Post("/slack", cg.Slack) // The main endpoint that Slack hits
	goji.Post("/play", cg.User)
	goji.Post("/portfolio", cg.Portfolio)
	goji.Get("/check/:symbol", cg.Check)
	goji.Post("/buy/:quantity/:symbol", cg.Buy)
	goji.Post("/sell/:quantity/:symbol", cg.Sell)
	goji.Serve()
}
<commit_msg>NOW it should work because UTC inside Docker and reasons<commit_after>package main

import (
	"os"

	"github.com/jessemillar/stalks/accessors"
	"github.com/jessemillar/stalks/controllers"
	"github.com/jessemillar/stalks/helpers"
	"github.com/robfig/cron"
	"github.com/zenazn/goji"
)

func main() {
	// Construct the dsn used for the database
	dsn := os.Getenv("STALKS_DB_USER") + ":" + os.Getenv("STALKS_DB_PASS") + "@tcp(" + os.Getenv("STALKS_DB_HOST") + ":" + os.Getenv("STALKS_DB_PORT") + ")/" + os.Getenv("STALKS_DB_NAME")

	// Construct a new AccessorGroup and connects it to the database
	ag := new(accessors.AccessorGroup)
	ag.ConnectToDB("mysql", dsn)

	// Constructs a new ControllerGroup and gives it the AccessorGroup
	cg := new(controllers.ControllerGroup)
	cg.Accessors = ag

	c := cron.New()
	c.AddFunc("0 0 21 * * 1-5", func() { // Run at 2:00pm MST (which is 21:00 UTC) Monday through Friday
		helpers.Webhook(helpers.ReportLeaders(ag))
	})
	c.Start()

	goji.Get("/health", cg.Health)
	goji.Get("/leaderboard", cg.ReportLeaders)
	goji.Post("/slack", cg.Slack) // The main endpoint that Slack hits
	goji.Post("/play", cg.User)
	goji.Post("/portfolio", cg.Portfolio)
	goji.Get("/check/:symbol", cg.Check)
	goji.Post("/buy/:quantity/:symbol", cg.Buy)
	goji.Post("/sell/:quantity/:symbol", cg.Sell)
	goji.Serve()
}
<|endoftext|>


<commit_before>package sftpd

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"

	"github.com/taruti/binp"
	"github.com/taruti/bytepool"
	"golang.org/x/crypto/ssh"
)

type Config struct {
	ssh.ServerConfig
}

var sftpSubSystem = []byte{0, 0, 0, 4, 115, 102, 116, 112}

// IsSftpRequest checks whether a given ssh.Request is for sftp.
func IsSftpRequest(req *ssh.Request) bool {
	return req.Type == "subsystem" && bytes.Equal(sftpSubSystem, req.Payload)
}

var initReply = []byte{0, 0, 0, 5, ssh_FXP_VERSION, 0, 0, 0, 3}

// ServeChannel serves a ssh.Channel with the given FileSystem.
func ServeChannel(c ssh.Channel, fs FileSystem) error {
	defer c.Close()
	var h handles
	h.init()
	defer h.closeAll()
	brd := bufio.NewReader(c)
	var e error
	var plen int
	var op byte
	var bs []byte
	var id uint32
	for {
		if e != nil {
			debug("Sending errror", e)
			e = writeErr(c, id, e)
			if e != nil {
				return e
			}
		}
		discard(brd, plen)
		plen, op, e = readPacketHeader(brd)
		if e != nil {
			return e
		}
		plen--
		debug("CR op=%d data len=%d\n", op, plen)
		if plen < 2 {
			return errors.New("Packet too short")
		}
		// Feeding too large values to peek is ok, it just errors.
		bs, e = brd.Peek(plen)
		if e != nil {
			return e
		}
		debugf("Data %X\n", bs)
		p := binp.NewParser(bs)
		switch op {
		case ssh_FXP_INIT:
			e = wrc(c, initReply)
		case ssh_FXP_OPEN:
			var path string
			var flags uint32
			var a Attr
			e = parseAttr(p.B32(&id).B32String(&path).B32(&flags), &a).End()
			if e != nil {
				return e
			}
			if h.nfiles() >= maxFiles {
				e = etoomany
				continue
			}
			var f File
			f, e = fs.OpenFile(path, flags, &a)
			if e != nil {
				continue
			}
			e = writeHandle(c, id, h.newFile(f))
		case ssh_FXP_CLOSE:
			var handle string
			e = p.B32(&id).B32String(&handle).End()
			if e != nil {
				return e
			}
			h.closeHandle(handle)
			e = writeErr(c, id, nil)
		case ssh_FXP_READ:
			var handle string
			var offset uint64
			var length uint32
			var n int
			e = p.B32(&id).B32String(&handle).B64(&offset).B32(&length).End()
			debug("READ0", id, handle, offset, length, e)
			if e != nil {
				return e
			}
			f := h.getFile(handle)
			debug("READ1", f)
			if f == nil {
				return einvhandle
			}
			if length > 64*1024 {
				length = 64 * 1024
			}
			bs := bytepool.Alloc(int(length))
			n, e = f.ReadAt(bs, int64(offset))
			debug("READ2", n, e)
			if e != nil {
				bytepool.Free(bs)
				continue
			}
			bs = bs[0:n]
			e = wrc(c, binp.Out().B32(1+4+4+uint32(len(bs))).Byte(ssh_FXP_DATA).B32(id).B32(uint32(len(bs))).Out())
			debug("READ3", len(bs), e)
			if e == nil {
				e = wrc(c, bs)
			}
			debug("READ4", e)
			bytepool.Free(bs)
		case ssh_FXP_WRITE:
			var handle string
			var offset uint64
			var length uint32
			e = p.B32(&id).B32String(&handle).B64(&offset).B32(&length).End()
			if e != nil {
				return e
			}
			f := h.getFile(handle)
			if f == nil {
				return einvhandle
			}
			var bs []byte
			p.NBytesPeek(int(length), &bs)
			_, e = f.WriteAt(bs, int64(offset))
			e = writeErr(c, id, e)
		case ssh_FXP_LSTAT, ssh_FXP_STAT:
			var path string
			var a *Attr
			e = p.B32(&id).B32String(&path).End()
			if e != nil {
				return e
			}
			a, e = fs.Stat(path, op == ssh_FXP_LSTAT)
			e = writeAttr(c, id, a, e)
		case ssh_FXP_FSTAT:
			var handle string
			var a *Attr
			e = p.B32(&id).B32String(&handle).End()
			if e != nil {
				return e
			}
			f := h.getFile(handle)
			if f == nil {
				return einvhandle
			}
			a, e = f.FStat()
			e = writeAttr(c, id, a, e)
		case ssh_FXP_SETSTAT:
			var path string
			var a Attr
			e = parseAttr(p.B32(&id).B32String(&path), &a).End()
			if e != nil {
				return e
			}
			e = writeErr(c, id, fs.SetStat(path, &a))
		case ssh_FXP_FSETSTAT:
			var handle string
			var a Attr
			e = parseAttr(p.B32(&id).B32String(&handle), &a).End()
			if e != nil {
				return e
			}
			f := h.getFile(handle)
			if f == nil {
				return einvhandle
			}
			e = writeErr(c, id, f.FSetStat(&a))
		case ssh_FXP_OPENDIR:
			var path string
			var dh Dir
			e = p.B32(&id).B32String(&path).End()
			if e != nil {
				return e
			}
			debug("opendir", id, path)
			dh, e = fs.OpenDir(path)
			debug("opendir =>", dh, e)
			if e != nil {
				continue
			}
			e = writeHandle(c, id, h.newDir(dh))

		case ssh_FXP_READDIR:
			var handle string
			e = p.B32(&id).B32String(&handle).End()
			if e != nil {
				return e
			}
			debug("readdir", id, handle)
			f := h.getDir(handle)
			if f == nil {
				return einvhandle
			}
			var fis []NamedAttr
			fis, e = f.Readdir(1024)
			if e != nil {
				continue
			}
			var l binp.Len
			o := binp.Out().LenB32(&l).LenStart(&l).Byte(ssh_FXP_NAME).B32(id).B32(uint32(len(fis)))
			for _, fi := range fis {
				// FIXME
				n := fi.Name
				const flag = ssh_FILEXFER_ATTR_SIZE
				o.B32String(n).B32String(n).B32(flag).B64(uint64(fi.Size))
			}
			o.LenDone(&l)
			e = wrc(c, o.Out())

		case ssh_FXP_REMOVE:
			var path string
			e = p.B32(&id).B32String(&path).End()
			if e != nil {
				return e
			}
			e = writeErr(c, id, fs.Remove(path))
		case ssh_FXP_MKDIR:
			var path string
			var a Attr
			p = p.B32(&id).B32String(&path)
			e = parseAttr(p, &a).End()
			if e != nil {
				return e
			}
			e = writeErr(c, id, fs.Mkdir(path, &a))
		case ssh_FXP_RMDIR:
			var path string
			e = p.B32(&id).B32String(&path).End()
			if e != nil {
				return e
			}
			e = writeErr(c, id, fs.Rmdir(path))
		case ssh_FXP_REALPATH:
			var path string
			p.B32(&id).B32String(&path).End()
			path, e = fs.RealPath(path)
			e = writeNameOnly(c, id, path, e)
		case ssh_FXP_RENAME:
			e = writeFail(c, id) // FIXME
		case ssh_FXP_READLINK:
			var path string
			e = p.B32(&id).B32String(&path).End()
			path, e = fs.ReadLink(path)
			e = writeNameOnly(c, id, path, e)
		case ssh_FXP_SYMLINK:
			e = writeFail(c, id) // FIXME
		}
		if e != nil {
			return e
		}
	}
}

var einvhandle = errors.New("Client supplied an invalid handle")
var etoomany = errors.New("Too many files")

const maxFiles = 0x100

func readPacketHeader(rd *bufio.Reader) (int, byte, error) {
	bs := make([]byte, 5)
	_, e := io.ReadFull(rd, bs)
	if e != nil {
		return 0, 0, e
	}
	return int(binary.BigEndian.Uint32(bs)), bs[4], nil
}

func parseAttr(p *binp.Parser, a *Attr) *binp.Parser {
	p = p.B32(&a.Flags)
	if a.Flags&ssh_FILEXFER_ATTR_SIZE != 0 {
		p = p.B64(&a.Size)
	}
	if a.Flags&ssh_FILEXFER_ATTR_UIDGID != 0 {
		p = p.B32(&a.Uid).B32(&a.Gid)
	}
	if a.Flags&ssh_FILEXFER_ATTR_PERMISSIONS != 0 {
		p = p.B32(&a.Mode)
	}
	if a.Flags&ssh_FILEXFER_ATTR_ACMODTIME != 0 {
		p = p.B32(&a.ATime).B32(&a.MTime)
	}
	if a.Flags&ssh_FILEXFER_ATTR_EXTENDED != 0 {
		var count uint32
		p = p.B32(&count)
		if count > 0xFF {
			return nil
		}
		ss := make([]string, 2*int(count))
		for i := 0; i < int(count); i++ {
			var k, v string
			p = p.B32String(&k).B32String(&v)
			ss[2*i+0] = k
			ss[2*i+1] = v
		}
		a.Extended = ss
	}
	return p
}

func writeAttr(c ssh.Channel, id uint32, a *Attr, e error) error {
	if e != nil {
		return writeErr(c, id, e)
	}
	var l binp.Len
	o := binp.Out().LenB32(&l).LenStart(&l).Byte(ssh_FXP_ATTRS).B32(id).B32(a.Flags)
	if a.Flags&ssh_FILEXFER_ATTR_SIZE != 0 {
		o = o.B64(a.Size)
	}
	if a.Flags&ssh_FILEXFER_ATTR_UIDGID != 0 {
		o = o.B32(a.Uid).B32(a.Gid)
	}
	if a.Flags&ssh_FILEXFER_ATTR_PERMISSIONS != 0 {
		o = o.B32(a.Mode)
	}
	if a.Flags&ssh_FILEXFER_ATTR_ACMODTIME != 0 {
		o = o.B32(a.ATime).B32(a.MTime)
	}
	if a.Flags&ssh_FILEXFER_ATTR_EXTENDED != 0 {
		count := uint32(len(a.Extended) / 2)
		o = o.B32(count)
		for _, s := range a.Extended {
			o = o.B32String(s)
		}
	}
	o.LenDone(&l)
	return wrc(c, o.Out())
}

func writeNameOnly(c ssh.Channel, id uint32, path string, e error) error {
	if e != nil {
		return writeErr(c, id, e)
	}
	var l binp.Len
	o := binp.Out().LenB32(&l).LenStart(&l).Byte(ssh_FXP_NAME).B32(id).B32(1)
	o.B32String(path).B32String(path).B32(0)
	o.LenDone(&l)
	return wrc(c, o.Out())
}

var failTmpl = []byte{0, 0, 0, 1 + 4 + 4 + 4 + 4, ssh_FXP_STATUS, 0, 0, 0, 0, 0, 0, 0, ssh_FX_FAILURE, 0, 0, 0, 0, 0, 0, 0, 0}

func writeFail(c ssh.Channel, id uint32) error {
	bs := make([]byte, len(failTmpl))
	copy(bs, failTmpl)
	binary.BigEndian.PutUint32(bs[5:], id)
	return wrc(c, bs)
}

func writeErr(c ssh.Channel, id uint32, err error) error {
	bs := make([]byte, len(failTmpl))
	copy(bs, failTmpl)
	binary.BigEndian.PutUint32(bs[5:], id)
	var code byte = ssh_FX_FAILURE
	switch err {
	case nil:
		code = ssh_FX_OK
	case io.EOF:
		code = ssh_FX_EOF
	}
	bs[12] = code
	return wrc(c, bs)
}

func writeHandle(c ssh.Channel, id uint32, handle string) error {
	return wrc(c, binp.OutCap(4+9+len(handle)).B32(uint32(9+len(handle))).B8(ssh_FXP_HANDLE).B32(id).B32String(handle).Out())
}

func wrc(c ssh.Channel, bs []byte) error {
	_, e := c.Write(bs)
	return e
}

func discard(brd *bufio.Reader, n int) error {
	if n == 0 {
		return nil
	}
	m, e := io.Copy(ioutil.Discard, &io.LimitedReader{R: brd, N: int64(n)})
	if int(m) == n && e == io.EOF {
		e = nil
	}
	return e
}
<commit_msg>Handle go readers that return io.EOF and bytes at the same time<commit_after>package sftpd

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"io/ioutil"

	"github.com/taruti/binp"
	"github.com/taruti/bytepool"
	"golang.org/x/crypto/ssh"
)

type Config struct {
	ssh.ServerConfig
}

var sftpSubSystem = []byte{0, 0, 0, 4, 115, 102, 116, 112}

// IsSftpRequest checks whether a given ssh.Request is for sftp.
func IsSftpRequest(req *ssh.Request) bool {
	return req.Type == "subsystem" && bytes.Equal(sftpSubSystem, req.Payload)
}

var initReply = []byte{0, 0, 0, 5, ssh_FXP_VERSION, 0, 0, 0, 3}

// ServeChannel serves a ssh.Channel with the given FileSystem.
func ServeChannel(c ssh.Channel, fs FileSystem) error {
	defer c.Close()
	var h handles
	h.init()
	defer h.closeAll()
	brd := bufio.NewReader(c)
	var e error
	var plen int
	var op byte
	var bs []byte
	var id uint32
	for {
		if e != nil {
			debug("Sending errror", e)
			e = writeErr(c, id, e)
			if e != nil {
				return e
			}
		}
		discard(brd, plen)
		plen, op, e = readPacketHeader(brd)
		if e != nil {
			return e
		}
		plen--
		debugf("CR op=%d data len=%d\n", op, plen)
		if plen < 2 {
			return errors.New("Packet too short")
		}
		// Feeding too large values to peek is ok, it just errors.
		bs, e = brd.Peek(plen)
		if e != nil {
			return e
		}
		debugf("Data %X\n", bs)
		p := binp.NewParser(bs)
		switch op {
		case ssh_FXP_INIT:
			e = wrc(c, initReply)
		case ssh_FXP_OPEN:
			var path string
			var flags uint32
			var a Attr
			e = parseAttr(p.B32(&id).B32String(&path).B32(&flags), &a).End()
			if e != nil {
				return e
			}
			if h.nfiles() >= maxFiles {
				e = etoomany
				continue
			}
			var f File
			f, e = fs.OpenFile(path, flags, &a)
			if e != nil {
				continue
			}
			e = writeHandle(c, id, h.newFile(f))
		case ssh_FXP_CLOSE:
			var handle string
			e = p.B32(&id).B32String(&handle).End()
			if e != nil {
				return e
			}
			h.closeHandle(handle)
			e = writeErr(c, id, nil)
		case ssh_FXP_READ:
			var handle string
			var offset uint64
			var length uint32
			var n int
			e = p.B32(&id).B32String(&handle).B64(&offset).B32(&length).End()
			debug("READ0", id, handle, offset, length, e)
			if e != nil {
				return e
			}
			f := h.getFile(handle)
			debug("READ1", f)
			if f == nil {
				return einvhandle
			}
			if length > 64*1024 {
				length = 64 * 1024
			}
			bs := bytepool.Alloc(int(length))
			n, e = f.ReadAt(bs, int64(offset))
			debug("READ2", n, e)
			// Handle go readers that return io.EOF and bytes at the same time.
			if e == io.EOF && n > 0 {
				e = nil
			}
			if e != nil {
				bytepool.Free(bs)
				continue
			}
			bs = bs[0:n]
			e = wrc(c, binp.Out().B32(1+4+4+uint32(len(bs))).Byte(ssh_FXP_DATA).B32(id).B32(uint32(len(bs))).Out())
			debug("READ3", len(bs), e)
			if e == nil {
				e = wrc(c, bs)
			}
			debug("READ4", e)
			bytepool.Free(bs)
		case ssh_FXP_WRITE:
			var handle string
			var offset uint64
			var length uint32
			e = p.B32(&id).B32String(&handle).B64(&offset).B32(&length).End()
			if e != nil {
				return e
			}
			f := h.getFile(handle)
			if f == nil {
				return einvhandle
			}
			var bs []byte
			p.NBytesPeek(int(length), &bs)
			_, e = f.WriteAt(bs, int64(offset))
			e = writeErr(c, id, e)
		case ssh_FXP_LSTAT, ssh_FXP_STAT:
			var path string
			var a *Attr
			e = p.B32(&id).B32String(&path).End()
			if e != nil {
				return e
			}
			a, e = fs.Stat(path, op == ssh_FXP_LSTAT)
			e = writeAttr(c, id, a, e)
		case ssh_FXP_FSTAT:
			var handle string
			var a *Attr
			e = p.B32(&id).B32String(&handle).End()
			if e != nil {
				return e
			}
			f := h.getFile(handle)
			if f == nil {
				return einvhandle
			}
			a, e = f.FStat()
			e = writeAttr(c, id, a, e)
		case ssh_FXP_SETSTAT:
			var path string
			var a Attr
			e = parseAttr(p.B32(&id).B32String(&path), &a).End()
			if e != nil {
				return e
			}
			e = writeErr(c, id, fs.SetStat(path, &a))
		case ssh_FXP_FSETSTAT:
			var handle string
			var a Attr
			e = parseAttr(p.B32(&id).B32String(&handle), &a).End()
			if e != nil {
				return e
			}
			f := h.getFile(handle)
			if f == nil {
				return einvhandle
			}
			e = writeErr(c, id, f.FSetStat(&a))
		case ssh_FXP_OPENDIR:
			var path string
			var dh Dir
			e = p.B32(&id).B32String(&path).End()
			if e != nil {
				return e
			}
			debug("opendir", id, path)
			dh, e = fs.OpenDir(path)
			debug("opendir =>", dh, e)
			if e != nil {
				continue
			}
			e = writeHandle(c, id, h.newDir(dh))

		case ssh_FXP_READDIR:
			var handle string
			e = p.B32(&id).B32String(&handle).End()
			if e != nil {
				return e
			}
			debug("readdir", id, handle)
			f := h.getDir(handle)
			if f == nil {
				return einvhandle
			}
			var fis []NamedAttr
			fis, e = f.Readdir(1024)
			if e != nil {
				continue
			}
			var l binp.Len
			o := binp.Out().LenB32(&l).LenStart(&l).Byte(ssh_FXP_NAME).B32(id).B32(uint32(len(fis)))
			for _, fi := range fis {
				// FIXME
				n := fi.Name
				const flag = ssh_FILEXFER_ATTR_SIZE
				o.B32String(n).B32String(n).B32(flag).B64(uint64(fi.Size))
			}
			o.LenDone(&l)
			e = wrc(c, o.Out())

		case ssh_FXP_REMOVE:
			var path string
			e = p.B32(&id).B32String(&path).End()
			if e != nil {
				return e
			}
			e = writeErr(c, id, fs.Remove(path))
		case ssh_FXP_MKDIR:
			var path string
			var a Attr
			p = p.B32(&id).B32String(&path)
			e = parseAttr(p, &a).End()
			if e != nil {
				return e
			}
			e = writeErr(c, id, fs.Mkdir(path, &a))
		case ssh_FXP_RMDIR:
			var path string
			e = p.B32(&id).B32String(&path).End()
			if e != nil {
				return e
			}
			e = writeErr(c, id, fs.Rmdir(path))
		case ssh_FXP_REALPATH:
			var path string
			p.B32(&id).B32String(&path).End()
			path, e = fs.RealPath(path)
			e = writeNameOnly(c, id, path, e)
		case ssh_FXP_RENAME:
			e = writeFail(c, id) // FIXME
		case ssh_FXP_READLINK:
			var path string
			e = p.B32(&id).B32String(&path).End()
			path, e = fs.ReadLink(path)
			e = writeNameOnly(c, id, path, e)
		case ssh_FXP_SYMLINK:
			e = writeFail(c, id) // FIXME
		}
		if e != nil {
			return e
		}
	}
}

var einvhandle = errors.New("Client supplied an invalid handle")
var etoomany = errors.New("Too many files")

const maxFiles = 0x100

func readPacketHeader(rd *bufio.Reader) (int, byte, error) {
	bs := make([]byte, 5)
	_, e := io.ReadFull(rd, bs)
	if e != nil {
		return 0, 0, e
	}
	return int(binary.BigEndian.Uint32(bs)), bs[4], nil
}

func parseAttr(p *binp.Parser, a *Attr) *binp.Parser {
	p = p.B32(&a.Flags)
	if a.Flags&ssh_FILEXFER_ATTR_SIZE != 0 {
		p = p.B64(&a.Size)
	}
	if a.Flags&ssh_FILEXFER_ATTR_UIDGID != 0 {
		p = p.B32(&a.Uid).B32(&a.Gid)
	}
	if a.Flags&ssh_FILEXFER_ATTR_PERMISSIONS != 0 {
		p = p.B32(&a.Mode)
	}
	if a.Flags&ssh_FILEXFER_ATTR_ACMODTIME != 0 {
		p = p.B32(&a.ATime).B32(&a.MTime)
	}
	if a.Flags&ssh_FILEXFER_ATTR_EXTENDED != 0 {
		var count uint32
		p = p.B32(&count)
		if count > 0xFF {
			return nil
		}
		ss := make([]string, 2*int(count))
		for i := 0; i < int(count); i++ {
			var k, v string
			p = p.B32String(&k).B32String(&v)
			ss[2*i+0] = k
			ss[2*i+1] = v
		}
		a.Extended = ss
	}
	return p
}

func writeAttr(c ssh.Channel, id uint32, a *Attr, e error) error {
	if e != nil {
		return writeErr(c, id, e)
	}
	var l binp.Len
	o := binp.Out().LenB32(&l).LenStart(&l).Byte(ssh_FXP_ATTRS).B32(id).B32(a.Flags)
	if a.Flags&ssh_FILEXFER_ATTR_SIZE != 0 {
		o = o.B64(a.Size)
	}
	if a.Flags&ssh_FILEXFER_ATTR_UIDGID != 0 {
		o = o.B32(a.Uid).B32(a.Gid)
	}
	if a.Flags&ssh_FILEXFER_ATTR_PERMISSIONS != 0 {
		o = o.B32(a.Mode)
	}
	if a.Flags&ssh_FILEXFER_ATTR_ACMODTIME != 0 {
		o = o.B32(a.ATime).B32(a.MTime)
	}
	if a.Flags&ssh_FILEXFER_ATTR_EXTENDED != 0 {
		count := uint32(len(a.Extended) / 2)
		o = o.B32(count)
		for _, s := range a.Extended {
			o = o.B32String(s)
		}
	}
	o.LenDone(&l)
	return wrc(c, o.Out())
}

func writeNameOnly(c ssh.Channel, id uint32, path string, e error) error {
	if e != nil {
		return writeErr(c, id, e)
	}
	var l binp.Len
	o := binp.Out().LenB32(&l).LenStart(&l).Byte(ssh_FXP_NAME).B32(id).B32(1)
	o.B32String(path).B32String(path).B32(0)
	o.LenDone(&l)
	return wrc(c, o.Out())
}

var failTmpl = []byte{0, 0, 0, 1 + 4 + 4 + 4 + 4, ssh_FXP_STATUS, 0, 0, 0, 0, 0, 0, 0, ssh_FX_FAILURE, 0, 0, 0, 0, 0, 0, 0, 0}

func writeFail(c ssh.Channel, id uint32) error {
	bs := make([]byte, len(failTmpl))
	copy(bs, failTmpl)
	binary.BigEndian.PutUint32(bs[5:], id)
	return wrc(c, bs)
}

func writeErr(c ssh.Channel, id uint32, err error) error {
	bs := make([]byte, len(failTmpl))
	copy(bs, failTmpl)
	binary.BigEndian.PutUint32(bs[5:], id)
	var code byte = ssh_FX_FAILURE
	switch err {
	case nil:
		code = ssh_FX_OK
	case io.EOF:
		code = ssh_FX_EOF
	}
	bs[12] = code
	return wrc(c, bs)
}

func writeHandle(c ssh.Channel, id uint32, handle string) error {
	return wrc(c, binp.OutCap(4+9+len(handle)).B32(uint32(9+len(handle))).B8(ssh_FXP_HANDLE).B32(id).B32String(handle).Out())
}

func wrc(c ssh.Channel, bs []byte) error {
	_, e := c.Write(bs)
	return e
}

func discard(brd *bufio.Reader, n int) error {
	if n == 0 {
		return nil
	}
	m, e := io.Copy(ioutil.Discard, &io.LimitedReader{R: brd, N: int64(n)})
	if int(m) == n && e == io.EOF {
		e = nil
	}
	return e
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/SchumacherFM/mediamock/common"
	"github.com/SchumacherFM/mediamock/record"
	"github.com/codegangsta/cli"
	_ "github.com/mkevac/debugcharts"
)

const (
	ContentEncoding = "Content-Encoding"
	ContentLength   = "Content-Length"
	ContentType     = "Content-Type"

	CharsetUTF8                = "charset=utf-8"
	ApplicationJSON            = "application/json"
	ApplicationJSONCharsetUTF8 = ApplicationJSON + "; " + CharsetUTF8

	TextHTML             = "text/html"
	TextHTMLCharsetUTF8  = TextHTML + "; " + CharsetUTF8
	TextPlain            = "text/plain"
	TextPlainCharsetUTF8 = TextPlain + "; " + CharsetUTF8
)

var (
	brByte     = []byte("\n")
	cacheUntil = time.Now().AddDate(60, 0, 0).Format(http.TimeFormat)
)

func actionServer(ctx *cli.Context) {
	h := newHandle(ctx)
	fmt.Fprintf(os.Stdout, "Found %d entries in the CSV file\n", h.length)
	fmt.Fprintf(os.Stdout, "Server started: %s\n", ctx.String("host"))
	http.HandleFunc("/", h.handler)
	if err := http.ListenAndServe(ctx.String("host"), nil); err != nil {
		panic(err)
	}
}

type handle struct {
	// fileMap contains sometimes up to 200k entries
	fileMap map[string]record.Record
	sync.RWMutex
	length  int
	pattern string
}

func newHandle(ctx *cli.Context) *handle {
	csvFile := ctx.String("i")
	rec := getCSVContent(csvFile)

	h := &handle{
		fileMap: make(map[string]record.Record),
		pattern: ctx.GlobalString("p"),
	}
	h.Lock()
	defer h.Unlock()

	h.fileMap["favicon.ico"] = record.NewRecordFields("happy", "favicon.ico", 16, 16)
	for _, row := range rec {
		rec, err := record.NewRecord(h.pattern, row...)
		if err != nil {
			common.InfoErr("File %s contains error: %s\n", csvFile, err)
		}
		rec.Path = ctx.String("urlPrefix") + rec.Path
		h.fileMap[rec.Path] = rec
	}
	h.length = len(rec)
	return h
}

func (h *handle) root(w http.ResponseWriter, r *http.Request) {
	w.Header().Set(ContentType, TextHTMLCharsetUTF8)
	fmt.Fprint(w, `
	<title>Mediamock Index</title>

		JSON Index

		HTML Index

		Debug Charts

	`)
}

func (h *handle) rootJSON(w http.ResponseWriter, r *http.Request) {
	w.Header().Set(ContentType, ApplicationJSONCharsetUTF8)
	h.RLock()
	for _, rec := range h.fileMap {
		if err := rec.ToJSON(w); err != nil {
			common.InfoErr("Failed to write JSON with error: %s\n", err)
		}
		if _, err := w.Write(brByte); err != nil {
			common.InfoErr("Failed to write JSON with error: %s\n", err)
		}
	}
	h.RUnlock()
}

func (h *handle) rootHTML(w http.ResponseWriter, r *http.Request) {
	h.RLock()
	defer h.RUnlock()
	w.Header().Set(ContentType, TextHTMLCharsetUTF8)
	fmt.Fprint(w, `<html>	<head><title>Mediamock Content Table</title></head>	<body><table>`)
	fmt.Fprint(w, `

			ModTime
			Width px
			Height px
			Link

`)

	var pathSlice = make(sort.StringSlice, len(h.fileMap))
	var i int
	for key, _ := range h.fileMap {
		pathSlice[i] = key
		i++
	}
	pathSlice.Sort()

	for _, key := range pathSlice {
		rec := h.fileMap[key]

		_, err := fmt.Fprintf(w, `

			%s
			%d
			%d
			%s

`,
			rec.ModTime,
			rec.Width,
			rec.Height,
			rec.Path, rec.Path,
		)
		if err != nil {
			common.InfoErr("Failed to write HTML table with error: %s\n", err)
		}

		if _, err := w.Write(brByte); err != nil {
			common.InfoErr("Failed to write brByte with error: %s\n", err)
		}

	}

	fmt.Fprint(w, `</tbody></table></body>	</html>`)
}

// post or get request
// $ curl --data "file=media/catalog/product/1/2/120---microsoft-natural-ergonomic-keyboard-4000.jpg" http://127.0.0.1:4711/fileDetails
// and returns:
// {"Path":"media/catalog/product/1/2/120---microsoft-natural-ergonomic-keyboard-4000.jpg","ModTime":"2014-02-16T03:27:45+01:00","Width":5184,"Height":3456}
func (h *handle) fileDetails(w http.ResponseWriter, r *http.Request) {
	filePath := r.FormValue("file")
	if filePath == "" {
		http.NotFound(w, r)
		return
	}

	h.RLock()
	defer h.RUnlock()

	rec, ok := h.fileMap[filePath]
	if !ok {
		common.InfoErr("%s not found in CSV file\n", filePath)
		http.NotFound(w, r)
		return
	}
	w.Header().Set(ContentType, ApplicationJSONCharsetUTF8)
	if err := rec.ToJSON(w); err != nil {
		common.InfoErr("Failed to write JSON with error: %s\n", err)
	}
}

func (h *handle) handler(w http.ResponseWriter, r *http.Request) {

	switch r.URL.Path {
	case "/":
		h.root(w, r)
		return
	case "/json":
		h.rootJSON(w, r)
		return
	case "/html":
		h.rootHTML(w, r)
		return
	case "/fileDetails":
		h.fileDetails(w, r)
		return
	case "/robots.txt":
		w.Header().Set("Content-Type", "text/plain")
		fmt.Fprint(w, "User-agent: *\nDisallow: /")
		return
	}

	h.RLock()
	defer h.RUnlock()

	var path = r.URL.Path[1:]
	rec, ok := h.fileMap[path]
	if !ok {

		if false == common.IsImage(path) {
			http.NotFound(w, r)
			return
		}

		width, height := common.FileSizeFromPath(path)

		if width > 0 && height > 0 {
			rec = record.NewRecordFields(h.pattern, path, width, height)
		} else {
			http.NotFound(w, r)
			return
		}
	}

	w.Header().Set("Cache-Control", "max-age:290304000, public")
	w.Header().Set("Last-Modified", rec.ModTime.Format(http.TimeFormat))
	w.Header().Set("Expires", cacheUntil)

	switch rec.FileExt() {
	case ".gif":
		w.Header().Set(ContentType, "image/gif")
	case ".png":
		w.Header().Set(ContentType, "image/png")
	case ".jpg", ".jpeg":
		w.Header().Set(ContentType, "image/jpeg")
	case ".css":
		w.Header().Set(ContentType, "text/css")
	case ".js":
		w.Header().Set(ContentType, "application/javascript")
	case ".pdf":
		w.Header().Set(ContentType, "application/pdf")
	case ".txt":
		w.Header().Set(ContentType, TextPlain)
	case ".html":
		w.Header().Set(ContentType, TextHTML)
	}

	rec.CreateContent(path, w)
}
<commit_msg>Fix routing when reading from map<commit_after>package main

import (
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/SchumacherFM/mediamock/common"
	"github.com/SchumacherFM/mediamock/record"
	"github.com/codegangsta/cli"
	_ "github.com/mkevac/debugcharts"
)

const (
	ContentEncoding = "Content-Encoding"
	ContentLength   = "Content-Length"
	ContentType     = "Content-Type"

	CharsetUTF8                = "charset=utf-8"
	ApplicationJSON            = "application/json"
	ApplicationJSONCharsetUTF8 = ApplicationJSON + "; " + CharsetUTF8

	TextHTML             = "text/html"
	TextHTMLCharsetUTF8  = TextHTML + "; " + CharsetUTF8
	TextPlain            = "text/plain"
	TextPlainCharsetUTF8 = TextPlain + "; " + CharsetUTF8
)

var (
	brByte     = []byte("\n")
	cacheUntil = time.Now().AddDate(60, 0, 0).Format(http.TimeFormat)
)

func actionServer(ctx *cli.Context) {
	h := newHandle(ctx)
	fmt.Fprintf(os.Stdout, "Found %d entries in the CSV file\n", h.length)
	fmt.Fprintf(os.Stdout, "Server started: %s\n", ctx.String("host"))
	http.HandleFunc("/", h.handler)
	if err := http.ListenAndServe(ctx.String("host"), nil); err != nil {
		panic(err)
	}
}

type handle struct {
	// fileMap contains sometimes up to 200k entries
	fileMap map[string]record.Record
	sync.RWMutex
	length  int
	pattern string
}

func newHandle(ctx *cli.Context) *handle {
	csvFile := ctx.String("i")
	rec := getCSVContent(csvFile)

	h := &handle{
		fileMap: make(map[string]record.Record),
		pattern: ctx.GlobalString("p"),
	}
	h.Lock()
	defer h.Unlock()

	h.fileMap["favicon.ico"] = record.NewRecordFields("happy", "favicon.ico", 16, 16)
	for _, row := range rec {
		rec, err := record.NewRecord(h.pattern, row...)
		if err != nil {
			common.InfoErr("File %s contains error: %s\n", csvFile, err)
		}
		rec.Path = ctx.String("urlPrefix") + rec.Path
		h.fileMap[rec.Path] = rec
	}
	h.length = len(rec)
	return h
}

func (h *handle) root(w http.ResponseWriter, r *http.Request) {
	w.Header().Set(ContentType, TextHTMLCharsetUTF8)
	fmt.Fprint(w, `
	<title>Mediamock Index</title>

		JSON Index

		HTML Index

		Debug Charts

	`)
}

func (h *handle) rootJSON(w http.ResponseWriter, r *http.Request) {
	w.Header().Set(ContentType, ApplicationJSONCharsetUTF8)
	h.RLock()
	for _, rec := range h.fileMap {
		if err := rec.ToJSON(w); err != nil {
			common.InfoErr("Failed to write JSON with error: %s\n", err)
		}
		if _, err := w.Write(brByte); err != nil {
			common.InfoErr("Failed to write JSON with error: %s\n", err)
		}
	}
	h.RUnlock()
}

func (h *handle) rootHTML(w http.ResponseWriter, r *http.Request) {
	h.RLock()
	defer h.RUnlock()
	w.Header().Set(ContentType, TextHTMLCharsetUTF8)
	fmt.Fprint(w, `<html>	<head><title>Mediamock Content Table</title></head>	<body><table>`)
	fmt.Fprint(w, `

			ModTime
			Width px
			Height px
			Link

`)

	var pathSlice = make(sort.StringSlice, len(h.fileMap))
	var i int
	for key, _ := range h.fileMap {
		pathSlice[i] = key
		i++
	}
	pathSlice.Sort()

	for _, key := range pathSlice {
		rec := h.fileMap[key]

		_, err := fmt.Fprintf(w, `

			%s
			%d
			%d
			%s

`,
			rec.ModTime,
			rec.Width,
			rec.Height,
			rec.Path, rec.Path,
		)
		if err != nil {
			common.InfoErr("Failed to write HTML table with error: %s\n", err)
		}

		if _, err := w.Write(brByte); err != nil {
			common.InfoErr("Failed to write brByte with error: %s\n", err)
		}

	}

	fmt.Fprint(w, `</tbody></table></body>	</html>`)
}

// post or get request
// $ curl --data "file=media/catalog/product/1/2/120---microsoft-natural-ergonomic-keyboard-4000.jpg" http://127.0.0.1:4711/fileDetails
// and returns:
// {"Path":"media/catalog/product/1/2/120---microsoft-natural-ergonomic-keyboard-4000.jpg","ModTime":"2014-02-16T03:27:45+01:00","Width":5184,"Height":3456}
func (h *handle) fileDetails(w http.ResponseWriter, r *http.Request) {
	filePath := r.FormValue("file")
	if filePath == "" {
		http.NotFound(w, r)
		return
	}

	h.RLock()
	defer h.RUnlock()

	rec, ok := h.fileMap[filePath]
	if !ok {
		common.InfoErr("%s not found in CSV file\n", filePath)
		http.NotFound(w, r)
		return
	}
	w.Header().Set(ContentType, ApplicationJSONCharsetUTF8)
	if err := rec.ToJSON(w); err != nil {
		common.InfoErr("Failed to write JSON with error: %s\n", err)
	}
}

func (h *handle) handler(w http.ResponseWriter, r *http.Request) {

	switch r.URL.Path {
	case "/":
		h.root(w, r)
		return
	case "/json":
		h.rootJSON(w, r)
		return
	case "/html":
		h.rootHTML(w, r)
		return
	case "/fileDetails":
		h.fileDetails(w, r)
		return
	case "/robots.txt":
		w.Header().Set("Content-Type", "text/plain")
		fmt.Fprint(w, "User-agent: *\nDisallow: /")
		return
	}

	h.RLock()
	defer h.RUnlock()

	var path = r.URL.Path[1:]

	rec, ok := h.fileMap[path]
	if !ok && len(path) > 0 && path[0] != '/' {
		rec, ok = h.fileMap["/"+path]
	}

	if !ok {

		if false == common.IsImage(path) {
			http.NotFound(w, r)
			return
		}

		width, height := common.FileSizeFromPath(path)

		if width > 0 && height > 0 {
			rec = record.NewRecordFields(h.pattern, path, width, height)
		} else {
			http.NotFound(w, r)
			return
		}
	}

	w.Header().Set("Cache-Control", "max-age:290304000, public")
	w.Header().Set("Last-Modified", rec.ModTime.Format(http.TimeFormat))
	w.Header().Set("Expires", cacheUntil)

	switch rec.FileExt() {
	case ".gif":
		w.Header().Set(ContentType, "image/gif")
	case ".png":
		w.Header().Set(ContentType, "image/png")
	case ".jpg", ".jpeg":
		w.Header().Set(ContentType, "image/jpeg")
	case ".css":
		w.Header().Set(ContentType, "text/css")
	case ".js":
		w.Header().Set(ContentType, "application/javascript")
	case ".pdf":
		w.Header().Set(ContentType, "application/pdf")
	case ".txt":
		w.Header().Set(ContentType, TextPlain)
	case ".html":
		w.Header().Set(ContentType, TextHTML)
	}

	rec.CreateContent(path, w)
}
<|endoftext|>


<commit_before>package main

import (
  "fmt"
  "log"
  "net/http"
  "strings"
  "time"
  "encoding/json"
  "flag"

  "github.com/marcelocajueiro/simple_url_shortener/urls"
)

var (
  port    int
  urlBase string
)

func init() {
  flag.IntVar(&port, "p", 8888 , "to set a custom port")
  urlBase = fmt.Sprintf("http://localhost:%d", port)

  flag.Parse()
}

func main() {
  stats := make (chan string)
  defer close (stats)
  go newStatistic(stats)

  http.HandleFunc("/api/shorten", Shortener)
  http.Handle("/r/", &Redirector{stats})
  http.HandleFunc("/api/stats/", StatsViewer)

  printLog("Starting server on port %d...", port)
  log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
}

type Redirector struct {
  stats chan string
}
type Headers map[string]string
type Url struct {
  Id        string
  CreatedAt time.Time
  Destiny   string
}

func Shortener(w http.ResponseWriter, r *http.Request) {
  if r.Method != "POST" {
    respondWith(w, http.StatusMethodNotAllowed, Headers{
      "Allow": "POST",
    })
    return
  }

  url, new, err := urls.FindOrCreateNewUrl(extractUrl(r))

  if err != nil {
    respondWith(w, http.StatusBadRequest, nil)
    return
  }

  var status int

  if new {
    status = http.StatusCreated
  } else {
    status = http.StatusOK
  }

  shortUrl := fmt.Sprintf("%s/r/%s", urlBase, url.Id)
  respondWith(w, status, Headers{
    "Location": shortUrl,
    "Link": fmt.Sprintf("<%s/api/stats/%s>; rel=\"stats\"", urlBase, url.Id),
  })

  printLog("URL %s successfully shortened to %s", url.Destiny, shortUrl)
}

func (red *Redirector) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  findUrlAndExecute(w, r, func (url *urls.Url) {
    http.Redirect(w, r, url.Destiny, http.StatusMovedPermanently)
    red.stats <- url.Id
  })
}

func StatsViewer(w http.ResponseWriter, r *http.Request) {
  findUrlAndExecute(w, r, func (url *urls.Url) {
    json, err := json.Marshal(url.Stats())

    if err != nil {
      w.WriteHeader(http.StatusInternalServerError)
      return
    }

    respondWithJSON(w, string(json))
  })
}

func findUrlAndExecute(
  w http.ResponseWriter,
  r *http.Request,
  executor func(*urls.Url),
) {
  path := strings.Split(r.URL.Path, "/")
  id := path[len(path) -1]

  if url := urls.Search(id); url != nil {
    executor(url)
  } else {
    http.NotFound(w, r)
  }
}

func respondWith(w http.ResponseWriter, status int, headers Headers) {
  for k, v := range headers {
    w.Header().Set(k, v)
  }
  w.WriteHeader(status)
}

func respondWithJSON(w http.ResponseWriter, response string) {
  respondWith(w, http.StatusOK, Headers{
    "Content-Type": "application/json",
  })
  fmt.Fprintf(w, response)
}

func extractUrl(r *http.Request) string {
  url := make([]byte, r.ContentLength, r.ContentLength)
  r.Body.Read(url)
  return string (url)
}

func newStatistic(ids <-chan string) {
  for id := range ids {
    urls.RegisterClick(id)
    printLog("%s was clicked", id)
  }
}

func printLog(format string, values ...interface{}) {
  // log.Printf("Testando %d %d %d", 1, 2, 3)
  log.Printf(fmt.Sprintf("%s\n", format), values...)
}
<commit_msg>Remove unnecessary commented code<commit_after>package main

import (
  "fmt"
  "log"
  "net/http"
  "strings"
  "time"
  "encoding/json"
  "flag"

  "github.com/marcelocajueiro/simple_url_shortener/urls"
)

var (
  port    int
  urlBase string
)

func init() {
  flag.IntVar(&port, "p", 8888 , "to set a custom port")
  urlBase = fmt.Sprintf("http://localhost:%d", port)

  flag.Parse()
}

func main() {
  stats := make (chan string)
  defer close (stats)
  go newStatistic(stats)

  http.HandleFunc("/api/shorten", Shortener)
  http.Handle("/r/", &Redirector{stats})
  http.HandleFunc("/api/stats/", StatsViewer)

  printLog("Starting server on port %d...", port)
  log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
}

type Redirector struct {
  stats chan string
}
type Headers map[string]string
type Url struct {
  Id        string
  CreatedAt time.Time
  Destiny   string
}

func Shortener(w http.ResponseWriter, r *http.Request) {
  if r.Method != "POST" {
    respondWith(w, http.StatusMethodNotAllowed, Headers{
      "Allow": "POST",
    })
    return
  }

  url, new, err := urls.FindOrCreateNewUrl(extractUrl(r))

  if err != nil {
    respondWith(w, http.StatusBadRequest, nil)
    return
  }

  var status int

  if new {
    status = http.StatusCreated
  } else {
    status = http.StatusOK
  }

  shortUrl := fmt.Sprintf("%s/r/%s", urlBase, url.Id)
  respondWith(w, status, Headers{
    "Location": shortUrl,
    "Link": fmt.Sprintf("<%s/api/stats/%s>; rel=\"stats\"", urlBase, url.Id),
  })

  printLog("URL %s successfully shortened to %s", url.Destiny, shortUrl)
}

func (red *Redirector) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  findUrlAndExecute(w, r, func (url *urls.Url) {
    http.Redirect(w, r, url.Destiny, http.StatusMovedPermanently)
    red.stats <- url.Id
  })
}

func StatsViewer(w http.ResponseWriter, r *http.Request) {
  findUrlAndExecute(w, r, func (url *urls.Url) {
    json, err := json.Marshal(url.Stats())

    if err != nil {
      w.WriteHeader(http.StatusInternalServerError)
      return
    }

    respondWithJSON(w, string(json))
  })
}

func findUrlAndExecute(
  w http.ResponseWriter,
  r *http.Request,
  executor func(*urls.Url),
) {
  path := strings.Split(r.URL.Path, "/")
  id := path[len(path) -1]

  if url := urls.Search(id); url != nil {
    executor(url)
  } else {
    http.NotFound(w, r)
  }
}

func respondWith(w http.ResponseWriter, status int, headers Headers) {
  for k, v := range headers {
    w.Header().Set(k, v)
  }
  w.WriteHeader(status)
}

func respondWithJSON(w http.ResponseWriter, response string) {
  respondWith(w, http.StatusOK, Headers{
    "Content-Type": "application/json",
  })
  fmt.Fprintf(w, response)
}

func extractUrl(r *http.Request) string {
  url := make([]byte, r.ContentLength, r.ContentLength)
  r.Body.Read(url)
  return string (url)
}

func newStatistic(ids <-chan string) {
  for id := range ids {
    urls.RegisterClick(id)
    printLog("%s was clicked", id)
  }
}

func printLog(format string, values ...interface{}) {
  log.Printf(fmt.Sprintf("%s\n", format), values...)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"compress/zlib"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
)

var port = flag.Int("port", 8080, "which port to listen on")
var authApiKey string
var dbUrl string

var rootMetrics = map[string]string{
	"system.load.1":       "system.load.1",
	"system.load.5":       "system.load.5",
	"system.load.15":      "system.load.15",
	"system.load.norm.1":  "system.load.norm.1",
	"system.load.norm.5":  "system.load.norm.5",
	"system.load.norm.15": "system.load.norm.15",
}

type Metric struct {
	Name    string          `json:"name"`
	Columns []string        `json:"columns"`
	Points  [][]interface{} `json:"points"`
}

func NewMetric(host, name string, timestamp, value interface{}, tags map[string]string) *Metric {
	columns := []string{"time", "value", "hostname"}
	points := [][]interface{}{{timestamp, value, host}}
	if tags != nil {
		for k, v := range tags {
			if k == "hostname" {
				points[0][2] = v
			} else {
				columns = append(columns, k)
				points[0] = append(points[0], v)
			}
		}
	}
	return &Metric{name, columns, points}
}

func PushMetrics(metrics []*Metric) {
	if len(metrics) == 0 {
		return
	}
	log.Printf("Pushing %d metrics to InfluxDB", len(metrics))

	body, err := json.Marshal(metrics)
	if err != nil {
		log.Println(err)
		return
	}

	log.Println(string(body))

	resp, err := http.Post(dbUrl, "application/json", bytes.NewBuffer(body))
	if err != nil {
		log.Println(err)
	} else {
		log.Println(resp)
	}
}

func mapMetrics(data map[string]interface{}) []*Metric {
	metrics := []*Metric{}
	host := data["internalHostname"].(string)
	timestamp := data["collection_timestamp"].(float64)

	fmt.Printf("Unmapped metrics (%s):", host)
	for key, value := range data {
		name, ok := rootMetrics[key]
		if ok {
			metrics = append(metrics, NewMetric(host, name, uint64(timestamp), value, nil))
		} else {
			fmt.Println(key)
		}
	}
	return metrics
}

func handleIntake(w http.ResponseWriter, req *http.Request) {
	if handleApiKey(w, req) {
		return
	}

	log.Println(req.Method, req.URL.Path)

	body := req.Body
	if req.Header.Get("Content-Encoding") == "deflate" {
		var err error
		body, err = zlib.NewReader(body)
		if err != nil {
			log.Println(err)
			io.WriteString(w, `{"status":"failed"}`)
			return
		}
	}
	buf := bytes.NewBuffer([]byte{})
	_, err := io.Copy(buf, body)
	if err != nil {
		log.Println(err)
		io.WriteString(w, `{"status":"failed"}`)
		return
	}

	data := make(map[string]interface{})
	err = json.Unmarshal(buf.Bytes(), &data)
	if err != nil {
		log.Println(err)
		io.WriteString(w, `{"status":"failed"}`)
		return
	}

	metrics := mapMetrics(data)
	go PushMetrics(metrics)

	w.Header().Set("Content-Type", "application/json")
	io.WriteString(w, `{"status":"ok"}`)
}

func handleApi(w http.ResponseWriter, req *http.Request) {
	if handleApiKey(w, req) {
		return
	}

	log.Println(req.Method, req.URL.Path)

	body := req.Body
	if req.Header.Get("Content-Encoding") == "deflate" {
		var err error
		body, err = zlib.NewReader(body)
		if err != nil {
			log.Println(err)
			io.WriteString(w, `{"status":"failed"}`)
			return
		}
	}
	buf := bytes.NewBuffer([]byte{})
	_, err := io.Copy(buf, body)
	if err != nil {
		log.Println(err)
		io.WriteString(w, `{"status":"failed"}`)
		return
	} else {
		log.Println(buf.String())
	}

	w.Header().Set("Content-Type", "application/json")
	io.WriteString(w, `{"status":"ok"}`)
}

func handleApiKey(w http.ResponseWriter, req *http.Request) bool {
	if req.UserAgent() == "Datadog-Status-Check" {
		io.WriteString(w, "STILL-ALIVE\n")
		return true
	}
	err := req.ParseForm()
	if err != nil {
		log.Println("Error parsing form:", err)
		http.Error(w, err.Error(), 500)
		return true
	}
	values := req.Form
	api_key := values.Get("api_key")
	delete(values, "api_key")
	if len(authApiKey) > 0 && api_key != authApiKey {
		log.Println("Got bad API key:", api_key)
		http.Error(w, "Bad API Key", 403)
		return true
	}
	return false
}

func main() {
	flag.Parse()
	authApiKey = os.Getenv("API_KEY")
	if len(authApiKey) == 0 {
		log.Println("Warning: API_KEY is blank")
	}
	dbUrl = os.Getenv("DB_URL")
	if len(dbUrl) == 0 {
		dbUrl = "http://localhost:8086/db/datadog/series?u=root&p=root"
	}

	log.Println("dd-house listening on", *port)

	http.HandleFunc("/intake", handleIntake)
	http.HandleFunc("/api/v1/series/", handleApi)
	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*port), nil))
}
<commit_msg>Add more metrics<commit_after>package main

import (
	"bytes"
	"compress/zlib"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
)

var port = flag.Int("port", 8080, "which port to listen on")
var authApiKey string
var dbUrl string

var rootMetrics = map[string]string{
	"system.load.1":       "system.load.1",
	"system.load.5":       "system.load.5",
	"system.load.15":      "system.load.15",
	"system.load.norm.1":  "system.load.norm.1",
	"system.load.norm.5":  "system.load.norm.5",
	"system.load.norm.15": "system.load.norm.15",

	"cpuIdle":   "system.cpu.idle",
	"cpuUser":   "system.cpu.user",
	"cpuWait":   "system.cpu.iowait",
	"cpuSystem": "system.cpu.system",
	"cpuStolen": "system.cpu.stolen",

	"memBuffers":       "system.mem.buffered",
	"memPhysPctUsable": "system.mem.pct_usable",
	"memShared":        "system.mem.shared",
	"memPhysTotal":     "system.mem.total",
	"memPhysUsable":    "system.mem.usable",
	"memCached":        "system.mem.cached",
	"memPhysUsed":      "system.mem.used",
	"memPhysFree":      "system.mem.free",

	"memSwapFree":    "system.swap.free",
	"memSwapTotal":   "system.swap.total",
	"memSwapUsed":    "system.swap.used",
	"memSwapPctFree": "system.swap.pct_free",
}

type Metric struct {
	Name    string          `json:"name"`
	Columns []string        `json:"columns"`
	Points  [][]interface{} `json:"points"`
}

func NewMetric(host, name string, timestamp, value interface{}, tags map[string]string) *Metric {
	columns := []string{"time", "value", "hostname"}
	points := [][]interface{}{{timestamp, value, host}}
	if tags != nil {
		for k, v := range tags {
			if k == "hostname" {
				points[0][2] = v
			} else {
				columns = append(columns, k)
				points[0] = append(points[0], v)
			}
		}
	}
	return &Metric{name, columns, points}
}

func PushMetrics(metrics []*Metric) {
	if len(metrics) == 0 {
		return
	}
	log.Printf("Pushing %d metrics to InfluxDB", len(metrics))

	body, err := json.Marshal(metrics)
	if err != nil {
		log.Println(err)
		return
	}

	log.Println(string(body))

	resp, err := http.Post(dbUrl, "application/json", bytes.NewBuffer(body))
	if err != nil {
		log.Println(err)
	} else {
		log.Println(resp)
	}
}

func mapMetrics(data map[string]interface{}) []*Metric {
	metrics := []*Metric{}
	host := data["internalHostname"].(string)
	timestamp := data["collection_timestamp"].(float64)

	fmt.Printf("Unmapped metrics (%s):\n", host)
	for key, value := range data {
		name, ok := rootMetrics[key]
		if ok {
			metrics = append(metrics, NewMetric(host, name, uint64(timestamp), value, nil))
		} else {
			fmt.Println(key)
		}
	}
	return metrics
}

func handleIntake(w http.ResponseWriter, req *http.Request) {
	if handleApiKey(w, req) {
		return
	}

	log.Println(req.Method, req.URL.Path)

	body := req.Body
	if req.Header.Get("Content-Encoding") == "deflate" {
		var err error
		body, err = zlib.NewReader(body)
		if err != nil {
			log.Println(err)
			io.WriteString(w, `{"status":"failed"}`)
			return
		}
	}
	buf := bytes.NewBuffer([]byte{})
	_, err := io.Copy(buf, body)
	if err != nil {
		log.Println(err)
		io.WriteString(w, `{"status":"failed"}`)
		return
	}

	data := make(map[string]interface{})
	err = json.Unmarshal(buf.Bytes(), &data)
	if err != nil {
		log.Println(err)
		io.WriteString(w, `{"status":"failed"}`)
		return
	}

	metrics := mapMetrics(data)
	go PushMetrics(metrics)

	w.Header().Set("Content-Type", "application/json")
	io.WriteString(w, `{"status":"ok"}`)
}

func handleApi(w http.ResponseWriter, req *http.Request) {
	if handleApiKey(w, req) {
		return
	}

	log.Println(req.Method, req.URL.Path)

	body := req.Body
	if req.Header.Get("Content-Encoding") == "deflate" {
		var err error
		body, err = zlib.NewReader(body)
		if err != nil {
			log.Println(err)
			io.WriteString(w, `{"status":"failed"}`)
			return
		}
	}
	buf := bytes.NewBuffer([]byte{})
	_, err := io.Copy(buf, body)
	if err != nil {
		log.Println(err)
		io.WriteString(w, `{"status":"failed"}`)
		return
	} else {
		log.Println(buf.String())
	}

	w.Header().Set("Content-Type", "application/json")
	io.WriteString(w, `{"status":"ok"}`)
}

func handleApiKey(w http.ResponseWriter, req *http.Request) bool {
	if req.UserAgent() == "Datadog-Status-Check" {
		io.WriteString(w, "STILL-ALIVE\n")
		return true
	}
	err := req.ParseForm()
	if err != nil {
		log.Println("Error parsing form:", err)
		http.Error(w, err.Error(), 500)
		return true
	}
	values := req.Form
	api_key := values.Get("api_key")
	delete(values, "api_key")
	if len(authApiKey) > 0 && api_key != authApiKey {
		log.Println("Got bad API key:", api_key)
		http.Error(w, "Bad API Key", 403)
		return true
	}
	return false
}

func main() {
	flag.Parse()
	authApiKey = os.Getenv("API_KEY")
	if len(authApiKey) == 0 {
		log.Println("Warning: API_KEY is blank")
	}
	dbUrl = os.Getenv("DB_URL")
	if len(dbUrl) == 0 {
		dbUrl = "http://localhost:8086/db/datadog/series?u=root&p=root"
	}

	log.Println("dd-house listening on", *port)

	http.HandleFunc("/intake", handleIntake)
	http.HandleFunc("/api/v1/series/", handleApi)
	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*port), nil))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strings"

	"golang.org/x/net/websocket"

	"github.com/dustin/go-humanize"
	"gopkg.in/fsnotify.v1"
)

type Server struct {
	dir        http.Dir
	fileServer http.Handler
	html       string
	js         string
}

func NewServer(dir http.Dir) Server {
	html, err := Asset("build/index.html")
	if err != nil {
		panic(err)
	}

	js, err := Asset("build/app.js")
	if err != nil {
		panic(err)
	}

	s := Server{
		dir:        dir,
		fileServer: http.FileServer(dir),
		html:       string(html),
		js:         string(js),
	}

	return s
}

func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/_srv/app.js" {
		w.Header()["Content-Type"] = []string{"application/javascript; charset=utf-8"}
		io.WriteString(w, s.js)
		return
	}

	file, err := s.dir.Open(r.URL.Path)
	if err != nil {
		s.fileServer.ServeHTTP(w, r)
		return
	}
	defer file.Close()

	info, err := file.Stat()
	if err != nil {
		s.fileServer.ServeHTTP(w, r)
		return
	}

	if info.IsDir() {
		// Redirect /dir to /dir/
		if !strings.HasSuffix(r.URL.Path, "/") {
			w.Header()["Location"] = []string{
				r.URL.Path + "/",
			}
			w.WriteHeader(http.StatusFound)
			return
		}

		w.Header()["Content-Type"] = []string{
			"text/html; charset=utf-8",
		}
		io.WriteString(w, s.html)
		return
	} else {
		s.fileServer.ServeHTTP(w, r)
		return
	}
}

func (s Server) wsHandler(ws *websocket.Conn) {
	// Wait for the path
	var _path string
	fmt.Fscanln(ws, &_path)

	path, err := url.QueryUnescape(_path)
	if err != nil {
		panic(err)
	}

	// Send dir
	s.writeDirectory(ws, path)

	// Send dir whenever a file is modified
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err)
	}
	defer watcher.Close()

	err = watcher.Add("." + path)
	if err != nil {
		panic(err)
	}

	for {
		select {
		case <-watcher.Events:
			s.writeDirectory(ws, path)
		case err := <-watcher.Errors:
			panic(err)
		}
	}
}

func (s Server) writeDirectory(w io.Writer, path string) {
	file, err := s.dir.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	info, err := file.Stat()
	if err != nil {
		panic(err)
	}

	if !info.IsDir() {
		panic(fmt.Errorf("oh no"))
	}

	files, err := file.Readdir(999) //TODO: 999 is too small
	if err != nil {
		panic(err)
	}

	outputFiles := []File{}
	for _, f := range files {
		outputFiles = append(outputFiles, File{
			Path: f.Name(),
			Size: humanize.IBytes(uint64(f.Size())),
			Dir:  f.IsDir(),
		})
	}

	sort.Sort(ByPath(outputFiles))

	bytes, err := json.Marshal(outputFiles)
	if err != nil {
		panic(err)
	}

	_, err = io.WriteString(w, string(bytes))
	if err != nil {
		panic(err)
	}
}
<commit_msg>Streamline the JSON<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strings"

	"golang.org/x/net/websocket"

	"github.com/dustin/go-humanize"
	"gopkg.in/fsnotify.v1"
)

type Server struct {
	dir        http.Dir
	fileServer http.Handler
	html       string
	js         string
}

func NewServer(dir http.Dir) Server {
	html, err := Asset("build/index.html")
	if err != nil {
		panic(err)
	}

	js, err := Asset("build/app.js")
	if err != nil {
		panic(err)
	}

	s := Server{
		dir:        dir,
		fileServer: http.FileServer(dir),
		html:       string(html),
		js:         string(js),
	}

	return s
}

func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/_srv/app.js" {
		w.Header()["Content-Type"] = []string{"application/javascript; charset=utf-8"}
		io.WriteString(w, s.js)
		return
	}

	file, err := s.dir.Open(r.URL.Path)
	if err != nil {
		s.fileServer.ServeHTTP(w, r)
		return
	}
	defer file.Close()

	info, err := file.Stat()
	if err != nil {
		s.fileServer.ServeHTTP(w, r)
		return
	}

	if info.IsDir() {
		// Redirect /dir to /dir/
		if !strings.HasSuffix(r.URL.Path, "/") {
			w.Header()["Location"] = []string{
				r.URL.Path + "/",
			}
			w.WriteHeader(http.StatusFound)
			return
		}

		w.Header()["Content-Type"] = []string{
			"text/html; charset=utf-8",
		}
		io.WriteString(w, s.html)
		return
	} else {
		s.fileServer.ServeHTTP(w, r)
		return
	}
}

func (s Server) wsHandler(ws *websocket.Conn) {
	// Wait for the path
	var _path string
	fmt.Fscanln(ws, &_path)

	path, err := url.QueryUnescape(_path)
	if err != nil {
		panic(err)
	}

	// Send dir
	s.writeDirectory(ws, path)

	// Send dir whenever a file is modified
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err)
	}
	defer watcher.Close()

	err = watcher.Add("." + path)
	if err != nil {
		panic(err)
	}

	for {
		select {
		case <-watcher.Events:
			s.writeDirectory(ws, path)
		case err := <-watcher.Errors:
			panic(err)
		}
	}
}

func (s Server) writeDirectory(w io.Writer, path string) {
	file, err := s.dir.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	info, err := file.Stat()
	if err != nil {
		panic(err)
	}

	if !info.IsDir() {
		panic(fmt.Errorf("oh no"))
	}

	files, err := file.Readdir(999) //TODO: 999 is too small
	if err != nil {
		panic(err)
	}

	outputFiles := []File{}
	for _, f := range files {
		outputFiles = append(outputFiles, File{
			Path: f.Name(),
			Size: humanize.IBytes(uint64(f.Size())),
			Dir:  f.IsDir(),
		})
	}

	sort.Sort(ByPath(outputFiles))

	err = json.NewEncoder(w).Encode(outputFiles)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package lazycache

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
	//       kitlog "github.com/go-kit/kit/log"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

const ApiVersion = "v1"

func RegisterDefaultHandlers() {
	http.HandleFunc("/", RootHandler)
	http.Handle("/metrics/", promhttp.Handler())
}

func AddMirror(serverAddr string) {

	url, err := url.Parse(serverAddr)
	fs, err := OpenHttpFS(*url)

	if err != nil {
		panic(fmt.Sprintf("Error opening HTTP FS Source: %s", err.Error()))
	}

	//serverAddr := fmt.Sprintf("%s:%d", config.ServerIp, config.ServerPort)

	// Reverse hostname
	splitHN := MungeHostname(fs.Uri.Host)
	root := fmt.Sprintf("/%s/%s%s", ApiVersion, strings.Join(splitHN, "/"), fs.Uri.Path)
	MakeRootNode(fs, root)
}
<commit_msg>Added brief info/versions endpoint to HTTP server.<commit_after>package lazycache

import (
	"bytes"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	//       kitlog "github.com/go-kit/kit/log"
	"github.com/prometheus/client_golang/prometheus/promhttp"

	"github.com/amarburg/go-lazyfs"
	"github.com/amarburg/go-lazyquicktime"
	"github.com/amarburg/go-quicktime"
)

const ApiVersion = "v1"

const Version = "v0.1.0"

func RegisterDefaultHandlers() {
	http.HandleFunc("/", RootHandler)
	http.Handle("/metrics/", promhttp.Handler())
	http.HandleFunc("/info/", InfoHandler)
}

func AddMirror(serverAddr string) {

	url, err := url.Parse(serverAddr)
	fs, err := OpenHttpFS(*url)

	if err != nil {
		panic(fmt.Sprintf("Error opening HTTP FS Source: %s", err.Error()))
	}

	ofs, err := OpenFileOverlayFS(fs, "/Users/aaron/workspace/go/src/github.com/amarburg/go-lazycache/app/overlay")
	ofs.Flatten = true

	if err != nil {
		panic(fmt.Sprintf("Error opening FileOverlay FS Source: %s", err.Error()))
	}

	// Reverse hostname
	splitHN := MungeHostname(fs.Uri.Host)
	root := fmt.Sprintf("/%s/%s%s", ApiVersion, strings.Join(splitHN, "/"), fs.Uri.Path)
	MakeRootNode(ofs, root)
}

func InfoHandler(w http.ResponseWriter, req *http.Request) {
	b := &bytes.Buffer{}

	// Could this be automatic...?

	fmt.Fprintf(b, "{\n")
	fmt.Fprintf(b, "    \"amarburg/github/go-lazycache\": \"%s\",\n", Version)
	fmt.Fprintf(b, "    \"amarburg/github/go-lazyquicktime\": \"%s\",\n", lazyquicktime.Version)
	fmt.Fprintf(b, "    \"amarburg/github/go-lazyfs\": \"%s\",\n", lazyfs.Version)
	fmt.Fprintf(b, "    \"amarburg/github/go-quicktime\": \"%s\",\n", quicktime.Version)

	fmt.Fprintf(b, "}\n")

	w.Write(b.Bytes())
}
<|endoftext|>


<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>Pushing a broken build 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|>