<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"image"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/PuerkitoBio/throttled"
	"github.com/PuerkitoBio/throttled/store"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/binding"
)

type UploadForm struct {
	PhotoUpload *multipart.FileHeader `form:"photo"`
}

func main() {
	var configFilePath string
	flag.StringVar(&configFilePath, "config", "", "path to a configuration file")
	flag.Parse()

	// Set up logging
	log.SetPrefix("[pixlserv] ")
	log.SetFlags(0) // Remove the timestamp

	// Initialise configuration
	config, err := configInit(configFilePath)
	if err != nil {
		log.Println("Configuration reading failed:", err)
		return
	}
	log.Printf("Running with config: %+v", config)

	// Initialise the cache
	err = cacheInit()
	if err != nil {
		log.Println("Cache initialisation failed:", err)
		return
	}

	// Initialise storage
	err = storageInit(config)
	if err != nil {
		log.Println("Storage initialisation failed:", err)
		return
	}

	// Run the server
	m := martini.Classic()
	if config.throttlingRate > 0 {
		m.Use(throttler(config.throttlingRate))
	}
	m.Get("/image/:parameters/**", transformationHandler)
	m.Post("/upload", binding.MultipartForm(UploadForm{}), uploadHandler)
	go m.Run()

	// Wait for when the program is terminated
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	<-ch

	// Clean up
	cacheCleanUp()
	storageCleanUp()
}

func transformationHandler(params martini.Params) (int, string) {
	var parameters Params
	var err error
	transformationName := parseTransformationName(params["parameters"])
	if transformationName != "" {
		var ok bool
		parameters, ok = config.transformations[transformationName]
		if !ok {
			return http.StatusBadRequest, "Unknown transformation: " + transformationName
		}
	} else if config.allowCustomTransformations {
		parameters, err = parseParameters(params["parameters"])
	} else {
		return http.StatusBadRequest, "Custom transformations not allowed"
	}
	baseImagePath, scale := parseBasePathAndScale(params["_1"])
	if config.allowCustomScale {
		parameters = parameters.WithScale(scale)
	}

	if err != nil {
		return http.StatusBadRequest, err.Error()
	}
	log.Println("Parameters:", parameters)

	// Check if the image with the given parameters already exists
	// and return it
	fullImagePath, _ := createFilePath(baseImagePath, parameters)
	img, format, err := loadFromCache(fullImagePath)
	if err == nil {
		var buffer bytes.Buffer
		writeImage(img, format, &buffer)

		return http.StatusOK, buffer.String()
	}

	// Load the original image and process it
	if !imageExists(baseImagePath) {
		return http.StatusNotFound, "Image not found: " + baseImagePath
	}

	img, format, err = loadImage(baseImagePath)
	if err != nil {
		return http.StatusInternalServerError, err.Error()
	}

	imgNew := transformCropAndResize(img, parameters)

	var buffer bytes.Buffer
	err = writeImage(imgNew, format, &buffer)
	if err != nil {
		log.Println("Writing an image to the response failed:", err)
	}

	// Cache the image asynchronously to speed up the response
	go func() {
		err = addToCache(fullImagePath, imgNew, format)
		if err != nil {
			log.Println("Saving an image to cache failed:", err)
		}
	}()

	return http.StatusOK, buffer.String()
}

func uploadHandler(uf UploadForm) (int, string) {
	file, err := uf.PhotoUpload.Open()
	if err != nil {
		return http.StatusBadRequest, err.Error()
	}

	config := getConfig()

	limit := io.LimitReader(file, int64(config.uploadMaxFileSize+1))
	data, err := ioutil.ReadAll(limit)
	if err != nil {
		return http.StatusBadRequest, err.Error()
	}
	if len(data) > config.uploadMaxFileSize {
		return http.StatusBadRequest, "max file size exceeded"
	}

	img, format, err := image.Decode(bytes.NewReader(data))
	if err != nil {
		return http.StatusBadRequest, err.Error()
	}

	defer file.Close()

	// Not a big fan of .jpeg file extensions
	baseImagePath := fmt.Sprintf("%d.%s", time.Now().Unix(), strings.Replace(format, "jpeg", "jpg", 1))
	log.Printf("Uploading %s", baseImagePath)

	if config.asyncUploads {
		go func() {
			saveImage(img, format, baseImagePath)
		}()
	} else {
		_, err := saveImage(img, format, baseImagePath)
		if err != nil {
			return http.StatusInternalServerError, err.Error()
		}
	}

	// Eager transformations
	if len(config.eagerTransformations) > 0 {
		go func() {
			for _, parameters := range config.eagerTransformations {
				imgNew := transformCropAndResize(img, parameters)
				fullImagePath, _ := createFilePath(baseImagePath, parameters)
				addToCache(fullImagePath, imgNew, format)
			}
		}()
	}

	return http.StatusOK, ""
}

func throttler(perMinRate int) http.Handler {
	t := throttled.RateLimit(throttled.PerMin(perMinRate), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(1000))
	return t.Throttle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Nothing needed here
	}))
}
<commit_msg>Changed the way command line arguments are handled<commit_after>package main

import (
	"bytes"
	"fmt"
	"image"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/PuerkitoBio/throttled"
	"github.com/PuerkitoBio/throttled/store"
	"github.com/codegangsta/cli"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/binding"
)

type UploadForm struct {
	PhotoUpload *multipart.FileHeader `form:"photo"`
}

func main() {
	app := cli.NewApp()
	app.Name = "pixlserv"
	app.Usage = "transform and serve images"
	app.Version = "1.0"
	app.Commands = []cli.Command{
		{
			Name:      "run",
			ShortName: "r",
			Usage:     "runs the server, run [config-file]",
			Action: func(c *cli.Context) {
				// Set up logging
				log.SetPrefix("[pixlserv] ")
				log.SetFlags(0) // Remove the timestamp

				if len(c.Args()) < 1 {
					log.Println("Provide a path to a config file")
					return
				}
				configFilePath := c.Args().First()

				// Initialise configuration
				config, err := configInit(configFilePath)
				if err != nil {
					log.Println("Configuration reading failed:", err)
					return
				}
				log.Printf("Running with config: %+v", config)

				// Initialise the cache
				err = cacheInit()
				if err != nil {
					log.Println("Cache initialisation failed:", err)
					return
				}

				// Initialise storage
				err = storageInit(config)
				if err != nil {
					log.Println("Storage initialisation failed:", err)
					return
				}

				// Run the server
				m := martini.Classic()
				if config.throttlingRate > 0 {
					m.Use(throttler(config.throttlingRate))
				}
				m.Get("/image/:parameters/**", transformationHandler)
				m.Post("/upload", binding.MultipartForm(UploadForm{}), uploadHandler)
				go m.Run()

				// Wait for when the program is terminated
				ch := make(chan os.Signal)
				signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
				<-ch

				// Clean up
				cacheCleanUp()
				storageCleanUp()
			},
		},
	}

	app.Run(os.Args)
}

func transformationHandler(params martini.Params) (int, string) {
	var parameters Params
	var err error
	transformationName := parseTransformationName(params["parameters"])
	if transformationName != "" {
		var ok bool
		parameters, ok = config.transformations[transformationName]
		if !ok {
			return http.StatusBadRequest, "Unknown transformation: " + transformationName
		}
	} else if config.allowCustomTransformations {
		parameters, err = parseParameters(params["parameters"])
	} else {
		return http.StatusBadRequest, "Custom transformations not allowed"
	}
	baseImagePath, scale := parseBasePathAndScale(params["_1"])
	if config.allowCustomScale {
		parameters = parameters.WithScale(scale)
	}

	if err != nil {
		return http.StatusBadRequest, err.Error()
	}
	log.Println("Parameters:", parameters)

	// Check if the image with the given parameters already exists
	// and return it
	fullImagePath, _ := createFilePath(baseImagePath, parameters)
	img, format, err := loadFromCache(fullImagePath)
	if err == nil {
		var buffer bytes.Buffer
		writeImage(img, format, &buffer)

		return http.StatusOK, buffer.String()
	}

	// Load the original image and process it
	if !imageExists(baseImagePath) {
		return http.StatusNotFound, "Image not found: " + baseImagePath
	}

	img, format, err = loadImage(baseImagePath)
	if err != nil {
		return http.StatusInternalServerError, err.Error()
	}

	imgNew := transformCropAndResize(img, parameters)

	var buffer bytes.Buffer
	err = writeImage(imgNew, format, &buffer)
	if err != nil {
		log.Println("Writing an image to the response failed:", err)
	}

	// Cache the image asynchronously to speed up the response
	go func() {
		err = addToCache(fullImagePath, imgNew, format)
		if err != nil {
			log.Println("Saving an image to cache failed:", err)
		}
	}()

	return http.StatusOK, buffer.String()
}

func uploadHandler(uf UploadForm) (int, string) {
	file, err := uf.PhotoUpload.Open()
	if err != nil {
		return http.StatusBadRequest, err.Error()
	}

	config := getConfig()

	limit := io.LimitReader(file, int64(config.uploadMaxFileSize+1))
	data, err := ioutil.ReadAll(limit)
	if err != nil {
		return http.StatusBadRequest, err.Error()
	}
	if len(data) > config.uploadMaxFileSize {
		return http.StatusBadRequest, "max file size exceeded"
	}

	img, format, err := image.Decode(bytes.NewReader(data))
	if err != nil {
		return http.StatusBadRequest, err.Error()
	}

	defer file.Close()

	// Not a big fan of .jpeg file extensions
	baseImagePath := fmt.Sprintf("%d.%s", time.Now().Unix(), strings.Replace(format, "jpeg", "jpg", 1))
	log.Printf("Uploading %s", baseImagePath)

	if config.asyncUploads {
		go func() {
			saveImage(img, format, baseImagePath)
		}()
	} else {
		_, err := saveImage(img, format, baseImagePath)
		if err != nil {
			return http.StatusInternalServerError, err.Error()
		}
	}

	// Eager transformations
	if len(config.eagerTransformations) > 0 {
		go func() {
			for _, parameters := range config.eagerTransformations {
				imgNew := transformCropAndResize(img, parameters)
				fullImagePath, _ := createFilePath(baseImagePath, parameters)
				addToCache(fullImagePath, imgNew, format)
			}
		}()
	}

	return http.StatusOK, ""
}

func throttler(perMinRate int) http.Handler {
	t := throttled.RateLimit(throttled.PerMin(perMinRate), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(1000))
	return t.Throttle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Nothing needed here
	}))
}
<|endoftext|>


<commit_before>package main

import (
    "archive/zip"
    "flag"
    "io"
    "log"
    "net/http"
    "net/url"
    "os"
    "path/filepath"
    "strings"
)

func main() {
    rawUrl := flag.String("url", "", "download zip file")
    flag.Parse()
    log.Println("Downloading and unzipping %s", *rawUrl)

    Download(*rawUrl)
    Unzip("/tmp/downloaded.zip", "/data")

    http.Handle("/", http.FileServer(http.Dir("/data")))

    log.Println("Serving /data on HTTP port 8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func Download(rawUrl string) {
    os.MkdirAll("/tmp", 0755)
    fileName := "/tmp/downloaded.zip"

    file, err := os.Create(fileName)
    if err != nil {
     log.Println(err)
     panic(err)
    }
    defer file.Close()

    check := http.Client{
        CheckRedirect: func(r *http.Request, via []*http.Request) error {
            r.URL.Opaque = r.URL.Path
            return nil
        },
    }

    resp, err := check.Get(rawUrl)
    if err != nil {
        log.Println(err)
        panic(err)
    }
    defer resp.Body.Close()

    size, err := io.Copy(file, resp.Body)
    if err != nil {
        panic(err)
    }
}

func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer func() {
        if err := r.Close(); err != nil {
            panic(err)
        }
    }()

    os.MkdirAll(dest, 0755)

    // Closure to address file descriptors issue with all the deferred .Close() methods
    extractAndWriteFile := func(f *zip.File) error {
        rc, err := f.Open()
        if err != nil {
            return err
        }
        defer func() {
            if err := rc.Close(); err != nil {
                panic(err)
            }
        }()

        path := filepath.Join(dest, f.Name)

        if f.FileInfo().IsDir() {
            os.MkdirAll(path, f.Mode())
        } else {
            f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                return err
            }
            defer func() {
                if err := f.Close(); err != nil {
                    panic(err)
                }
            }()

            _, err = io.Copy(f, rc)
            if err != nil {
                return err
            }
        }
        return nil
    }

    for _, f := range r.File {
        err := extractAndWriteFile(f)
        if err != nil {
            return err
        }
    }

    return nil
}
<commit_msg>removed unused import<commit_after>package main

import (
    "archive/zip"
    "flag"
    "io"
    "log"
    "net/http"
    "os"
    "path/filepath"
)

func main() {
    rawUrl := flag.String("url", "", "download zip file")
    flag.Parse()
    log.Println("Downloading and unzipping %s", *rawUrl)

    Download(*rawUrl)
    Unzip("/tmp/downloaded.zip", "/data")

    http.Handle("/", http.FileServer(http.Dir("/data")))

    log.Println("Serving /data on HTTP port 8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func Download(rawUrl string) {
    os.MkdirAll("/tmp", 0755)
    fileName := "/tmp/downloaded.zip"

    file, err := os.Create(fileName)
    if err != nil {
     log.Println(err)
     panic(err)
    }
    defer file.Close()

    check := http.Client{
        CheckRedirect: func(r *http.Request, via []*http.Request) error {
            r.URL.Opaque = r.URL.Path
            return nil
        },
    }

    resp, err := check.Get(rawUrl)
    if err != nil {
        log.Println(err)
        panic(err)
    }
    defer resp.Body.Close()

    size, err := io.Copy(file, resp.Body)
    if err != nil {
        panic(err)
    }
}

func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer func() {
        if err := r.Close(); err != nil {
            panic(err)
        }
    }()

    os.MkdirAll(dest, 0755)

    // Closure to address file descriptors issue with all the deferred .Close() methods
    extractAndWriteFile := func(f *zip.File) error {
        rc, err := f.Open()
        if err != nil {
            return err
        }
        defer func() {
            if err := rc.Close(); err != nil {
                panic(err)
            }
        }()

        path := filepath.Join(dest, f.Name)

        if f.FileInfo().IsDir() {
            os.MkdirAll(path, f.Mode())
        } else {
            f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                return err
            }
            defer func() {
                if err := f.Close(); err != nil {
                    panic(err)
                }
            }()

            _, err = io.Copy(f, rc)
            if err != nil {
                return err
            }
        }
        return nil
    }

    for _, f := range r.File {
        err := extractAndWriteFile(f)
        if err != nil {
            return err
        }
    }

    return nil
}
<|endoftext|>


<commit_before>package dhcp

import (
	"errors"
	"net"
	"time"

	log "github.com/Sirupsen/logrus"
	dhcp "github.com/krolaw/dhcp4"
	"github.com/mistifyio/mistify-agent/client"
)

var NotFound = errors.New("not found")

type Server struct {
	client *client.Client
	config *Config
}

func NewServer(conf *Config) *Server {
	server := &Server{}
	server.config = conf

	c, _ := client.NewClient(&client.Config{Address: conf.Agent})
	server.client = c

	return server
}

func (s *Server) Run() {
	log.WithFields(log.Fields{
		"agent_address": s.client.Config.Address,
	}).Info("Starting DHCP server")

	conn, err := NewDHCPConnection(s.config.Interfaces)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
			"func":  "dhcp.NewDHCPConnection",
		}).Fatal(err)
	}

	err = dhcp.Serve(conn, s)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
			"func":  "dhcp.Serve",
		}).Fatal(err)
	}
}

func (s *Server) ServeDHCP(packet dhcp.Packet, msgType dhcp.MessageType, options dhcp.Options) dhcp.Packet {
	var replyType dhcp.MessageType
	var logMessage string

	switch msgType {
	case dhcp.Discover:
		replyType = dhcp.Offer
		logMessage = "Discover"
	case dhcp.Request:
		replyType = dhcp.ACK
		logMessage = "Request"
	default:
		return nil
	}

	mac := packet.CHAddr().String()

	log.WithFields(log.Fields{
		"mac": mac,
	}).Info("dhcp." + logMessage)

	nic, err := s.getNic(mac)
	if err != nil {
		log.WithFields(log.Fields{
			"mac":   mac,
			"error": err,
			"func":  "dhcp.Server.getNic",
		}).Error("Couldn't get NIC for MAC address")
		return nil
	}

	log.WithFields(log.Fields{
		"mac": mac,
		"ip":  nic.Address,
	}).Info("Returning IP for MAC address")

	replyOpts := dhcp.Options{
		dhcp.OptionRouter:           net.ParseIP(nic.Gateway).To4(),
		dhcp.OptionDomainNameServer: net.IP{8, 8, 8, 8},
		dhcp.OptionSubnetMask:       net.ParseIP(nic.Netmask).To4(),
	}

	reply := dhcp.ReplyPacket(packet, replyType, net.IPv4zero, net.ParseIP(nic.Address).To4(), time.Hour*24*7, replyOpts.SelectOrderOrAll(replyOpts[dhcp.OptionParameterRequestList]))
	return reply
}

func (server *Server) getNic(mac string) (*client.Nic, error) {
	guests, err := server.client.ListGuests()
	if err != nil {
		return nil, err
	}

	for _, guest := range guests {
		for _, nic := range guest.Nics {
			if nic.Mac == mac {
				return &nic, nil
			}
		}
	}
	return nil, NotFound
}
<commit_msg>[MIST-359] Send the message type in fields<commit_after>package dhcp

import (
	"errors"
	"net"
	"time"

	log "github.com/Sirupsen/logrus"
	dhcp "github.com/krolaw/dhcp4"
	"github.com/mistifyio/mistify-agent/client"
)

var NotFound = errors.New("not found")

type Server struct {
	client *client.Client
	config *Config
}

func NewServer(conf *Config) *Server {
	server := &Server{}
	server.config = conf

	c, _ := client.NewClient(&client.Config{Address: conf.Agent})
	server.client = c

	return server
}

func (s *Server) Run() {
	log.WithFields(log.Fields{
		"agent_address": s.client.Config.Address,
	}).Info("Starting DHCP server")

	conn, err := NewDHCPConnection(s.config.Interfaces)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
			"func":  "dhcp.NewDHCPConnection",
		}).Fatal(err)
	}

	err = dhcp.Serve(conn, s)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
			"func":  "dhcp.Serve",
		}).Fatal(err)
	}
}

func (s *Server) ServeDHCP(packet dhcp.Packet, msgType dhcp.MessageType, options dhcp.Options) dhcp.Packet {
	var replyType dhcp.MessageType
	var logType string

	switch msgType {
	case dhcp.Discover:
		replyType = dhcp.Offer
		logType = "Discover"
	case dhcp.Request:
		replyType = dhcp.ACK
		logType = "Request"
	default:
		return nil
	}

	mac := packet.CHAddr().String()

	log.WithFields(log.Fields{
		"mac":     mac,
		"msgType": logType,
	}).Info("Message received")

	nic, err := s.getNic(mac)
	if err != nil {
		log.WithFields(log.Fields{
			"mac":   mac,
			"error": err,
			"func":  "dhcp.Server.getNic",
		}).Error("Couldn't get NIC for MAC address")
		return nil
	}

	log.WithFields(log.Fields{
		"mac": mac,
		"ip":  nic.Address,
	}).Info("Returning IP for MAC address")

	replyOpts := dhcp.Options{
		dhcp.OptionRouter:           net.ParseIP(nic.Gateway).To4(),
		dhcp.OptionDomainNameServer: net.IP{8, 8, 8, 8},
		dhcp.OptionSubnetMask:       net.ParseIP(nic.Netmask).To4(),
	}

	reply := dhcp.ReplyPacket(packet, replyType, net.IPv4zero, net.ParseIP(nic.Address).To4(), time.Hour*24*7, replyOpts.SelectOrderOrAll(replyOpts[dhcp.OptionParameterRequestList]))
	return reply
}

func (server *Server) getNic(mac string) (*client.Nic, error) {
	guests, err := server.client.ListGuests()
	if err != nil {
		return nil, err
	}

	for _, guest := range guests {
		for _, nic := range guest.Nics {
			if nic.Mac == mac {
				return &nic, nil
			}
		}
	}
	return nil, NotFound
}
<|endoftext|>


<commit_before>package main

import (
        "code.google.com/p/go.net/websocket"
        "net"
        "net/http"
        "log"
	"encoding/json"
	"fmt"
	"strings"
	"text/template"
	"./uuid"
)

const (
	HOST_NAME = "localhost"
	PORT_NUMBER = "8080"
	APPSERVER_API_PREFIX = "/notify/"
)

type Client struct {
	Websocket *websocket.Conn
	UAID string
	Ip   string
	Port float64
}

type Channel struct {
	uaid string

	ChannelID string `json:"channelID"`
	Version string   `json:"version"`
}

// Mapping from a UAID to the Client object
var gConnectedClients map[string]*Client;

// Mapping from a UAID to all channels owned by that UAID
var gUAIDToChannel map[string][]*Channel;

// Mapping from a ChannelID to the cooresponding Channel
var gChannelIDToChannel map[string] *Channel;

func handleRegister(client *Client, f map[string]interface{}) {
	log.Println(" -> handleRegister");

	if (f["channelID"] == nil) {
		log.Println("channelID is missing!");
		return;
	}

	var channelID = f["channelID"].(string);

	// TODO https!
	var pushEndpoint = "http://" + HOST_NAME + ":" + PORT_NUMBER + APPSERVER_API_PREFIX + channelID;

	channel := &Channel{client.UAID, channelID, ""};

	gUAIDToChannel[client.UAID] = append(gUAIDToChannel[client.UAID], channel);
	gChannelIDToChannel[channelID] = channel;

	type RegisterResponse struct {
		Name string          `json:"messageType"`
		Status int           `json:"status"`
		PushEndpoint string  `json:"pushEndpoint"`
		ChannelID string     `json:"channelID"`
	}

	register := RegisterResponse{"register", 200, pushEndpoint, channelID}

	j, err := json.Marshal(register);
	if err != nil {
		log.Println("Could not convert hello response to json %s",err)
		return;
        }

	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		// we could not send the message to a peer
		log.Println("Could not send message to ", client.Websocket, err.Error())
	}
}

func handleHello(client *Client, f map[string]interface{}) {
	log.Println(" -> handleHello");

	status := 200;

	if (f["uaid"] == nil) {
		uaid, err := uuid.GenUUID()
		if  err != nil {
			status = 400;
			log.Println("GenUUID error %s",err)
		}
		client.UAID = uaid;
	} else {
		client.UAID = f["uaid"].(string)
	}

	if (f["interface"] != nil) {
		m := f["interface"].(map[string]interface{})
		client.Ip = m["ip"].(string);
		client.Port = m["port"].(float64);
	}

	type HelloResponse struct {
		Name string          `json:"messageType"`
		Status int           `json:"status"`
		UAID string          `json:"uaid"`
		Channels []*Channel  `json:"channelIDs"`
	}

	hello := HelloResponse{"hello", status, client.UAID, gUAIDToChannel[client.UAID]}

	j, err := json.Marshal(hello);
	if err != nil {
		log.Println("Could not convert hello response to json %s",err)
		return;
        }

	log.Println("going to send:  \n  ", string(j));
	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		log.Println("Could not send message to ", client.Websocket, err.Error())
	}
}

func handleAck(client *Client, f map[string]interface{}) {
	log.Println(" -> ack");
}

func pushHandler(ws *websocket.Conn) {

	log.Println("a");

	client := &Client{ws, "", "", 0}

	for {
		var f map[string]interface{}

		var err error
		if err = websocket.JSON.Receive(ws, &f); err != nil {
			log.Println("Websocket Disconnected.", err.Error())
			break;
		}

		log.Println("hi!");

		switch f["messageType"] {
		case "hello":
			handleHello(client, f);
			gConnectedClients[client.UAID] = client;
			break;

		case "register":
			handleRegister(client, f);
			break;

		case "ack":
			handleAck(client, f);
			break;
		default:
			log.Println(" -> Unknown", f);
			break;
		}
	}
	log.Println("Closing Websocket!");
	ws.Close();
	gConnectedClients[client.UAID].Websocket = nil;
}

func notifyHandler(w http.ResponseWriter, r *http.Request) {

	if r.Method != "PUT" {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Method must be PUT."))
		return
	}

	channelID := strings.Trim(r.URL.Path, APPSERVER_API_PREFIX)

	if (strings.Contains(channelID, "/") || len(channelID) != (32+4)) {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Could not find a valid channelID."))
		return;
	}

	if err := r.ParseForm(); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Could not find a valid version."))
		return;
	}

	values := r.Form["version"]

	if (len(values) != 1) {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Could not find one version."))
		return;
	}

	channel := gChannelIDToChannel[channelID];
	channel.Version = values[0];

	client := gConnectedClients[channel.uaid];
	if (client == nil) {
		log.Println("no known client for the channel.");
	} else if (client.Websocket == nil) {
		wakeupClient(client)
	} else {
		sendNotificationToClient(client, channel)
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte("OK"))
}

func wakeupClient(client *Client) {

	log.Println("wakeupClient: ", client);
	service := fmt.Sprintf("%s:%g", client.Ip, client.Port)

	udpAddr, err := net.ResolveUDPAddr("up4", service)
	if err != nil {
		log.Println("ResolveUDPAddr error ", err.Error())
		return
	}

	conn, err := net.DialUDP("udp", nil, udpAddr)
	if err != nil {
		log.Println("DialUDP error ", err.Error())
		return
	}

	_, err = conn.Write([]byte("anything"))
	if err != nil {
		log.Println("UDP Write error ", err.Error())
		return
	}

}

func sendNotificationToClient(client *Client, channel *Channel)  {

	type NotificationResponse struct {
		Name string          `json:"messageType"`
		Channels []Channel   `json:"updates"`
	}

	var channels []Channel;
	channels = append(channels, *channel);

	notification := NotificationResponse{"notification",  channels}

	j, err := json.Marshal(notification);
	if err != nil {
		log.Println("Could not convert hello response to json %s",err)
		return;
        }

	log.Println("going to send:  \n  ", string(j));
	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		log.Println("Could not send message to ", channel, err.Error())
	}

}

func admin(w http.ResponseWriter, r *http.Request) {

	type User struct {
		UAID string
		Connected bool
		Channels []*Channel
	}

	type Arguments struct {
		PushEndpointPrefix   string
		Users []User
	}

// TODO https!
	arguments := Arguments{"http://" + HOST_NAME + ":" + PORT_NUMBER + APPSERVER_API_PREFIX, nil}

	for k := range gUAIDToChannel {
		connected := gConnectedClients[k] != nil;
		u := User{k, connected, gUAIDToChannel[k]};
		arguments.Users = append(arguments.Users, u);
	}

	t := template.New("users.template")
	s1, _ := t.ParseFiles("templates/users.template");
	s1.Execute(w, arguments)
}

func main() {

	gUAIDToChannel = make(map[string][]*Channel)
	gChannelIDToChannel = make(map[string]*Channel)

	gConnectedClients = make(map[string]*Client)

	http.Handle("/", http.FileServer(http.Dir(".")))

	http.HandleFunc("/admin", admin)

	http.Handle("/push", websocket.Handler(pushHandler))

	http.HandleFunc(APPSERVER_API_PREFIX, notifyHandler);

	log.Println("Listening on", HOST_NAME + ":" + PORT_NUMBER );
	log.Fatal(http.ListenAndServe(HOST_NAME  + ":" + PORT_NUMBER , nil))
}

<commit_msg>Make websocket handle at root<commit_after>package main

import (
        "code.google.com/p/go.net/websocket"
        "net"
        "net/http"
        "log"
	"encoding/json"
	"fmt"
	"strings"
	"text/template"
	"./uuid"
)

const (
	HOST_NAME = "localhost"
	PORT_NUMBER = "8080"
	APPSERVER_API_PREFIX = "/notify/"
)

type Client struct {
	Websocket *websocket.Conn
	UAID string
	Ip   string
	Port float64
}

type Channel struct {
	uaid string

	ChannelID string `json:"channelID"`
	Version string   `json:"version"`
}

// Mapping from a UAID to the Client object
var gConnectedClients map[string]*Client;

// Mapping from a UAID to all channels owned by that UAID
var gUAIDToChannel map[string][]*Channel;

// Mapping from a ChannelID to the cooresponding Channel
var gChannelIDToChannel map[string] *Channel;

func handleRegister(client *Client, f map[string]interface{}) {
	log.Println(" -> handleRegister");

	if (f["channelID"] == nil) {
		log.Println("channelID is missing!");
		return;
	}

	var channelID = f["channelID"].(string);

	// TODO https!
	var pushEndpoint = "http://" + HOST_NAME + ":" + PORT_NUMBER + APPSERVER_API_PREFIX + channelID;

	channel := &Channel{client.UAID, channelID, ""};

	gUAIDToChannel[client.UAID] = append(gUAIDToChannel[client.UAID], channel);
	gChannelIDToChannel[channelID] = channel;

	type RegisterResponse struct {
		Name string          `json:"messageType"`
		Status int           `json:"status"`
		PushEndpoint string  `json:"pushEndpoint"`
		ChannelID string     `json:"channelID"`
	}

	register := RegisterResponse{"register", 200, pushEndpoint, channelID}

	j, err := json.Marshal(register);
	if err != nil {
		log.Println("Could not convert hello response to json %s",err)
		return;
        }

	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		// we could not send the message to a peer
		log.Println("Could not send message to ", client.Websocket, err.Error())
	}
}

func handleHello(client *Client, f map[string]interface{}) {
	log.Println(" -> handleHello");

	status := 200;

	if (f["uaid"] == nil) {
		uaid, err := uuid.GenUUID()
		if  err != nil {
			status = 400;
			log.Println("GenUUID error %s",err)
		}
		client.UAID = uaid;
	} else {
		client.UAID = f["uaid"].(string)
	}

	if (f["interface"] != nil) {
		m := f["interface"].(map[string]interface{})
		client.Ip = m["ip"].(string);
		client.Port = m["port"].(float64);
	}

	type HelloResponse struct {
		Name string          `json:"messageType"`
		Status int           `json:"status"`
		UAID string          `json:"uaid"`
		Channels []*Channel  `json:"channelIDs"`
	}

	hello := HelloResponse{"hello", status, client.UAID, gUAIDToChannel[client.UAID]}

	j, err := json.Marshal(hello);
	if err != nil {
		log.Println("Could not convert hello response to json %s",err)
		return;
        }

	log.Println("going to send:  \n  ", string(j));
	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		log.Println("Could not send message to ", client.Websocket, err.Error())
	}
}

func handleAck(client *Client, f map[string]interface{}) {
	log.Println(" -> ack");
}

func pushHandler(ws *websocket.Conn) {

    log.Println("pushHandler: new websocket connection");

	client := &Client{ws, "", "", 0}

	for {
		var f map[string]interface{}

		var err error
		if err = websocket.JSON.Receive(ws, &f); err != nil {
			log.Println("Websocket Disconnected.", err.Error())
			break;
		}

		log.Println("hi!");

		switch f["messageType"] {
		case "hello":
			handleHello(client, f);
			gConnectedClients[client.UAID] = client;
			break;

		case "register":
			handleRegister(client, f);
			break;

		case "ack":
			handleAck(client, f);
			break;
		default:
			log.Println(" -> Unknown", f);
			break;
		}
	}
	log.Println("Closing Websocket!");
	ws.Close();
	gConnectedClients[client.UAID].Websocket = nil;
}

func notifyHandler(w http.ResponseWriter, r *http.Request) {

	if r.Method != "PUT" {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Method must be PUT."))
		return
	}

	channelID := strings.Trim(r.URL.Path, APPSERVER_API_PREFIX)

	if (strings.Contains(channelID, "/") || len(channelID) != (32+4)) {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Could not find a valid channelID."))
		return;
	}

	if err := r.ParseForm(); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Could not find a valid version."))
		return;
	}

	values := r.Form["version"]

	if (len(values) != 1) {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("Could not find one version."))
		return;
	}

	channel := gChannelIDToChannel[channelID];
	channel.Version = values[0];

	client := gConnectedClients[channel.uaid];
	if (client == nil) {
		log.Println("no known client for the channel.");
	} else if (client.Websocket == nil) {
		wakeupClient(client)
	} else {
		sendNotificationToClient(client, channel)
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte("OK"))
}

func wakeupClient(client *Client) {

	log.Println("wakeupClient: ", client);
	service := fmt.Sprintf("%s:%g", client.Ip, client.Port)

	udpAddr, err := net.ResolveUDPAddr("up4", service)
	if err != nil {
		log.Println("ResolveUDPAddr error ", err.Error())
		return
	}

	conn, err := net.DialUDP("udp", nil, udpAddr)
	if err != nil {
		log.Println("DialUDP error ", err.Error())
		return
	}

	_, err = conn.Write([]byte("anything"))
	if err != nil {
		log.Println("UDP Write error ", err.Error())
		return
	}

}

func sendNotificationToClient(client *Client, channel *Channel)  {

	type NotificationResponse struct {
		Name string          `json:"messageType"`
		Channels []Channel   `json:"updates"`
	}

	var channels []Channel;
	channels = append(channels, *channel);

	notification := NotificationResponse{"notification",  channels}

	j, err := json.Marshal(notification);
	if err != nil {
		log.Println("Could not convert hello response to json %s",err)
		return;
        }

	log.Println("going to send:  \n  ", string(j));
	if err = websocket.Message.Send(client.Websocket, string(j)); err != nil {
		log.Println("Could not send message to ", channel, err.Error())
	}

}

func admin(w http.ResponseWriter, r *http.Request) {

	type User struct {
		UAID string
		Connected bool
		Channels []*Channel
	}

	type Arguments struct {
		PushEndpointPrefix   string
		Users []User
	}

// TODO https!
	arguments := Arguments{"http://" + HOST_NAME + ":" + PORT_NUMBER + APPSERVER_API_PREFIX, nil}

	for k := range gUAIDToChannel {
		connected := gConnectedClients[k] != nil;
		u := User{k, connected, gUAIDToChannel[k]};
		arguments.Users = append(arguments.Users, u);
	}

	t := template.New("users.template")
	s1, _ := t.ParseFiles("templates/users.template");
	s1.Execute(w, arguments)
}

func main() {

	gUAIDToChannel = make(map[string][]*Channel)
	gChannelIDToChannel = make(map[string]*Channel)

	gConnectedClients = make(map[string]*Client)

	//http.Handle("/", http.FileServer(http.Dir(".")))

	http.HandleFunc("/admin", admin)

	http.Handle("/", websocket.Handler(pushHandler))

	http.HandleFunc(APPSERVER_API_PREFIX, notifyHandler);

	log.Println("Listening on", HOST_NAME + ":" + PORT_NUMBER );
	log.Fatal(http.ListenAndServe(HOST_NAME  + ":" + PORT_NUMBER , nil))
}

<|endoftext|>


<commit_before>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

package com

import (
	"errors"
	"log"
	"os"
	"path/filepath"
	"sync"

	"github.com/admpub/fsnotify"
)

var (
	DefaultMonitor      = NewMonitor()
	MonitorEventEmptyFn = func(string) {}
)

func NewMonitor() *MonitorEvent {
	return &MonitorEvent{
		Create:  MonitorEventEmptyFn,
		Delete:  MonitorEventEmptyFn,
		Modify:  MonitorEventEmptyFn,
		Chmod:   MonitorEventEmptyFn,
		Rename:  MonitorEventEmptyFn,
		filters: []func(string) bool{},
	}
}

//MonitorEvent 监控事件函数
type MonitorEvent struct {
	//文件事件
	Create func(string) //创建
	Delete func(string) //删除（包含文件夹和文件。因为已经删除，无法确定是文件夹还是文件）
	Modify func(string) //修改（包含修改权限。如果是文件夹，则内部的文件被更改也会触发此事件）
	Chmod  func(string) //修改权限（windows不支持）
	Rename func(string) //重命名

	//其它
	Channel chan bool //管道
	Debug   bool
	watcher *fsnotify.Watcher
	filters []func(string) bool
	lock    sync.RWMutex
}

func (m *MonitorEvent) AddFilter(args ...func(string) bool) *MonitorEvent {
	if m.filters == nil {
		m.filters = []func(string) bool{}
	}
	m.filters = append(m.filters, args...)
	return m
}

func (m *MonitorEvent) SetFilters(args ...func(string) bool) *MonitorEvent {
	m.filters = args
	return m
}

func (m *MonitorEvent) Watch(args ...func(string) bool) *MonitorEvent {
	m.SetFilters(args...)
	go func() {
		m.backendListen()
		<-m.Channel
	}()
	return m
}

func (m *MonitorEvent) Close() error {
	if m.Channel != nil {
		close(m.Channel)
	}
	if m.watcher != nil {
		return m.watcher.Close()
	}
	return nil
}

func (m *MonitorEvent) Watcher() *fsnotify.Watcher {
	m.lock.Lock()
	defer m.lock.Unlock()
	if m.watcher == nil {
		var err error
		m.watcher, err = fsnotify.NewWatcher()
		m.Channel = make(chan bool)
		m.filters = []func(string) bool{}
		if err != nil {
			log.Panic(err)
		}
	}
	return m.watcher
}

func (m *MonitorEvent) backendListen() *MonitorEvent {
	go m.listen()
	return m
}

func (m *MonitorEvent) AddDir(dir string) error {
	f, err := os.Stat(dir)
	if err != nil {
		return err
	}
	if !f.IsDir() {
		return errors.New(dir + ` is not dir.`)
	}
	err = filepath.Walk(dir, func(f string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			if m.Debug {
				log.Println(`[Monitor]`, `Add Watch:`, f)
			}
			return m.Watcher().Add(f)
		}
		return nil
	})
	return err
}

func (m *MonitorEvent) AddFile(file string) error {
	return m.Watcher().Add(file)
}

func (m *MonitorEvent) Remove(fileOrDir string) error {
	if m.watcher != nil {
		return m.watcher.Remove(fileOrDir)
	}
	return nil
}

func (m *MonitorEvent) listen() {
	for {
		watcher := m.Watcher()
		select {
		case ev, ok := <-watcher.Events:
			if !ok {
				return
			}
			if m.Debug {
				log.Println(`[Monitor]`, `Trigger Event:`, ev)
			}
			if m.filters != nil {
				var skip bool
				for _, filter := range m.filters {
					if !filter(ev.Name) {
						skip = true
						break
					}
				}
				if skip {
					break
				}
			}
			switch {
			case ev.Op&fsnotify.Create == fsnotify.Create:
				if m.IsDir(ev.Name) {
					watcher.Add(ev.Name)
				}
				if m.Create != nil {
					m.Create(ev.Name)
				}
			case ev.Op&fsnotify.Remove == fsnotify.Remove:
				if m.IsDir(ev.Name) {
					watcher.Remove(ev.Name)
				}
				if m.Delete != nil {
					m.Delete(ev.Name)
				}
			case ev.Op&fsnotify.Write == fsnotify.Write:
				if m.Modify != nil {
					m.Modify(ev.Name)
				}
			case ev.Op&fsnotify.Rename == fsnotify.Rename:
				watcher.Remove(ev.Name)
				if m.Rename != nil {
					m.Rename(ev.Name)
				}
			case ev.Op&fsnotify.Chmod == fsnotify.Chmod:
				if m.Chmod != nil {
					m.Chmod(ev.Name)
				}
			}
		case err, ok := <-watcher.Errors:
			if !ok {
				return
			}
			if err != nil {
				log.Println("Watcher error:", err)
			}
		}
	}
}

func (m *MonitorEvent) IsDir(path string) bool {
	d, e := os.Stat(path)
	if e != nil {
		return false
	}
	return d.IsDir()
}

//Monitor 文件监测
func Monitor(rootDir string, callback *MonitorEvent, args ...func(string) bool) error {
	watcher := callback.Watcher()
	defer watcher.Close()
	callback.Watch(args...)
	err := callback.AddDir(rootDir)
	if err != nil {
		callback.Close()
		return err
	}
	return nil
}
<commit_msg>update<commit_after>/*

   Copyright 2016 Wenhui Shen <www.webx.top>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

package com

import (
	"errors"
	"log"
	"os"
	"path/filepath"
	"sync"

	"github.com/admpub/fsnotify"
)

var (
	DefaultMonitor      = NewMonitor()
	MonitorEventEmptyFn = func(string) {}
)

func NewMonitor() *MonitorEvent {
	return &MonitorEvent{
		Create:  MonitorEventEmptyFn,
		Delete:  MonitorEventEmptyFn,
		Modify:  MonitorEventEmptyFn,
		Chmod:   MonitorEventEmptyFn,
		Rename:  MonitorEventEmptyFn,
		filters: []func(string) bool{},
	}
}

//MonitorEvent 监控事件函数
type MonitorEvent struct {
	//文件事件
	Create func(string) //创建
	Delete func(string) //删除（包含文件夹和文件。因为已经删除，无法确定是文件夹还是文件）
	Modify func(string) //修改（包含修改权限。如果是文件夹，则内部的文件被更改也会触发此事件）
	Chmod  func(string) //修改权限（windows不支持）
	Rename func(string) //重命名

	//其它
	Channel chan bool //管道
	Debug   bool
	watcher *fsnotify.Watcher
	filters []func(string) bool
	lock    sync.RWMutex
}

func (m *MonitorEvent) AddFilter(args ...func(string) bool) *MonitorEvent {
	if m.filters == nil {
		m.filters = []func(string) bool{}
	}
	m.filters = append(m.filters, args...)
	return m
}

func (m *MonitorEvent) SetFilters(args ...func(string) bool) *MonitorEvent {
	m.filters = args
	return m
}

func (m *MonitorEvent) Watch(args ...func(string) bool) *MonitorEvent {
	m.SetFilters(args...)
	go func() {
		m.backendListen()
		<-m.Channel
	}()
	return m
}

func (m *MonitorEvent) Close() error {
	if m.Channel != nil {
		close(m.Channel)
	}
	if m.watcher != nil {
		return m.watcher.Close()
	}
	return nil
}

func (m *MonitorEvent) Watcher() *fsnotify.Watcher {
	m.lock.Lock()
	defer m.lock.Unlock()
	if m.watcher == nil {
		var err error
		m.watcher, err = fsnotify.NewWatcher()
		m.Channel = make(chan bool)
		m.filters = []func(string) bool{}
		if err != nil {
			log.Panic(err)
		}
	}
	return m.watcher
}

func (m *MonitorEvent) backendListen() *MonitorEvent {
	go m.listen()
	return m
}

func (m *MonitorEvent) AddDir(dir string) error {
	f, err := os.Stat(dir)
	if err != nil {
		return err
	}
	if !f.IsDir() {
		return errors.New(dir + ` is not dir.`)
	}
	err = filepath.Walk(dir, func(f string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			if m.Debug {
				log.Println(`[Monitor]`, `Add Watch:`, f)
			}
			return m.Watcher().Add(f)
		}
		return nil
	})
	return err
}

func (m *MonitorEvent) AddFile(file string) error {
	if m.Debug {
		log.Println(`[Monitor]`, `Add Watch:`, file)
	}
	return m.Watcher().Add(file)
}

func (m *MonitorEvent) Remove(fileOrDir string) error {
	if m.watcher != nil {
		return m.watcher.Remove(fileOrDir)
	}
	return nil
}

func (m *MonitorEvent) listen() {
	for {
		watcher := m.Watcher()
		select {
		case ev, ok := <-watcher.Events:
			if !ok {
				return
			}
			if m.Debug {
				log.Println(`[Monitor]`, `Trigger Event:`, ev)
			}
			if m.filters != nil {
				var skip bool
				for _, filter := range m.filters {
					if !filter(ev.Name) {
						skip = true
						break
					}
				}
				if skip {
					break
				}
			}
			switch {
			case ev.Op&fsnotify.Create == fsnotify.Create:
				if m.IsDir(ev.Name) {
					watcher.Add(ev.Name)
				}
				if m.Create != nil {
					m.Create(ev.Name)
				}
			case ev.Op&fsnotify.Remove == fsnotify.Remove:
				if m.IsDir(ev.Name) {
					watcher.Remove(ev.Name)
				}
				if m.Delete != nil {
					m.Delete(ev.Name)
				}
			case ev.Op&fsnotify.Write == fsnotify.Write:
				if m.Modify != nil {
					m.Modify(ev.Name)
				}
			case ev.Op&fsnotify.Rename == fsnotify.Rename:
				watcher.Remove(ev.Name)
				if m.Rename != nil {
					m.Rename(ev.Name)
				}
			case ev.Op&fsnotify.Chmod == fsnotify.Chmod:
				if m.Chmod != nil {
					m.Chmod(ev.Name)
				}
			}
		case err, ok := <-watcher.Errors:
			if !ok {
				return
			}
			if err != nil {
				log.Println("Watcher error:", err)
			}
		}
	}
}

func (m *MonitorEvent) IsDir(path string) bool {
	d, e := os.Stat(path)
	if e != nil {
		return false
	}
	return d.IsDir()
}

//Monitor 文件监测
func Monitor(rootDir string, callback *MonitorEvent, args ...func(string) bool) error {
	watcher := callback.Watcher()
	defer watcher.Close()
	callback.Watch(args...)
	err := callback.AddDir(rootDir)
	if err != nil {
		callback.Close()
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Package monitor allows to print counters information into stdout, printing a
// row each second with the status of all Field objects and a title row each ten
// seconds with titles given to each Field objects.
//
// Fields column order is set by creation order.
//
// Fields can be added in realtime after a Start() signal is given. In this case
// a new row with titles will be printed
//
// Field titles must be longest than longest value that will be displayed as
// titles they are not adapted yet
package monitor

import (
	"fmt"
	"strconv"
	"time"
)

// Field struct gives control over information printed for this field on each
// row
type Field struct {
	title string
	value int
	reset bool
	wait  chan int
}

// Add adds given value to Field value
func (f *Field) Add(number int) {
	f.wait <- 1
	f.value += number
	<-f.wait
}

// Set sets Field value to given number
func (f *Field) Set(number int) {
	f.wait <- 1
	f.value = number
	<-f.wait
}

var fields = []*Field{}
var previousFieldCount int
var stop = make(chan int)

// Start gives the order to start printing information on stdout
func Start() {
	go func() {
		tick := time.NewTicker(time.Second).C
		count := 0
		for {
			select {
			case <-tick:
				if count%10 == 0 || previousFieldCount != len(fields) {
					printTitles()
				}
				previousFieldCount = len(fields)

				printValues()
				count++

			case <-stop:
				break
			}
		}
	}()
}

// Stop gives the order to stop printing information on stdout
func Stop() {
	stop <- 1
}

func printTitles() {
	first := true
	for _, field := range fields {
		if !first {
			fmt.Printf(" ")
		}
		fmt.Printf("%v", field.title)
		first = false
	}
	fmt.Println()
}

func printValues() {
	first := true
	for _, field := range fields {
		field.wait <- 1
		s := strconv.Itoa(field.value)
		if !first {
			fmt.Printf(" ")
		}
		fmt.Printf("%[2]*[1]v", s, len(field.title))
		if field.reset {
			field.value = 0
		}
		first = false
		<-field.wait
	}
	fmt.Println()
}

// NewField creates a new monitor field that is shown as a column each second
//
// - title is the title printed on titles row each ten seconds
//
// - reset tells to reset field value after each time its printed
func NewField(title string, reset bool) *Field {
	field := new(Field)
	field.title = title
	field.reset = reset
	field.wait = make(chan int, 1)
	fields = append(fields, field)
	return field
}
<commit_msg>go-monitor now prints accumulations each 10 secs<commit_after>// Package monitor allows to print counters information into stdout, printing a
// row each second with the status of all Field objects and a title row each ten
// seconds with titles given to each Field objects.
//
// Fields column order is set by creation order.
//
// Fields can be added in realtime after a Start() signal is given. In this case
// a new row with titles will be printed
//
// Field titles must be longest than longest value that will be displayed as
// titles they are not adapted yet
package monitor

import (
	"fmt"
	"strconv"
	"time"
)

// Field struct gives control over information printed for this field on each
// row
type Field struct {
	title string
	value int
	accu  int
	reset bool
	wait  chan int
}

// Add adds given value to Field value
func (f *Field) Add(number int) {
	f.wait <- 1
	f.value += number
	<-f.wait
}

// Set sets Field value to given number
func (f *Field) Set(number int) {
	f.wait <- 1
	f.value = number
	<-f.wait
}

var fields = []*Field{}
var previousFieldCount int
var stop = make(chan int)

// Start gives the order to start printing information on stdout
func Start() {
	go func() {
		tick := time.NewTicker(time.Second).C
		count := 0
		for {
			select {
			case <-tick:
				if count%10 == 0 || previousFieldCount != len(fields) {
					printTitles()
				}
				previousFieldCount = len(fields)

				printValues()
				count++
				if count%10 == 0 || previousFieldCount != len(fields) {
					printAccu()
				}

			case <-stop:
				break
			}
		}
	}()
}

// Stop gives the order to stop printing information on stdout
func Stop() {
	stop <- 1
}

func printTitles() {
	fmt.Println()
	first := true
	for _, field := range fields {
		if !first {
			fmt.Printf(" ")
		}
		fmt.Printf("%v", field.title)
		first = false
	}
	fmt.Println()
}

func printValues() {
	first := true
	for _, field := range fields {
		field.wait <- 1
		s := strconv.Itoa(field.value)
		if !first {
			fmt.Printf(" ")
		}
		fmt.Printf("%[2]*[1]v", s, len(field.title))
		if field.reset {
			field.accu += field.value
			field.value = 0
		}
		first = false
		<-field.wait
	}
	fmt.Println()
}

func printAccu() {
	first := true
	for _, field := range fields {
		field.wait <- 1
		s := strconv.Itoa(field.accu)
		if !first {
			fmt.Printf(" ")
		}
		fmt.Printf("(%[2]*[1]v)", s, len(field.title)-2)
		if field.reset {
			field.accu = 0
		}
		first = false
		<-field.wait
	}
	fmt.Println()
}

// NewField creates a new monitor field that is shown as a column each second
//
// - title is the title printed on titles row each ten seconds
//
// - reset tells to reset field value after each time its printed
func NewField(title string, reset bool) *Field {
	field := new(Field)
	field.title = title
	field.reset = reset
	field.wait = make(chan int, 1)
	fields = append(fields, field)
	return field
}
<|endoftext|>


<commit_before>/*
Copyright (C) 2013 Ondrej Kupka
Copyright (C) 2013 Contributors as noted in the AUTHORS file

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/

/*
gozmq Monitor combines gozmq.Socket.Monitor API call with gozmq-poller.Poller
and implements a channel-based API for receiving socket events.
*/
package monitor

import (
	"fmt"
	"unsafe"
	"errors"
)

import zmq "github.com/alecthomas/gozmq"
import poller "github.com/tchap/gozmq-poller"

/*
#include <stdlib.h>
#include <zmq.h>
*/
import "C"

type SocketEvent struct {
	Event uint16
	Value int32
	Addr  string
	Error error
}

type Monitor struct {
	socket *zmq.Socket
	poller *poller.Poller
}

// Construct a new monitor reacting to the requested event types. Use addr as
// the internal inproc endpoint for transporting events.
func New(ctx *zmq.Context, socket *zmq.Socket,
	addr string, events zmq.Event) (monitor *Monitor, err error) {

	if C.ZMQ_VERSION_MAJOR < 3 || C.ZMQ_VERSION_MAJOR < 3 {
		return nil, errors.New("Only libzmq >= 3.3 is supported.")
	}

	mon, err := ctx.NewSocket(zmq.PAIR)
	if err != nil {
		return
	}

	err = socket.Monitor(addr, events)
	if err != nil {
		return
	}

	err = mon.Connect(addr)
	if err != nil {
		mon.Close()
		return
	}

	poller, err := poller.New(ctx, 0)
	if err != nil {
		mon.Close()
		return
	}

	monitor = &Monitor{
		socket: mon,
		poller: poller,
	}
	return
}

// Start polling for events on the monitor socket and pass those socket events
// through the channel back to the user.
func (self *Monitor) Start() (eventChan <-chan *SocketEvent, err error) {
	ch, err := self.poller.Poll(zmq.PollItems{zmq.PollItem{
		Socket: self.socket,
		Events: zmq.POLLIN,
	}})
	if err != nil {
		return
	}

	eventCh := make(chan *SocketEvent, 1)

	go func() {
		for res := range ch {
			if res.Error != nil {
				eventCh <- &SocketEvent{Error: res.Error}
				break
			}

			frames, ex := self.socket.RecvMultipart(0)
			if ex != nil {
				eventCh <- &SocketEvent{Error: ex}
				continue
			}

			ex = self.poller.Continue()
			if ex != nil {
				eventCh <- &SocketEvent{Error: ex}
				continue
			}

			eventCh <- self.parseEvent(frames)
		}
		close(eventCh)
	}()

	eventChan = eventCh
	return
}

// A wrapper around Poller.Stop that stops the internal poller.
func (self *Monitor) Stop() (err error) {
	return self.poller.Stop()
}

// Close the poller, stop the other background goroutine.
func (self *Monitor) Close() (exit <-chan bool, err error) {
	ch, err := self.poller.Close()
	if err != nil {
		return
	}

	exitCh := make(chan bool, 1)
	exit = exitCh

	go func() {
		<-ch

		// Ignore the error (ENOTSOCK cannot happen anyway).
		self.socket.Close()

		exitCh <- true
		close(exitCh)
	}()

	return
}

// Close and wait for the close channel to be signaled.
func (self *Monitor) CloseWait() (err error) {
	ch, err := self.Close()
	if err != nil {
		return
	}
	<-ch
	return
}

// Internal wrapper around the helper function, which is implemented in C so
// that we can access C structures directly.
func (self *Monitor) parseEvent(frames [][]byte) (event *SocketEvent) {
	switch len(frames) {
	case 1: // libzmq < 3.3.0 - not supported
		panic("Deprecated libzmq API is not supported.")
	case 2: // libzmq >= 3.3.0
		var e *C.zmq_event_t
		e = ((*C.zmq_event_t)(unsafe.Pointer(&frames[0])));

		return &SocketEvent{
			Event: uint16(e.event),
			Value: int32(e.value),
			Addr:  string(frames[1]),
		}
	default:
		panic(fmt.Sprintln("Unexpected payload received: ", frames))
	}
}
<commit_msg>Get rid of C helper for event parsing<commit_after>/*
Copyright (C) 2013 Ondrej Kupka
Copyright (C) 2013 Contributors as noted in the AUTHORS file

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/

/*
gozmq Monitor combines gozmq.Socket.Monitor API call with gozmq-poller.Poller
and implements a channel-based API for receiving socket events.
*/
package monitor

// #include <zmq.h>
import "C"

import (
	"errors"
	"fmt"
	"unsafe"
)

import zmq "github.com/alecthomas/gozmq"
import poller "github.com/tchap/gozmq-poller"

type SocketEvent struct {
	Event uint16
	Value int32
	Addr  string
	Error error
}

type Monitor struct {
	socket *zmq.Socket
	poller *poller.Poller
}

// Construct a new monitor reacting to the requested event types. Use addr as
// the internal inproc endpoint for transporting events.
func New(ctx *zmq.Context, socket *zmq.Socket,
	addr string, events zmq.Event) (monitor *Monitor, err error) {

	if C.ZMQ_VERSION_MAJOR < 3 || C.ZMQ_VERSION_MINOR < 3 {
		return nil, errors.New("Deprecated API not supported")
	}

	mon, err := ctx.NewSocket(zmq.PAIR)
	if err != nil {
		return
	}

	err = socket.Monitor(addr, events)
	if err != nil {
		return
	}

	err = mon.Connect(addr)
	if err != nil {
		mon.Close()
		return
	}

	poller, err := poller.New(ctx, 0)
	if err != nil {
		mon.Close()
		return
	}

	monitor = &Monitor{
		socket: mon,
		poller: poller,
	}
	return
}

// Start polling for events on the monitor socket and pass those socket events
// through the channel back to the user.
func (self *Monitor) Start() (eventChan <-chan *SocketEvent, err error) {
	ch, err := self.poller.Poll(zmq.PollItems{zmq.PollItem{
		Socket: self.socket,
		Events: zmq.POLLIN,
	}})
	if err != nil {
		return
	}

	eventCh := make(chan *SocketEvent, 1)

	go func() {
		for res := range ch {
			if res.Error != nil {
				eventCh <- &SocketEvent{Error: res.Error}
				break
			}

			frames, ex := self.socket.RecvMultipart(0)
			if ex != nil {
				eventCh <- &SocketEvent{Error: ex}
				continue
			}

			ex = self.poller.Continue()

			if ex != nil {
				eventCh <- &SocketEvent{Error: ex}
				continue
			}

			eventCh <- self.parseEvent(frames)
		}
		close(eventCh)
	}()

	eventChan = eventCh
	return
}

// A wrapper around Poller.Stop that stops the internal poller.
func (self *Monitor) Stop() (err error) {
	return self.poller.Stop()
}

// Close the poller, stop the other background goroutine.
func (self *Monitor) Close() (exit <-chan bool, err error) {
	ch, err := self.poller.Close()
	if err != nil {
		return
	}

	exitCh := make(chan bool, 1)
	exit = exitCh

	go func() {
		<-ch

		// Ignore the error (ENOTSOCK cannot happen anyway).
		self.socket.Close()

		exitCh <- true
		close(exitCh)
	}()

	return
}

// Close and wait for the close channel to be signaled.
func (self *Monitor) CloseWait() (err error) {
	ch, err := self.Close()
	if err != nil {
		return
	}
	<-ch
	return
}

// Internal wrapper around the helper function, which is implemented in C so
// that we can access C structures directly.
func (self *Monitor) parseEvent(frames [][]byte) (event *SocketEvent) {
	switch len(frames) {
	case 1: // libzmq < 3.3.0 - not supported
		panic("Deprecated libzmq API is not supported.")
	case 2: // libzmq >= 3.3.0
		var e SocketEvent
		uint16Size := unsafe.Sizeof(e.Event)
		int32Size := unsafe.Sizeof(e.Value)

		if uintptr(len(frames[0])) != (uint16Size + int32Size) {
			panic(fmt.Sprintln("Invalid payload size [frame %v]", frames[0]))
		}
		event_raw := frames[0][:uint16Size]
		value_raw := frames[0][int32Size:]

		e.Event = *(*uint16)(unsafe.Pointer(&event_raw[0]))
		e.Value = *(*int32)(unsafe.Pointer(&value_raw[0]))
		e.Addr = string(frames[1])
		return &e
	default:
		panic(fmt.Sprintln("Unexpected payload received: %v", frames))
	}
}
<|endoftext|>


<commit_before>package mvc

import (
	"github.com/kataras/iris/context"
	"github.com/kataras/iris/core/router"
	"github.com/kataras/iris/hero"
	"github.com/kataras/iris/hero/di"
)

var (
	// HeroDependencies let you share bindable dependencies between
	// package-level hero's registered dependencies and all MVC instances that comes later.
	//
	// `hero.Register(...)`
	// `myMVC := mvc.New(app.Party(...))`
	// the "myMVC" registers the dependencies provided by the `hero.Register` func
	// automatically.
	//
	// Set it to false to disable that behavior, you have to use the `mvc#Register`
	// even if you had register dependencies with the `hero` package.
	//
	// Defaults to true.
	HeroDependencies = true
)

// Application is the high-level compoment of the "mvc" package.
// It's the API that you will be using to register controllers among with their
// dependencies that your controllers may expecting.
// It contains the Router(iris.Party) in order to be able to register
// template layout, middleware, done handlers as you used with the
// standard Iris APIBuilder.
//
// The Engine is created by the `New` method and it's the dependencies holder
// and controllers factory.
//
// See `mvc#New` for more.
type Application struct {
	Dependencies di.Values
	Router       router.Party
}

func newApp(subRouter router.Party, values di.Values) *Application {
	return &Application{
		Router:       subRouter,
		Dependencies: values,
	}
}

// New returns a new mvc Application based on a "party".
// Application creates a new engine which is responsible for binding the dependencies
// and creating and activating the app's controller(s).
//
// Example: `New(app.Party("/todo"))` or `New(app)` as it's the same as `New(app.Party("/"))`.
func New(party router.Party) *Application {
	values := di.NewValues()
	if HeroDependencies {
		values = hero.Dependencies().Clone()
	}
	return newApp(party, values)
}

// Configure creates a new controller and configures it,
// this function simply calls the `New(party)` and its `.Configure(configurators...)`.
//
// A call of `mvc.New(app.Party("/path").Configure(buildMyMVC)` is equal to
//           	 `mvc.Configure(app.Party("/path"), buildMyMVC)`.
//
// Read more at `New() Application` and `Application#Configure` methods.
func Configure(party router.Party, configurators ...func(*Application)) *Application {
	// Author's Notes->
	// About the Configure's comment: +5 space to be shown in equal width to the previous or after line.
	//
	// About the Configure's design chosen:
	// Yes, we could just have a `New(party, configurators...)`
	// but I think the `New()` and `Configure(configurators...)` API seems more native to programmers,
	// at least to me and the people I ask for their opinion between them.
	// Because the `New()` can actually return something that can be fully configured without its `Configure`,
	// its `Configure` is there just to design the apps better and help end-devs to split their code wisely.
	return New(party).Configure(configurators...)
}

// Configure can be used to pass one or more functions that accept this
// Application, use this to add dependencies and controller(s).
//
// Example: `New(app.Party("/todo")).Configure(func(mvcApp *mvc.Application){...})`.
func (app *Application) Configure(configurators ...func(*Application)) *Application {
	for _, c := range configurators {
		c(app)
	}
	return app
}

// Register appends one or more values as dependencies.
// The value can be a single struct value-instance or a function
// which has one input and one output, the input should be
// an `iris.Context` and the output can be any type, that output type
// will be binded to the controller's field, if matching or to the
// controller's methods, if matching.
//
// These dependencies "values" can be changed per-controller as well,
// via controller's `BeforeActivation` and `AfterActivation` methods,
// look the `Handle` method for more.
//
// It returns this Application.
//
// Example: `.Register(loggerService{prefix: "dev"}, func(ctx iris.Context) User {...})`.
func (app *Application) Register(values ...interface{}) *Application {
	app.Dependencies.Add(values...)
	return app
}

// Handle serves a controller for the current mvc application's Router.
// It accept any custom struct which its functions will be transformed
// to routes.
//
// If "controller" has `BeforeActivation(b mvc.BeforeActivation)`
// or/and `AfterActivation(a mvc.AfterActivation)` then these will be called between the controller's `.activate`,
// use those when you want to modify the controller before or/and after
// the controller will be registered to the main Iris Application.
//
// It returns this mvc Application.
//
// Usage: `.Handle(new(TodoController))`.
//
// Controller accepts a sub router and registers any custom struct
// as controller, if struct doesn't have any compatible methods
// neither are registered via `ControllerActivator`'s `Handle` method
// then the controller is not registered at all.
//
// A Controller may have one or more methods
// that are wrapped to a handler and registered as routes before the server ran.
// The controller's method can accept any input argument that are previously binded
// via the dependencies or route's path accepts dynamic path parameters.
// The controller's fields are also bindable via the dependencies, either a
// static value (service) or a function (dynamically) which accepts a context
// and returns a single value (this type is being used to find the relative field or method's input argument).
//
// func(c *ExampleController) Get() string |
// (string, string) |
// (string, int) |
// int |
// (int, string |
// (string, error) |
// bool |
// (any, bool) |
// error |
// (int, error) |
// (customStruct, error) |
// customStruct |
// (customStruct, int) |
// (customStruct, string) |
// Result or (Result, error)
// where Get is an HTTP Method func.
//
// Examples at: https://github.com/kataras/iris/tree/master/_examples/mvc
func (app *Application) Handle(controller interface{}) *Application {
	// initialize the controller's activator, nothing too magical so far.
	c := newControllerActivator(app.Router, controller, app.Dependencies)

	// check the controller's "BeforeActivation" or/and "AfterActivation" method(s) between the `activate`
	// call, which is simply parses the controller's methods, end-dev can register custom controller's methods
	// by using the BeforeActivation's (a ControllerActivation) `.Handle` method.
	if before, ok := controller.(interface {
		BeforeActivation(BeforeActivation)
	}); ok {
		before.BeforeActivation(c)
	}

	c.activate()

	if after, okAfter := controller.(interface {
		AfterActivation(AfterActivation)
	}); okAfter {
		after.AfterActivation(c)
	}
	return app
}

// Clone returns a new mvc Application which has the dependencies
// of the current mvc Mpplication's dependencies.
//
// Example: `.Clone(app.Party("/path")).Handle(new(TodoSubController))`.
func (app *Application) Clone(party router.Party) *Application {
	return newApp(party, app.Dependencies.Clone())
}

// Party returns a new child mvc Application based on the current path + "relativePath".
// The new mvc Application has the same dependencies of the current mvc Application,
// until otherwise specified later manually.
//
// The router's root path of this child will be the current mvc Application's root path + "relativePath".
func (app *Application) Party(relativePath string, middleware ...context.Handler) *Application {
	return app.Clone(app.Router.Party(relativePath, middleware...))
}
<commit_msg>add a warning on mvc if someone didn't read the examples or the godocs and .Register dependencies after .Handle<commit_after>package mvc

import (
	"strings"

	"github.com/kataras/iris/context"
	"github.com/kataras/iris/core/router"
	"github.com/kataras/iris/hero"
	"github.com/kataras/iris/hero/di"

	"github.com/kataras/golog"
)

var (
	// HeroDependencies let you share bindable dependencies between
	// package-level hero's registered dependencies and all MVC instances that comes later.
	//
	// `hero.Register(...)`
	// `myMVC := mvc.New(app.Party(...))`
	// the "myMVC" registers the dependencies provided by the `hero.Register` func
	// automatically.
	//
	// Set it to false to disable that behavior, you have to use the `mvc#Register`
	// even if you had register dependencies with the `hero` package.
	//
	// Defaults to true.
	HeroDependencies = true
)

// Application is the high-level compoment of the "mvc" package.
// It's the API that you will be using to register controllers among with their
// dependencies that your controllers may expecting.
// It contains the Router(iris.Party) in order to be able to register
// template layout, middleware, done handlers as you used with the
// standard Iris APIBuilder.
//
// The Engine is created by the `New` method and it's the dependencies holder
// and controllers factory.
//
// See `mvc#New` for more.
type Application struct {
	Dependencies di.Values
	Router       router.Party
	Controllers  []*ControllerActivator
}

func newApp(subRouter router.Party, values di.Values) *Application {
	return &Application{
		Router:       subRouter,
		Dependencies: values,
	}
}

// New returns a new mvc Application based on a "party".
// Application creates a new engine which is responsible for binding the dependencies
// and creating and activating the app's controller(s).
//
// Example: `New(app.Party("/todo"))` or `New(app)` as it's the same as `New(app.Party("/"))`.
func New(party router.Party) *Application {
	values := di.NewValues()
	if HeroDependencies {
		values = hero.Dependencies().Clone()
	}
	return newApp(party, values)
}

// Configure creates a new controller and configures it,
// this function simply calls the `New(party)` and its `.Configure(configurators...)`.
//
// A call of `mvc.New(app.Party("/path").Configure(buildMyMVC)` is equal to
//           	 `mvc.Configure(app.Party("/path"), buildMyMVC)`.
//
// Read more at `New() Application` and `Application#Configure` methods.
func Configure(party router.Party, configurators ...func(*Application)) *Application {
	// Author's Notes->
	// About the Configure's comment: +5 space to be shown in equal width to the previous or after line.
	//
	// About the Configure's design chosen:
	// Yes, we could just have a `New(party, configurators...)`
	// but I think the `New()` and `Configure(configurators...)` API seems more native to programmers,
	// at least to me and the people I ask for their opinion between them.
	// Because the `New()` can actually return something that can be fully configured without its `Configure`,
	// its `Configure` is there just to design the apps better and help end-devs to split their code wisely.
	return New(party).Configure(configurators...)
}

// Configure can be used to pass one or more functions that accept this
// Application, use this to add dependencies and controller(s).
//
// Example: `New(app.Party("/todo")).Configure(func(mvcApp *mvc.Application){...})`.
func (app *Application) Configure(configurators ...func(*Application)) *Application {
	for _, c := range configurators {
		c(app)
	}
	return app
}

// Register appends one or more values as dependencies.
// The value can be a single struct value-instance or a function
// which has one input and one output, the input should be
// an `iris.Context` and the output can be any type, that output type
// will be binded to the controller's field, if matching or to the
// controller's methods, if matching.
//
// These dependencies "values" can be changed per-controller as well,
// via controller's `BeforeActivation` and `AfterActivation` methods,
// look the `Handle` method for more.
//
// It returns this Application.
//
// Example: `.Register(loggerService{prefix: "dev"}, func(ctx iris.Context) User {...})`.
func (app *Application) Register(values ...interface{}) *Application {
	if len(values) > 0 && app.Dependencies.Len() == 0 && len(app.Controllers) > 0 {
		allControllerNamesSoFar := make([]string, len(app.Controllers))
		for i := range app.Controllers {
			allControllerNamesSoFar[i] = app.Controllers[i].Name()
		}

		golog.Warnf(`mvc.Application#Register called after mvc.Application#Handle.
The controllers[%s] may miss required dependencies.
Set the Logger's Level to "debug" to view the active dependencies per controller.`, strings.Join(allControllerNamesSoFar, ","))
	}

	app.Dependencies.Add(values...)

	return app
}

// Handle serves a controller for the current mvc application's Router.
// It accept any custom struct which its functions will be transformed
// to routes.
//
// If "controller" has `BeforeActivation(b mvc.BeforeActivation)`
// or/and `AfterActivation(a mvc.AfterActivation)` then these will be called between the controller's `.activate`,
// use those when you want to modify the controller before or/and after
// the controller will be registered to the main Iris Application.
//
// It returns this mvc Application.
//
// Usage: `.Handle(new(TodoController))`.
//
// Controller accepts a sub router and registers any custom struct
// as controller, if struct doesn't have any compatible methods
// neither are registered via `ControllerActivator`'s `Handle` method
// then the controller is not registered at all.
//
// A Controller may have one or more methods
// that are wrapped to a handler and registered as routes before the server ran.
// The controller's method can accept any input argument that are previously binded
// via the dependencies or route's path accepts dynamic path parameters.
// The controller's fields are also bindable via the dependencies, either a
// static value (service) or a function (dynamically) which accepts a context
// and returns a single value (this type is being used to find the relative field or method's input argument).
//
// func(c *ExampleController) Get() string |
// (string, string) |
// (string, int) |
// int |
// (int, string |
// (string, error) |
// bool |
// (any, bool) |
// error |
// (int, error) |
// (customStruct, error) |
// customStruct |
// (customStruct, int) |
// (customStruct, string) |
// Result or (Result, error)
// where Get is an HTTP Method func.
//
// Examples at: https://github.com/kataras/iris/tree/master/_examples/mvc
func (app *Application) Handle(controller interface{}) *Application {
	// initialize the controller's activator, nothing too magical so far.
	c := newControllerActivator(app.Router, controller, app.Dependencies)

	// check the controller's "BeforeActivation" or/and "AfterActivation" method(s) between the `activate`
	// call, which is simply parses the controller's methods, end-dev can register custom controller's methods
	// by using the BeforeActivation's (a ControllerActivation) `.Handle` method.
	if before, ok := controller.(interface {
		BeforeActivation(BeforeActivation)
	}); ok {
		before.BeforeActivation(c)
	}

	c.activate()

	if after, okAfter := controller.(interface {
		AfterActivation(AfterActivation)
	}); okAfter {
		after.AfterActivation(c)
	}

	app.Controllers = append(app.Controllers, c)
	return app
}

// Clone returns a new mvc Application which has the dependencies
// of the current mvc Mpplication's dependencies.
//
// Example: `.Clone(app.Party("/path")).Handle(new(TodoSubController))`.
func (app *Application) Clone(party router.Party) *Application {
	return newApp(party, app.Dependencies.Clone())
}

// Party returns a new child mvc Application based on the current path + "relativePath".
// The new mvc Application has the same dependencies of the current mvc Application,
// until otherwise specified later manually.
//
// The router's root path of this child will be the current mvc Application's root path + "relativePath".
func (app *Application) Party(relativePath string, middleware ...context.Handler) *Application {
	return app.Clone(app.Router.Party(relativePath, middleware...))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	blockSize = flag.Int64("b", 1024*1024, "block size")
	numBlocks = flag.Int("n", 10, "number of concurrent blocks")
	cont      = flag.Bool("c", false, "continue")
	filename  = flag.String("o", "", "output filename. the last part of the url is used if not set.")
	urlPath   = flag.String("u", "", "url")
	verbose   = flag.Bool("v", false, "verbose")

	ur *url.URL
	c  = http.Client{Timeout: 5 * time.Minute}
)

func lg(format string, arg ...interface{}) {
	if *verbose {
		log.Printf(format, arg...)
	}
}

type offsetRange struct {
	Start, End int64 // inclusive
}

type offsetRangeSlice []offsetRange

func (ors offsetRangeSlice) Less(i, j int) bool { return ors[i].Start < ors[j].Start }
func (ors offsetRangeSlice) Len() int           { return len(ors) }
func (ors offsetRangeSlice) Swap(i, j int)      { ors[i], ors[j] = ors[j], ors[i] }

type downloader struct {
	file      *os.File
	mu        sync.Mutex
	total     int64
	committed int64
	offset    int64
	copied    []offsetRange
	err       error
}

// TODO:
//  - support cookies

func main() {
	flag.Parse()

	var err error
	if ur, err = url.Parse(*urlPath); err != nil {
		log.Fatalf("invalid url=%s err=%v", *urlPath, err)
	}

	dl := NewDownloader()
	lg("dl starts with filename=%s committed=%d total=%d", *filename, dl.committed, dl.total)

	var wg sync.WaitGroup
	wg.Add(*numBlocks)
	for i := 0; i < *numBlocks; i++ {
		go func(i int) {
			defer wg.Done()
			err := dl.Run()
			lg("dl-%d stopped err=%v", i, err)
		}(i)
	}
	wg.Wait()

	if err = dl.Close(); err != nil {
		log.Fatalf("close err=%v", err)
	}
	if dl.committed != dl.total {
		log.Fatalf("want=%d got=%d", dl.total, dl.committed)
	}
}

func getFilename() string {
	if *filename != "" {
		return *filename
	}
	_, fn := filepath.Split(ur.Path)
	if fn == "" {
		log.Fatalf("cannot derive file name from url path=%s", ur.Path)
	}
	return fn
}

func openFile() *os.File {
	openFlag := os.O_WRONLY | os.O_CREATE
	if !*cont {
		openFlag |= os.O_TRUNC
	}
	f, err := os.OpenFile(*filename, openFlag, 0600)
	if err != nil {
		log.Fatalf("open file=%s err=%v", *filename, err)
	}
	return f
}

func getFileSize(file *os.File) int64 {
	fi, err := file.Stat()
	if err != nil {
		log.Fatalf("stat file=%s err=%v", file.Name(), err)
	}
	return fi.Size()
}

func (dl *downloader) Run() error {
	req := http.Request{
		URL:    ur,
		Header: make(http.Header),
	}

	b := make([]byte, int(*blockSize))
	for {
		dl.mu.Lock()
		start, end := dl.offset, dl.offset+*blockSize-1
		if dl.total > 0 && end >= dl.total {
			end = dl.total - 1
		}
		dl.offset = end + 1
		err := dl.err
		dl.mu.Unlock()

		if err != nil {
			return fmt.Errorf("dl done at start=%d end=%d with err=%v", start, end, err)
		}
		if start > end {
			return fmt.Errorf("dl reaches end start=%d end=%d", start, end)
		}

		rr, err := doRangeRequest(&req, start, end)
		if err != nil {
			err = fmt.Errorf("req of range=%d-%d err=%v", start, end, err)
			dl.setErr(err)
			return err
		}
		lg("range resp=%+v", rr)

		n, err := dl.readBody(rr.body, b)
		if err != nil {
			err = fmt.Errorf("read body err=%v", err)
			dl.setErr(err)
			return err
		}
		if n == 0 {
			log.Panicln("readbody returns 0 but no err")
		}
		if n != int(rr.end-rr.start+1) {
			log.Panicf("readbody got=%d/%d-%d want=%d", rr.end-rr.start+1, rr.start, rr.end, n)
		}

		dl.mu.Lock()
		// serialize disk i/o
		if _, err = dl.file.WriteAt(b[:n], start); err != nil {
			err = fmt.Errorf("write at=%d of n=%d err=%v", start, n, err)
		}

		if err == nil {
			end = rr.end
			dl.setTotal(rr.total)
			lg("%d-%d downloaded", start, end)
			dl.copied = append(dl.copied, offsetRange{start, end})
			dl.advanceCommit()
		}
		dl.mu.Unlock()

		if err != nil {
			return err
		}
	}
}

func (dl *downloader) advanceCommit() {
	begin := dl.committed
	sort.Sort(offsetRangeSlice(dl.copied))
	lg("copied=%v", dl.copied)
	i := 0
	for ; i < len(dl.copied); i++ {
		r := dl.copied[i]
		if r.Start == dl.committed {
			dl.committed = r.End + 1
			continue
		}
		if r.Start < dl.committed {
			dl.setErr(fmt.Errorf("invalid range=(%d,%d) before committed=%d", r.Start, r.End, dl.committed))
		}
		break
	}
	dl.copied = dl.copied[i:]
	if dl.committed > begin {
		log.Printf("advance committed to %d", dl.committed)
	}
}

func (dl *downloader) setErr(err error) {
	if dl.err == nil {
		lg("dl err=%v", err)
		dl.err = err
	}
}

func (dl *downloader) readBody(body io.ReadCloser, b []byte) (int, error) {
	n, err := io.ReadFull(body, b)
	body.Close()
	if err != nil && err != io.ErrUnexpectedEOF {
		return 0, fmt.Errorf("read body err=%v", err)
	}
	return n, nil
}

func parseContentRange(cr string) (start, end, total int64, err error) {
	s := cr

	i := strings.LastIndex(s, " ")
	if i >= 0 {
		s = s[i+1:]
	}

	fs := make([]string, 3)
	// a-b/c
	i = strings.Index(s, "-")
	if i < 0 {
		return 0, 0, 0, fmt.Errorf("malformed Content-Range=%s", cr)
	}
	fs[0] = s[:i]
	s = s[i+1:]

	i = strings.Index(s, "/")
	if i < 0 {
		return 0, 0, 0, fmt.Errorf("malformed Content-Range=%s", cr)
	}
	fs[1], fs[2] = s[:i], s[i+1:]

	is := make([]int64, 3)
	for i, s := range fs {
		n, err := strconv.ParseInt(s, 10, 64)
		if err != nil {
			return 0, 0, 0, fmt.Errorf("malformed range Content-Range=%s err=%v", cr, err)
		}
		is[i] = n
	}
	return is[0], is[1], is[2], nil
}

func firstErr(err ...error) error {
	for _, e := range err {
		if e != nil {
			return e
		}
	}
	return nil
}

func (dl *downloader) Close() error {
	err1 := dl.file.Truncate(dl.committed)
	err2 := dl.file.Close()
	return firstErr(err1, err2)
}

func (dl *downloader) setTotal(total int64) {
	switch {
	case total <= 0:
		return

	case dl.total == 0:
		dl.total = total

	case dl.total != total:
		log.Fatalf("total changed from %d to %d", dl.total, total)
	}
}

func NewDownloader() *downloader {
	sfn, total, err := queryURL()
	if err != nil {
		log.Fatalf("head url=%v err=%v", ur, err)
	}

	var committed int64
	*filename = getFilename()
	fi, err := os.Stat(*filename)
	if err != nil {
		// file does not exist
		if sfn != "" {
			*filename = sfn
		}
		*cont = false
	} else if *cont {
		committed = fi.Size()
	}
	log.Printf("start downloader: filename=%s committed=%d total=%d", sfn, committed, total)

	return &downloader{
		file:      openFile(),
		committed: committed,
		offset:    committed,
		total:     total,
	}
}

type rangeResp struct {
	filename   string
	start, end int64
	total      int64
	body       io.ReadCloser
}

func doRangeRequest(req *http.Request, start, end int64) (*rangeResp, error) {
	rh := fmt.Sprintf("bytes=%d-%d", start, end)
	req.Header.Set("Range", rh)
	resp, err := c.Do(req)
	if err != nil {
		// unexpected error
		return nil, fmt.Errorf("http do %s err=%v", rh, err)
	}

	if (resp.StatusCode / 100) != 2 {
		return nil, fmt.Errorf("bad resp status=%s", resp.Status)
	}

	rr := &rangeResp{body: resp.Body}
	if rr.start, rr.end, rr.total, err = parseContentRange(resp.Header.Get("Content-Range")); err != nil {
		return nil, err
	}
	if rr.start != start {
		return nil, fmt.Errorf("start of range resp want=%d got=%d", start, rr.start)
	}
	if cd := resp.Header.Get("Content-Disposition"); cd != "" {
		rr.filename = parseContentDisposition(cd)
	}
	return rr, nil
}

func queryURL() (fn string, total int64, err error) {
	req := http.Request{
		// implement HEAD with GET, which has wider support
		Method: "GET",
		URL:    ur,
		Header: make(http.Header),
	}

	rr, err := doRangeRequest(&req, 0, 1)
	if err != nil {
		return "", 0, err
	}
	return rr.filename, rr.total, nil
}

func parseContentDisposition(cd string) string {
	tok := `filename="`
	i := strings.Index(cd, tok)
	if i < 0 {
		return ""
	}
	i += len(tok)
	if !strings.HasSuffix(cd, `"`) {
		return ""
	}
	return cd[i : len(cd)-1]
}
<commit_msg>fix file name<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	blockSize = flag.Int64("b", 1024*1024, "block size")
	numBlocks = flag.Int("n", 10, "number of concurrent blocks")
	cont      = flag.Bool("c", false, "continue")
	filename  = flag.String("o", "", "output filename. the last part of the url is used if not set.")
	urlPath   = flag.String("u", "", "url")
	verbose   = flag.Bool("v", false, "verbose")

	ur *url.URL
	c  = http.Client{Timeout: 5 * time.Minute}
)

func lg(format string, arg ...interface{}) {
	if *verbose {
		log.Printf(format, arg...)
	}
}

type offsetRange struct {
	Start, End int64 // inclusive
}

type offsetRangeSlice []offsetRange

func (ors offsetRangeSlice) Less(i, j int) bool { return ors[i].Start < ors[j].Start }
func (ors offsetRangeSlice) Len() int           { return len(ors) }
func (ors offsetRangeSlice) Swap(i, j int)      { ors[i], ors[j] = ors[j], ors[i] }

type downloader struct {
	file      *os.File
	mu        sync.Mutex
	total     int64
	committed int64
	offset    int64
	copied    []offsetRange
	err       error
}

// TODO:
//  - support cookies

func main() {
	flag.Parse()

	var err error
	if ur, err = url.Parse(*urlPath); err != nil {
		log.Fatalf("invalid url=%s err=%v", *urlPath, err)
	}

	dl := NewDownloader()
	lg("dl starts with filename=%s committed=%d total=%d", *filename, dl.committed, dl.total)

	var wg sync.WaitGroup
	wg.Add(*numBlocks)
	for i := 0; i < *numBlocks; i++ {
		go func(i int) {
			defer wg.Done()
			err := dl.Run()
			lg("dl-%d stopped err=%v", i, err)
		}(i)
	}
	wg.Wait()

	if err = dl.Close(); err != nil {
		log.Fatalf("close err=%v", err)
	}
	if dl.committed != dl.total {
		log.Fatalf("want=%d got=%d", dl.total, dl.committed)
	}
}

func getFilename(sfn string) string {
	switch {
	case *filename != "":
		return *filename
	case sfn != "":
		return sfn
	default:
		_, fn := filepath.Split(ur.Path)
		if fn == "" {
			log.Fatalf("cannot derive file name from url path=%s", ur.Path)
		}
		return fn
	}
}

func openFile() *os.File {
	openFlag := os.O_WRONLY | os.O_CREATE
	if !*cont {
		openFlag |= os.O_TRUNC
	}
	f, err := os.OpenFile(*filename, openFlag, 0600)
	if err != nil {
		log.Fatalf("open file=%s err=%v", *filename, err)
	}
	return f
}

func getFileSize(file *os.File) int64 {
	fi, err := file.Stat()
	if err != nil {
		log.Fatalf("stat file=%s err=%v", file.Name(), err)
	}
	return fi.Size()
}

func (dl *downloader) Run() error {
	req := http.Request{
		URL:    ur,
		Header: make(http.Header),
	}

	b := make([]byte, int(*blockSize))
	for {
		dl.mu.Lock()
		start, end := dl.offset, dl.offset+*blockSize-1
		if dl.total > 0 && end >= dl.total {
			end = dl.total - 1
		}
		dl.offset = end + 1
		err := dl.err
		dl.mu.Unlock()

		if err != nil {
			return fmt.Errorf("dl done at start=%d end=%d with err=%v", start, end, err)
		}
		if start > end {
			return fmt.Errorf("dl reaches end start=%d end=%d", start, end)
		}

		rr, err := doRangeRequest(&req, start, end)
		if err != nil {
			err = fmt.Errorf("req of range=%d-%d err=%v", start, end, err)
			dl.setErr(err)
			return err
		}
		lg("range resp=%+v", rr)

		n, err := dl.readBody(rr.body, b)
		if err != nil {
			err = fmt.Errorf("read body err=%v", err)
			dl.setErr(err)
			return err
		}
		if n == 0 {
			log.Panicln("readbody returns 0 but no err")
		}
		if n != int(rr.end-rr.start+1) {
			log.Panicf("readbody got=%d/%d-%d want=%d", rr.end-rr.start+1, rr.start, rr.end, n)
		}

		dl.mu.Lock()
		// serialize disk i/o
		if _, err = dl.file.WriteAt(b[:n], start); err != nil {
			err = fmt.Errorf("write at=%d of n=%d err=%v", start, n, err)
		}

		if err == nil {
			end = rr.end
			dl.setTotal(rr.total)
			lg("%d-%d downloaded", start, end)
			dl.copied = append(dl.copied, offsetRange{start, end})
			dl.advanceCommit()
		}
		dl.mu.Unlock()

		if err != nil {
			return err
		}
	}
}

func (dl *downloader) advanceCommit() {
	begin := dl.committed
	sort.Sort(offsetRangeSlice(dl.copied))
	lg("copied=%v", dl.copied)
	i := 0
	for ; i < len(dl.copied); i++ {
		r := dl.copied[i]
		if r.Start == dl.committed {
			dl.committed = r.End + 1
			continue
		}
		if r.Start < dl.committed {
			dl.setErr(fmt.Errorf("invalid range=(%d,%d) before committed=%d", r.Start, r.End, dl.committed))
		}
		break
	}
	dl.copied = dl.copied[i:]
	if dl.committed > begin {
		log.Printf("advance committed to %d", dl.committed)
	}
}

func (dl *downloader) setErr(err error) {
	if dl.err == nil {
		lg("dl err=%v", err)
		dl.err = err
	}
}

func (dl *downloader) readBody(body io.ReadCloser, b []byte) (int, error) {
	n, err := io.ReadFull(body, b)
	body.Close()
	if err != nil && err != io.ErrUnexpectedEOF {
		return 0, fmt.Errorf("read body err=%v", err)
	}
	return n, nil
}

func parseContentRange(cr string) (start, end, total int64, err error) {
	s := cr

	i := strings.LastIndex(s, " ")
	if i >= 0 {
		s = s[i+1:]
	}

	fs := make([]string, 3)
	// a-b/c
	i = strings.Index(s, "-")
	if i < 0 {
		return 0, 0, 0, fmt.Errorf("malformed Content-Range=%s", cr)
	}
	fs[0] = s[:i]
	s = s[i+1:]

	i = strings.Index(s, "/")
	if i < 0 {
		return 0, 0, 0, fmt.Errorf("malformed Content-Range=%s", cr)
	}
	fs[1], fs[2] = s[:i], s[i+1:]

	is := make([]int64, 3)
	for i, s := range fs {
		n, err := strconv.ParseInt(s, 10, 64)
		if err != nil {
			return 0, 0, 0, fmt.Errorf("malformed range Content-Range=%s err=%v", cr, err)
		}
		is[i] = n
	}
	return is[0], is[1], is[2], nil
}

func firstErr(err ...error) error {
	for _, e := range err {
		if e != nil {
			return e
		}
	}
	return nil
}

func (dl *downloader) Close() error {
	err1 := dl.file.Truncate(dl.committed)
	err2 := dl.file.Close()
	return firstErr(err1, err2)
}

func (dl *downloader) setTotal(total int64) {
	switch {
	case total <= 0:
		return

	case dl.total == 0:
		dl.total = total

	case dl.total != total:
		log.Fatalf("total changed from %d to %d", dl.total, total)
	}
}

func NewDownloader() *downloader {
	sfn, total, err := queryURL()
	if err != nil {
		log.Fatalf("head url=%v err=%v", ur, err)
	}

	var committed int64
	*filename = getFilename(sfn)
	fi, err := os.Stat(*filename)
	if err != nil {
		// file does not exist
		*cont = false
	} else if *cont {
		committed = fi.Size()
	}
	log.Printf("start downloader: filename=%s committed=%d total=%d", sfn, committed, total)

	return &downloader{
		file:      openFile(),
		committed: committed,
		offset:    committed,
		total:     total,
	}
}

type rangeResp struct {
	filename   string
	start, end int64
	total      int64
	body       io.ReadCloser
}

func doRangeRequest(req *http.Request, start, end int64) (*rangeResp, error) {
	rh := fmt.Sprintf("bytes=%d-%d", start, end)
	req.Header.Set("Range", rh)
	resp, err := c.Do(req)
	if err != nil {
		// unexpected error
		return nil, fmt.Errorf("http do %s err=%v", rh, err)
	}

	if (resp.StatusCode / 100) != 2 {
		return nil, fmt.Errorf("bad resp status=%s", resp.Status)
	}

	rr := &rangeResp{body: resp.Body}
	if rr.start, rr.end, rr.total, err = parseContentRange(resp.Header.Get("Content-Range")); err != nil {
		return nil, err
	}
	if rr.start != start {
		return nil, fmt.Errorf("start of range resp want=%d got=%d", start, rr.start)
	}
	if cd := resp.Header.Get("Content-Disposition"); cd != "" {
		rr.filename = parseContentDisposition(cd)
	}
	return rr, nil
}

func queryURL() (fn string, total int64, err error) {
	req := http.Request{
		// implement HEAD with GET, which has wider support
		Method: "GET",
		URL:    ur,
		Header: make(http.Header),
	}

	rr, err := doRangeRequest(&req, 0, 1)
	if err != nil {
		return "", 0, err
	}
	return rr.filename, rr.total, nil
}

func parseContentDisposition(cd string) string {
	tok := `filename="`
	i := strings.Index(cd, tok)
	if i < 0 {
		return ""
	}
	i += len(tok)
	if !strings.HasSuffix(cd, `"`) {
		return ""
	}
	return cd[i : len(cd)-1]
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package astar

import (
	"container/heap"
)

// An item is something we manage in a priority queue.
type item struct {
	value    interface{} // The value of the item; arbitrary.
	priority float64     // The priority of the item in the queue.
	// The index is needed by update and is maintained by the heap.Interface methods.
	index int // The index of the item in the heap.
}

// A priorityQueue implements heap.Interface and holds items.
type priorityQueue []*item

func (pq priorityQueue) Len() int { return len(pq) }

func (pq priorityQueue) Less(i, j int) bool {
	// We want Pop to give us the highest, not lowest, priority so we use greater than here.
	return pq[i].priority > pq[j].priority
}

func (pq priorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].index = i
	pq[j].index = j
}

func (pq *priorityQueue) Push(x interface{}) {
	n := len(*pq)
	item := x.(*item)
	item.index = n
	*pq = append(*pq, item)
}

func (pq *priorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	item.index = -1 // for safety
	*pq = old[0 : n-1]
	return item
}

// update modifies the priority and value of an item in the queue.
func (pq *priorityQueue) update(item *item, value interface{}, priority float64) {
	heap.Remove(pq, item.index)
	item.value = value
	item.priority = priority
	heap.Push(pq, item)
}
<commit_msg>remove unused code from priority queue implementation<commit_after>// Copyright (c) 2012 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//    * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//    * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package astar

// An item is something we manage in a priority queue.
type item struct {
	value    interface{} // The value of the item; arbitrary.
	priority float64     // The priority of the item in the queue.
}

// A priorityQueue implements heap.Interface and holds items.
type priorityQueue []*item

func (pq priorityQueue) Len() int { return len(pq) }

func (pq priorityQueue) Less(i, j int) bool {
	// We want Pop to give us the highest, not lowest,
	// priority so we use greater than here.
	return pq[i].priority > pq[j].priority
}

func (pq priorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}

func (pq *priorityQueue) Push(x interface{}) {
	item := x.(*item)
	*pq = append(*pq, item)
}

func (pq *priorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	*pq = old[0 : n-1]
	return item
}
<|endoftext|>