<commit_before>// Package handlers provides the http functionality
package handlers

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/maxibanki/golang-url-shortener/config"
	"github.com/maxibanki/golang-url-shortener/store"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

// Handler holds the funcs and attributes for the
// http communication
type Handler struct {
	config    config.Handlers
	store     store.Store
	engine    *gin.Engine
	oAuthConf *oauth2.Config
}

// URLUtil is used to help in- and outgoing requests for json
// un- and marshalling
type URLUtil struct {
	URL string
}

type oAuthUser struct {
	Sub           string `json:"sub"`
	Name          string `json:"name"`
	GivenName     string `json:"given_name"`
	FamilyName    string `json:"family_name"`
	Profile       string `json:"profile"`
	Picture       string `json:"picture"`
	Email         string `json:"email"`
	EmailVerified bool   `json:"email_verified"`
	Gender        string `json:"gender"`
	Hd            string `json:"hd"`
}

// New initializes the http handlers
func New(handlerConfig config.Handlers, store store.Store) *Handler {
	h := &Handler{
		config: handlerConfig,
		store:  store,
		engine: gin.Default(),
	}
	h.setHandlers()
	h.initOAuth()
	return h
}

func (h *Handler) setHandlers() {
	if !h.config.EnableGinDebugMode {
		gin.SetMode(gin.ReleaseMode)
	}
	h.engine.POST("/api/v1/create", h.handleCreate)
	h.engine.POST("/api/v1/info", h.handleInfo)
	// h.engine.Static("/static", "static/src")
	h.engine.NoRoute(h.handleAccess)
}

func (h *Handler) initOAuth() {
	store := sessions.NewCookieStore([]byte("secret"))

	h.oAuthConf = &oauth2.Config{
		ClientID:     h.config.OAuth.Google.ClientID,
		ClientSecret: h.config.OAuth.Google.ClientSecret,
		RedirectURL:  "http://127.0.0.1:3000/api/v1/auth/",
		Scopes: []string{
			"https://www.googleapis.com/auth/userinfo.email",
		},
		Endpoint: google.Endpoint,
	}
	h.engine.Use(sessions.Sessions("goquestsession", store))
	h.engine.GET("/api/v1/login", h.handleGoogleLogin)

	private := h.engine.Group("/api/v1/auth")
	private.Use(h.handleGoogleAuth)
	private.GET("/", h.handleGoogleCallback)
	private.GET("/api", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "Hello from private for groups"})
	})
}

func (h *Handler) randToken() string {
	b := make([]byte, 32)
	rand.Read(b)
	return base64.StdEncoding.EncodeToString(b)
}

func (h *Handler) handleGoogleAuth(c *gin.Context) {
	// Handle the exchange code to initiate a transport.
	session := sessions.Default(c)
	retrievedState := session.Get("state")
	if retrievedState != c.Query("state") {
		c.JSON(http.StatusUnauthorized, gin.H{"error": fmt.Errorf("Invalid session state: %s", retrievedState)})
		return
	}

	token, err := h.oAuthConf.Exchange(oauth2.NoContext, c.Query("code"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	client := h.oAuthConf.Client(oauth2.NoContext, token)
	userinfo, err := client.Get("https://www.googleapis.com/oauth2/v3/userinfo")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	defer userinfo.Body.Close()
	data, err := ioutil.ReadAll(userinfo.Body)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Could not read body: %v", err)})
		return
	}

	var user oAuthUser
	err = json.Unmarshal(data, &user)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Decoding userinfo failed: %v", err)})
		return
	}
	c.Set("user", user)
}

func (h *Handler) handleGoogleLogin(c *gin.Context) {
	state := h.randToken()
	session := sessions.Default(c)
	session.Set("state", state)
	session.Save()
	c.Redirect(http.StatusTemporaryRedirect, h.oAuthConf.AuthCodeURL(state))
}

func (h *Handler) handleGoogleCallback(ctx *gin.Context) {
	ctx.JSON(http.StatusOK, gin.H{"Hello": "from private", "user": ctx.MustGet("user").(oAuthUser)})
}

// handleCreate handles requests to create an entry
func (h *Handler) handleCreate(c *gin.Context) {
	var data struct {
		URL string
	}
	err := c.ShouldBind(&data)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	id, err := h.store.CreateEntry(data.URL, c.ClientIP())
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	data.URL = h.getSchemaAndHost(c) + "/" + id
	c.JSON(http.StatusOK, data)
}

func (h *Handler) getSchemaAndHost(c *gin.Context) string {
	protocol := "http"
	if c.Request.TLS != nil {
		protocol = "https"
	}
	return fmt.Sprintf("%s://%s", protocol, c.Request.Host)
}

// handleInfo is the http handler for getting the infos
func (h *Handler) handleInfo(c *gin.Context) {
	var data struct {
		ID string `binding:"required"`
	}
	err := c.ShouldBind(&data)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	entry, err := h.store.GetEntryByID(data.ID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}
	entry.RemoteAddr = ""
	c.JSON(http.StatusOK, entry)
}

// handleAccess handles the access for incoming requests
func (h *Handler) handleAccess(c *gin.Context) {
	var id string
	if len(c.Request.URL.Path) > 1 {
		id = c.Request.URL.Path[1:]
	}
	entry, err := h.store.GetEntryByID(id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}
	err = h.store.IncreaseVisitCounter(id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.Redirect(http.StatusTemporaryRedirect, entry.URL)
}

// Listen starts the http server
func (h *Handler) Listen() error {
	return h.engine.Run(h.config.ListenAddr)
}

// CloseStore stops the http server and the closes the db gracefully
func (h *Handler) CloseStore() error {
	return h.store.Close()
}
<commit_msg>Added normal 404 handling for URLs which doen't exist<commit_after>// Package handlers provides the http functionality
package handlers

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/maxibanki/golang-url-shortener/config"
	"github.com/maxibanki/golang-url-shortener/store"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

// Handler holds the funcs and attributes for the
// http communication
type Handler struct {
	config    config.Handlers
	store     store.Store
	engine    *gin.Engine
	oAuthConf *oauth2.Config
}

// URLUtil is used to help in- and outgoing requests for json
// un- and marshalling
type URLUtil struct {
	URL string
}

type oAuthUser struct {
	Sub           string `json:"sub"`
	Name          string `json:"name"`
	GivenName     string `json:"given_name"`
	FamilyName    string `json:"family_name"`
	Profile       string `json:"profile"`
	Picture       string `json:"picture"`
	Email         string `json:"email"`
	EmailVerified bool   `json:"email_verified"`
	Gender        string `json:"gender"`
	Hd            string `json:"hd"`
}

// New initializes the http handlers
func New(handlerConfig config.Handlers, store store.Store) *Handler {
	h := &Handler{
		config: handlerConfig,
		store:  store,
		engine: gin.Default(),
	}
	h.setHandlers()
	h.initOAuth()
	return h
}

func (h *Handler) setHandlers() {
	if !h.config.EnableGinDebugMode {
		gin.SetMode(gin.ReleaseMode)
	}
	h.engine.POST("/api/v1/create", h.handleCreate)
	h.engine.POST("/api/v1/info", h.handleInfo)
	// h.engine.Static("/static", "static/src")
	h.engine.NoRoute(h.handleAccess)
}

func (h *Handler) initOAuth() {
	store := sessions.NewCookieStore([]byte("secret"))

	h.oAuthConf = &oauth2.Config{
		ClientID:     h.config.OAuth.Google.ClientID,
		ClientSecret: h.config.OAuth.Google.ClientSecret,
		RedirectURL:  "http://127.0.0.1:3000/api/v1/auth/",
		Scopes: []string{
			"https://www.googleapis.com/auth/userinfo.email",
		},
		Endpoint: google.Endpoint,
	}
	h.engine.Use(sessions.Sessions("goquestsession", store))
	h.engine.GET("/api/v1/login", h.handleGoogleLogin)

	private := h.engine.Group("/api/v1/auth")
	private.Use(h.handleGoogleAuth)
	private.GET("/", h.handleGoogleCallback)
	private.GET("/api", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "Hello from private for groups"})
	})
}

func (h *Handler) randToken() string {
	b := make([]byte, 32)
	rand.Read(b)
	return base64.StdEncoding.EncodeToString(b)
}

func (h *Handler) handleGoogleAuth(c *gin.Context) {
	// Handle the exchange code to initiate a transport.
	session := sessions.Default(c)
	retrievedState := session.Get("state")
	if retrievedState != c.Query("state") {
		c.JSON(http.StatusUnauthorized, gin.H{"error": fmt.Errorf("Invalid session state: %s", retrievedState)})
		return
	}

	token, err := h.oAuthConf.Exchange(oauth2.NoContext, c.Query("code"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	client := h.oAuthConf.Client(oauth2.NoContext, token)
	userinfo, err := client.Get("https://www.googleapis.com/oauth2/v3/userinfo")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	defer userinfo.Body.Close()
	data, err := ioutil.ReadAll(userinfo.Body)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Could not read body: %v", err)})
		return
	}

	var user oAuthUser
	err = json.Unmarshal(data, &user)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Decoding userinfo failed: %v", err)})
		return
	}
	c.Set("user", user)
}

func (h *Handler) handleGoogleLogin(c *gin.Context) {
	state := h.randToken()
	session := sessions.Default(c)
	session.Set("state", state)
	session.Save()
	c.Redirect(http.StatusTemporaryRedirect, h.oAuthConf.AuthCodeURL(state))
}

func (h *Handler) handleGoogleCallback(ctx *gin.Context) {
	ctx.JSON(http.StatusOK, gin.H{"Hello": "from private", "user": ctx.MustGet("user").(oAuthUser)})
}

// handleCreate handles requests to create an entry
func (h *Handler) handleCreate(c *gin.Context) {
	var data struct {
		URL string
	}
	err := c.ShouldBind(&data)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	id, err := h.store.CreateEntry(data.URL, c.ClientIP())
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	data.URL = h.getSchemaAndHost(c) + "/" + id
	c.JSON(http.StatusOK, data)
}

func (h *Handler) getSchemaAndHost(c *gin.Context) string {
	protocol := "http"
	if c.Request.TLS != nil {
		protocol = "https"
	}
	return fmt.Sprintf("%s://%s", protocol, c.Request.Host)
}

// handleInfo is the http handler for getting the infos
func (h *Handler) handleInfo(c *gin.Context) {
	var data struct {
		ID string `binding:"required"`
	}
	err := c.ShouldBind(&data)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	entry, err := h.store.GetEntryByID(data.ID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}
	entry.RemoteAddr = ""
	c.JSON(http.StatusOK, entry)
}

// handleAccess handles the access for incoming requests
func (h *Handler) handleAccess(c *gin.Context) {
	var id string
	if len(c.Request.URL.Path) > 1 {
		id = c.Request.URL.Path[1:]
	}
	entry, err := h.store.GetEntryByID(id)
	if err == store.ErrIDIsEmpty || err == store.ErrNoEntryFound {
		return // return normal 404 error if such an error occurs
	} else if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		return
	}
	err = h.store.IncreaseVisitCounter(id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.Redirect(http.StatusTemporaryRedirect, entry.URL)
}

// Listen starts the http server
func (h *Handler) Listen() error {
	return h.engine.Run(h.config.ListenAddr)
}

// CloseStore stops the http server and the closes the db gracefully
func (h *Handler) CloseStore() error {
	return h.store.Close()
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Pagoda Box Inc
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not distributed with this file, You can obtain one
// at http://mozilla.org/MPL/2.0/.
//

package scribble

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync"

	"github.com/pagodabox/golang-hatchet"
)

//
const Version = "0.0.1"

//
type (

	// Driver
	Driver struct {
		mutexes map[string]sync.Mutex
		dir      string
		log      hatchet.Logger
	}

	// Transaction represents
	Transaction struct {
		Action     string
		Collection string
		ResourceID string
		Container  interface{}
	}
)

// New
func New(dir string, logger hatchet.Logger) (*Driver, error) {
	fmt.Printf("Creating database directory at '%v'...\n", dir)

	//
  if logger == nil {
    logger = hatchet.DevNullLogger{}
  }

	//
	scribble := &Driver{
		dir: 		 dir,
		mutexes: make(map[string]sync.Mutex),
		log: 		 logger,
	}

	//
	if err := mkDir(scribble.dir); err != nil {
		return nil, err
	}

	//
	return scribble, nil
}

// Transact
func (d *Driver) Transact(trans Transaction) error {

	//
	switch trans.Action {
	case "write":
		return d.write(trans)
	case "read":
		return d.read(trans)
	case "readall":
		return d.readAll(trans)
	case "delete":
		return d.delete(trans)
	default:
		return errors.New(fmt.Sprintf("Unsupported action %+v", trans.Action))
	}

	return nil
}

// private

// write
func (d *Driver) write(trans Transaction) error {

	mutex := d.getOrCreateMutex(trans.Collection)
	mutex.Lock()

	//
	dir := d.dir + "/" + trans.Collection

	//
	b, err := json.MarshalIndent(trans.Container, "", "\t")
	if err != nil {
		return err
	}

	//
	if err := mkDir(dir); err != nil {
		return err
	}

	//
	if err := ioutil.WriteFile(dir + "/" + trans.ResourceID, b, 0666); err != nil {
		return err
	}

	mutex.Unlock()

	return nil
}

// read
func (d *Driver) read(trans Transaction) error {

	dir := d.dir + "/" + trans.Collection

	b, err := ioutil.ReadFile(dir + "/" + trans.ResourceID)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(b, trans.Container); err != nil {
		return err
	}

	return nil
}

// readAll
func (d *Driver) readAll(trans Transaction) error {

	dir := d.dir + "/" + trans.Collection

	//
	files, err := ioutil.ReadDir(dir)

	// an error here just means an empty collection so do nothing
	if err != nil {
	}

	var f []string

	for _, file := range files {
		b, err := ioutil.ReadFile(dir + "/" + file.Name())
		if err != nil {
			return err
		}

		f = append(f, string(b))
	}

	//
	if err := json.Unmarshal([]byte("["+strings.Join(f, ",")+"]"), trans.Container); err != nil {
		return err
	}

	return nil
}

// delete
func (d *Driver) delete(trans Transaction) error {

	mutex := d.getOrCreateMutex(trans.Collection)
	mutex.Lock()

	dir := d.dir + "/" + trans.Collection

	err := os.Remove(dir + "/" + trans.ResourceID)
	if err != nil {
		return err
	}

	mutex.Unlock()

	return nil
}

// helpers

// getOrCreateMutex
func (d *Driver) getOrCreateMutex(collection string) sync.Mutex {

	c, ok := d.mutexes[collection]

	// if the mutex doesn't exist make it
	if !ok {
		d.mutexes[collection] = sync.Mutex{}
		return d.mutexes[collection]
	}

	return c
}

// mkDir
func mkDir(d string) error {

	//
	dir, _ := os.Stat(d)

	if dir == nil {
		err := os.MkdirAll(d, 0755)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>gofmt all the things<commit_after>// Copyright (c) 2015 Pagoda Box Inc
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not distributed with this file, You can obtain one
// at http://mozilla.org/MPL/2.0/.
//

package scribble

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync"

	"github.com/pagodabox/golang-hatchet"
)

//
const Version = "0.0.1"

//
type (

	// Driver
	Driver struct {
		mutexes map[string]sync.Mutex
		dir     string
		log     hatchet.Logger
	}

	// Transaction represents
	Transaction struct {
		Action     string
		Collection string
		ResourceID string
		Container  interface{}
	}
)

// New
func New(dir string, logger hatchet.Logger) (*Driver, error) {
	fmt.Printf("Creating database directory at '%v'...\n", dir)

	//
	if logger == nil {
		logger = hatchet.DevNullLogger{}
	}

	//
	scribble := &Driver{
		dir:     dir,
		mutexes: make(map[string]sync.Mutex),
		log:     logger,
	}

	//
	if err := mkDir(scribble.dir); err != nil {
		return nil, err
	}

	//
	return scribble, nil
}

// Transact
func (d *Driver) Transact(trans Transaction) error {

	//
	switch trans.Action {
	case "write":
		return d.write(trans)
	case "read":
		return d.read(trans)
	case "readall":
		return d.readAll(trans)
	case "delete":
		return d.delete(trans)
	default:
		return errors.New(fmt.Sprintf("Unsupported action %+v", trans.Action))
	}

	return nil
}

// private

// write
func (d *Driver) write(trans Transaction) error {

	mutex := d.getOrCreateMutex(trans.Collection)
	mutex.Lock()

	//
	dir := d.dir + "/" + trans.Collection

	//
	b, err := json.MarshalIndent(trans.Container, "", "\t")
	if err != nil {
		return err
	}

	//
	if err := mkDir(dir); err != nil {
		return err
	}

	//
	if err := ioutil.WriteFile(dir+"/"+trans.ResourceID, b, 0666); err != nil {
		return err
	}

	mutex.Unlock()

	return nil
}

// read
func (d *Driver) read(trans Transaction) error {

	dir := d.dir + "/" + trans.Collection

	b, err := ioutil.ReadFile(dir + "/" + trans.ResourceID)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(b, trans.Container); err != nil {
		return err
	}

	return nil
}

// readAll
func (d *Driver) readAll(trans Transaction) error {

	dir := d.dir + "/" + trans.Collection

	//
	files, err := ioutil.ReadDir(dir)

	// an error here just means an empty collection so do nothing
	if err != nil {
	}

	var f []string

	for _, file := range files {
		b, err := ioutil.ReadFile(dir + "/" + file.Name())
		if err != nil {
			return err
		}

		f = append(f, string(b))
	}

	//
	if err := json.Unmarshal([]byte("["+strings.Join(f, ",")+"]"), trans.Container); err != nil {
		return err
	}

	return nil
}

// delete
func (d *Driver) delete(trans Transaction) error {

	mutex := d.getOrCreateMutex(trans.Collection)
	mutex.Lock()

	dir := d.dir + "/" + trans.Collection

	err := os.Remove(dir + "/" + trans.ResourceID)
	if err != nil {
		return err
	}

	mutex.Unlock()

	return nil
}

// helpers

// getOrCreateMutex
func (d *Driver) getOrCreateMutex(collection string) sync.Mutex {

	c, ok := d.mutexes[collection]

	// if the mutex doesn't exist make it
	if !ok {
		d.mutexes[collection] = sync.Mutex{}
		return d.mutexes[collection]
	}

	return c
}

// mkDir
func mkDir(d string) error {

	//
	dir, _ := os.Stat(d)

	if dir == nil {
		err := os.MkdirAll(d, 0755)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package exif44

import (
	"errors"
	jseg "github.com/garyhouston/jpegsegs"
	tiff "github.com/garyhouston/tiff66"
	"io"
	"io/ioutil"
	"os"
)

// Control structure for Read and ReadFile, with optional callbacks.
type ReadControl struct {
	ReadExif ReadExif // Process Exif tree, or nil.
	// Additional callbacks could be added, e.g., for processing
	// other types of metadata, JPEG blocks, or full MPF trees.
}

type ReadExif interface {
	// Callback for processing Exif data, read-only. In the case
	// of TIFF files, this will be called once on the entire TIFF
	// tree. For JPEG files, it will be called on the Exif segment
	// for each image in the file (multiple images are supported
	// via Multi-Picture Format, MPF).
	ReadExif(format FileFormat, imageIdx uint32, exif Exif) error
}

// Read processes its input, which is expected to be an open image
// file in a supported format, currently JPEG or TIFF. It invokes
// any callbacks in the control structure.
func Read(reader io.ReadSeeker, control ReadControl) error {
	fileType, err := fileType(reader)
	if err != nil {
		return err
	}
	if _, err := reader.Seek(0, 0); err != nil {
		return err
	}
	if fileType == FileTIFF {
		if control.ReadExif != nil {
			if err := readTIFF(reader, control); err != nil {
				return err
			}
		}
	} else {
		if err := readJPEG(reader, control); err != nil {
			return err
		}
	}
	return nil
}

// ReadFile opens a file and processes it with Read.
func ReadFile(filename string, control ReadControl) error {
	reader, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer reader.Close()
	return Read(reader, control)
}

// Supported file formats for ReadFile and ReadWriteFile.
type FileFormat uint8

const (
	FileTIFF = 1
	FileJPEG = 2
)

// Determine type of stream. Anything not supported is an error. This will
// read a few bytes from the reader, changing the position.
func fileType(file io.Reader) (FileFormat, error) {
	buf := make([]byte, tiff.HeaderSize)
	if _, err := io.ReadFull(file, buf); err != nil {
		return 0, err
	}
	if jseg.IsJPEGHeader(buf) {
		return FileJPEG, nil
	}
	if validTIFF, _, _ := tiff.GetHeader(buf); validTIFF {
		return FileTIFF, nil
	}
	return 0, errors.New("File doesn't have a TIFF or JPEG header")
}

func readTIFF(reader io.Reader, control ReadControl) error {
	buf, err := ioutil.ReadAll(reader)
	if err != nil {
		return err
	}
	return readTIFFBuf(FileTIFF, 0, buf, control)
}

// State for the MPF image iterator.
type scanData struct {
	control ReadControl
}

// Function to be applied to each MPF image.
func (scan *scanData) MPFApply(reader io.ReadSeeker, index uint32, length uint32) error {
	if index > 0 {
		return readJPEGImage(index, reader, &jseg.MPFCheck{}, scan.control)
	}
	return nil
}

func readJPEG(reader io.ReadSeeker, control ReadControl) error {
	var index jseg.MPFGetIndex
	if err := readJPEGImage(0, reader, &index, control); err != nil {
		return err
	}
	if index.Index != nil {
		scandata := &scanData{}
		scandata.control = control
		err := index.Index.ImageIterate(reader, scandata)
		if err != nil {
			return err
		}
	}
	return nil
}

// Process a single image in a JPEG file. A file using the
// Multi-Picture Format extension will contain multiple images.
func readJPEGImage(imageIdx uint32, reader io.ReadSeeker, mpfProcessor jseg.MPFProcessor, control ReadControl) error {
	scanner, err := jseg.NewScanner(reader)
	if err != nil {
		return err
	}
	for {
		marker, buf, err := scanner.Scan()
		if err != nil {
			return err
		}
		if marker == jseg.SOS || marker == jseg.EOI {
			// No more metadata expected.
			return nil
		}
		if marker == jseg.APP0+1 && control.ReadExif != nil {
			isExif, next := GetHeader(buf)
			if isExif {
				if err := readTIFFBuf(FileJPEG, imageIdx, buf[next:], control); err != nil {
					return err
				}
			}
		}
		if marker == jseg.APP0+2 {
			_, _, err := mpfProcessor.ProcessAPP2(nil, reader, buf)
			if err != nil {
				return err
			}
		}
	}
}

func readTIFFBuf(format FileFormat, imageIdx uint32, buf []byte, control ReadControl) error {
	exif, err := GetExifTree(buf)
	if err != nil {
		return err
	}
	return control.ReadExif.ReadExif(format, imageIdx, *exif)
}

// Control structure for ReadWrite and ReadWriteFile, with optional callbacks.
type ReadWriteControl struct {
	ReadWriteExif ReadWriteExif // Process Exif tree, or nil.
	ExifRequired ExifRequired   // Check whether Exif block should be added if not present.

	// Additional callbacks could be added, e.g., for processing
	// other types of metadata, JPEG blocks, or full MPF trees.
}

type ReadWriteExif interface {
	// Callback for processing Exif data, read-write. In the case
	// of TIFF files, this will be called once on the entire TIFF
	// tree. For JPEG files, it will be called on the Exif segment
	// for each image in the file (multiple images are supported
	// via Multi-Picture Format, MPF). The data can be returned
	// modified or unmodified as desired.
	ReadWriteExif(format FileFormat, imageIdx uint32, exif Exif) (Exif, error)
}

type ExifRequired interface {
	// Callback to determine whether an Exif block should be
	// created if not already present for the specfied image
	// number. For a JPEG file, an APP1 segment will be created if
	// necessary. For JPEG or TIFF, an Exif IFD will be created
	// containing an ExifVersion field.
	ExifRequired(format FileFormat, imageIdx uint32) bool
}

// ReadWrite processes its input, which is expected to be an open image
// file in a supported format, currently JPEG or TIFF. It invokes
// any callbacks in the control structure.
func ReadWrite(reader io.ReadSeeker, writer io.WriteSeeker, control ReadWriteControl) error {
	fileType, err := fileType(reader)
	if err != nil {
		return err
	}
	if _, err := reader.Seek(0, 0); err != nil {
		return err
	}
	if fileType == FileTIFF {
		return readWriteTIFF(reader, writer, control)
	} else {
		return readWriteJPEG(reader, writer, control)
	}
}

// ReadWriteFile opens input and output files and processes them with ReadWrite.
func ReadWriteFile(infile, outfile string, control ReadWriteControl) error {
	reader, err := os.Open(infile)
	if err != nil {
		return err
	}
	defer reader.Close()
	writer, err := os.Create(outfile)
	if err != nil {
		return err
	}
	defer writer.Close()
	return ReadWrite(reader, writer, control)
}

func readWriteTIFF(infile io.Reader, outfile io.Writer, control ReadWriteControl) error {
	inbuf, err := ioutil.ReadAll(infile)
	if err != nil {
		return err
	}
	if inbuf == nil {
	}
	outbuf, err := readWriteTIFFBuf(FileTIFF, 0, inbuf, control)
	if err != nil {
		return err
	}
	_, err = outfile.Write(outbuf)
	return err
}

// State for MPF image iterator.
type iterData struct {
	writer     io.WriteSeeker
	newOffsets []uint32
	control    ReadWriteControl
}

// Function to be applied to each MPF image.
func (iter *iterData) MPFApply(reader io.ReadSeeker, index uint32, length uint32) error {
	if index > 0 {
		pos, err := iter.writer.Seek(0, io.SeekCurrent)
		if err != nil {
			return err
		}
		iter.newOffsets[index] = uint32(pos)
		return readWriteJPEGImage(index, reader, iter.writer, &jseg.MPFCheck{}, iter.control)
	}
	return nil
}

func readWriteJPEG(reader io.ReadSeeker, writer io.WriteSeeker, control ReadWriteControl) error {
	var mpfIndex jseg.MPFIndexRewriter
	if err := readWriteJPEGImage(0, reader, writer, &mpfIndex, control); err != nil {
		return err
	}
	if mpfIndex.Tree != nil {
		var iter iterData
		iter.writer = writer
		iter.control = control
		index := mpfIndex.Index
		iter.newOffsets = make([]uint32, len(index.ImageOffsets))
		err := index.ImageIterate(reader, &iter)
		if err != nil {
			return err
		}
		end, err := writer.Seek(0, io.SeekCurrent)
		if err != nil {
			return err
		}
		if err = jseg.RewriteMPF(writer, mpfIndex.Tree, mpfIndex.APP2WritePos, iter.newOffsets, uint32(end)); err != nil {
			return err
		}
	}
	return nil
}

// Process a single image in a JPEG file. A file using Multi-Picture
// Format will contain multiple images.
func readWriteJPEGImage(imageIdx uint32, reader io.ReadSeeker, writer io.WriteSeeker, mpfProcessor jseg.MPFProcessor, control ReadWriteControl) error {
	scanner, err := jseg.NewScanner(reader)
	if err != nil {
		return err
	}
	dumper, err := jseg.NewDumper(writer)
	if err != nil {
		return err
	}
	for {
		marker, buf, err := scanner.Scan()
		if err != nil {
			return err
		}
		if marker == jseg.APP0+1 {
			isExif, next := GetHeader(buf)
			if isExif {
				newTIFF, err := readWriteTIFFBuf(FileJPEG, imageIdx, buf[next:], control)
				if err != nil {
					return err
				}
				buf = append(header, newTIFF...)
			}

		}
		if marker == jseg.APP0+2 {
			_, buf, err = mpfProcessor.ProcessAPP2(writer, reader, buf)
			if err != nil {
				return err
			}
		}
		if err := dumper.Dump(marker, buf); err != nil {
			return err
		}
		if marker == jseg.EOI {
			return nil
		}
	}
}

// Create an Exif IFD and add it to a TIFF tree.
func addExifIFD(exif *Exif) {
	// Create the Exif IFD node.
	exifNode := tiff.NewIFDNode(tiff.ExifSpace)
	exifNode.Order = exif.TIFF.Order
	// Add the version field to the node.
	exifVersionData := make([]byte, 4)
	copy(exifVersionData, []byte("0230"))
	exifVersion := tiff.Field{Tag:ExifVersion, Type:tiff.UNDEFINED, Count:4, Data:exifVersionData}
	exifNode.AddFields([]tiff.Field{exifVersion})
	// Add a ExifIFD field to the TIFF IFD. Data will be set to the right
	// offset when the tree is serialized.
	exifIFDData := make([]byte, 4)
	tiffNode := exif.TIFF
	tiffNode.AddFields([]tiff.Field{{Tag:tiff.ExifIFD, Type:tiff.LONG, Count:1, Data:exifIFDData}})
	// Add the Exif node to the TIFF node's sub-IFD list.
	subIFD := tiff.SubIFD{tiff.ExifIFD, exifNode}
	tiffNode.SubIFDs = append(tiffNode.SubIFDs, subIFD)
	// Set the pointer in the Exif struct.
	exif.Exif = exifNode
}

func readWriteTIFFBuf(format FileFormat, imageIdx uint32, buf []byte, control ReadWriteControl) ([]byte, error) {
	exif, err := GetExifTree(buf)
	if err != nil {
		return nil, err
	}
	exif.TIFF.Fix()
	if exif.Exif == nil && control.ExifRequired != nil && control.ExifRequired.ExifRequired(format, imageIdx) == true {
		addExifIFD(exif)
	}
	exifOut := *exif
	if control.ReadWriteExif != nil {
		exifOut, err = control.ReadWriteExif.ReadWriteExif(format, imageIdx, *exif)
		if err != nil {
			return nil, err
		}
	}
	if err = exifOut.CheckMakerNote(); err != nil {
		return nil, err
	}
	if err = exifOut.MakerNoteComplexities(); err != nil {
		return nil, err
	}
	fileSize := tiff.HeaderSize + exifOut.TreeSize()
	outbuf := make([]byte, fileSize)
	tiff.PutHeader(outbuf, exifOut.TIFF.Order, tiff.HeaderSize)
	_, err = exifOut.TIFF.PutIFDTree(outbuf, tiff.HeaderSize)
	return outbuf, err
}
<commit_msg>go fmt<commit_after>package exif44

import (
	"errors"
	jseg "github.com/garyhouston/jpegsegs"
	tiff "github.com/garyhouston/tiff66"
	"io"
	"io/ioutil"
	"os"
)

// Control structure for Read and ReadFile, with optional callbacks.
type ReadControl struct {
	ReadExif ReadExif // Process Exif tree, or nil.
	// Additional callbacks could be added, e.g., for processing
	// other types of metadata, JPEG blocks, or full MPF trees.
}

type ReadExif interface {
	// Callback for processing Exif data, read-only. In the case
	// of TIFF files, this will be called once on the entire TIFF
	// tree. For JPEG files, it will be called on the Exif segment
	// for each image in the file (multiple images are supported
	// via Multi-Picture Format, MPF).
	ReadExif(format FileFormat, imageIdx uint32, exif Exif) error
}

// Read processes its input, which is expected to be an open image
// file in a supported format, currently JPEG or TIFF. It invokes
// any callbacks in the control structure.
func Read(reader io.ReadSeeker, control ReadControl) error {
	fileType, err := fileType(reader)
	if err != nil {
		return err
	}
	if _, err := reader.Seek(0, 0); err != nil {
		return err
	}
	if fileType == FileTIFF {
		if control.ReadExif != nil {
			if err := readTIFF(reader, control); err != nil {
				return err
			}
		}
	} else {
		if err := readJPEG(reader, control); err != nil {
			return err
		}
	}
	return nil
}

// ReadFile opens a file and processes it with Read.
func ReadFile(filename string, control ReadControl) error {
	reader, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer reader.Close()
	return Read(reader, control)
}

// Supported file formats for ReadFile and ReadWriteFile.
type FileFormat uint8

const (
	FileTIFF = 1
	FileJPEG = 2
)

// Determine type of stream. Anything not supported is an error. This will
// read a few bytes from the reader, changing the position.
func fileType(file io.Reader) (FileFormat, error) {
	buf := make([]byte, tiff.HeaderSize)
	if _, err := io.ReadFull(file, buf); err != nil {
		return 0, err
	}
	if jseg.IsJPEGHeader(buf) {
		return FileJPEG, nil
	}
	if validTIFF, _, _ := tiff.GetHeader(buf); validTIFF {
		return FileTIFF, nil
	}
	return 0, errors.New("File doesn't have a TIFF or JPEG header")
}

func readTIFF(reader io.Reader, control ReadControl) error {
	buf, err := ioutil.ReadAll(reader)
	if err != nil {
		return err
	}
	return readTIFFBuf(FileTIFF, 0, buf, control)
}

// State for the MPF image iterator.
type scanData struct {
	control ReadControl
}

// Function to be applied to each MPF image.
func (scan *scanData) MPFApply(reader io.ReadSeeker, index uint32, length uint32) error {
	if index > 0 {
		return readJPEGImage(index, reader, &jseg.MPFCheck{}, scan.control)
	}
	return nil
}

func readJPEG(reader io.ReadSeeker, control ReadControl) error {
	var index jseg.MPFGetIndex
	if err := readJPEGImage(0, reader, &index, control); err != nil {
		return err
	}
	if index.Index != nil {
		scandata := &scanData{}
		scandata.control = control
		err := index.Index.ImageIterate(reader, scandata)
		if err != nil {
			return err
		}
	}
	return nil
}

// Process a single image in a JPEG file. A file using the
// Multi-Picture Format extension will contain multiple images.
func readJPEGImage(imageIdx uint32, reader io.ReadSeeker, mpfProcessor jseg.MPFProcessor, control ReadControl) error {
	scanner, err := jseg.NewScanner(reader)
	if err != nil {
		return err
	}
	for {
		marker, buf, err := scanner.Scan()
		if err != nil {
			return err
		}
		if marker == jseg.SOS || marker == jseg.EOI {
			// No more metadata expected.
			return nil
		}
		if marker == jseg.APP0+1 && control.ReadExif != nil {
			isExif, next := GetHeader(buf)
			if isExif {
				if err := readTIFFBuf(FileJPEG, imageIdx, buf[next:], control); err != nil {
					return err
				}
			}
		}
		if marker == jseg.APP0+2 {
			_, _, err := mpfProcessor.ProcessAPP2(nil, reader, buf)
			if err != nil {
				return err
			}
		}
	}
}

func readTIFFBuf(format FileFormat, imageIdx uint32, buf []byte, control ReadControl) error {
	exif, err := GetExifTree(buf)
	if err != nil {
		return err
	}
	return control.ReadExif.ReadExif(format, imageIdx, *exif)
}

// Control structure for ReadWrite and ReadWriteFile, with optional callbacks.
type ReadWriteControl struct {
	ReadWriteExif ReadWriteExif // Process Exif tree, or nil.
	ExifRequired  ExifRequired  // Check whether Exif block should be added if not present.

	// Additional callbacks could be added, e.g., for processing
	// other types of metadata, JPEG blocks, or full MPF trees.
}

type ReadWriteExif interface {
	// Callback for processing Exif data, read-write. In the case
	// of TIFF files, this will be called once on the entire TIFF
	// tree. For JPEG files, it will be called on the Exif segment
	// for each image in the file (multiple images are supported
	// via Multi-Picture Format, MPF). The data can be returned
	// modified or unmodified as desired.
	ReadWriteExif(format FileFormat, imageIdx uint32, exif Exif) (Exif, error)
}

type ExifRequired interface {
	// Callback to determine whether an Exif block should be
	// created if not already present for the specfied image
	// number. For a JPEG file, an APP1 segment will be created if
	// necessary. For JPEG or TIFF, an Exif IFD will be created
	// containing an ExifVersion field.
	ExifRequired(format FileFormat, imageIdx uint32) bool
}

// ReadWrite processes its input, which is expected to be an open image
// file in a supported format, currently JPEG or TIFF. It invokes
// any callbacks in the control structure.
func ReadWrite(reader io.ReadSeeker, writer io.WriteSeeker, control ReadWriteControl) error {
	fileType, err := fileType(reader)
	if err != nil {
		return err
	}
	if _, err := reader.Seek(0, 0); err != nil {
		return err
	}
	if fileType == FileTIFF {
		return readWriteTIFF(reader, writer, control)
	} else {
		return readWriteJPEG(reader, writer, control)
	}
}

// ReadWriteFile opens input and output files and processes them with ReadWrite.
func ReadWriteFile(infile, outfile string, control ReadWriteControl) error {
	reader, err := os.Open(infile)
	if err != nil {
		return err
	}
	defer reader.Close()
	writer, err := os.Create(outfile)
	if err != nil {
		return err
	}
	defer writer.Close()
	return ReadWrite(reader, writer, control)
}

func readWriteTIFF(infile io.Reader, outfile io.Writer, control ReadWriteControl) error {
	inbuf, err := ioutil.ReadAll(infile)
	if err != nil {
		return err
	}
	if inbuf == nil {
	}
	outbuf, err := readWriteTIFFBuf(FileTIFF, 0, inbuf, control)
	if err != nil {
		return err
	}
	_, err = outfile.Write(outbuf)
	return err
}

// State for MPF image iterator.
type iterData struct {
	writer     io.WriteSeeker
	newOffsets []uint32
	control    ReadWriteControl
}

// Function to be applied to each MPF image.
func (iter *iterData) MPFApply(reader io.ReadSeeker, index uint32, length uint32) error {
	if index > 0 {
		pos, err := iter.writer.Seek(0, io.SeekCurrent)
		if err != nil {
			return err
		}
		iter.newOffsets[index] = uint32(pos)
		return readWriteJPEGImage(index, reader, iter.writer, &jseg.MPFCheck{}, iter.control)
	}
	return nil
}

func readWriteJPEG(reader io.ReadSeeker, writer io.WriteSeeker, control ReadWriteControl) error {
	var mpfIndex jseg.MPFIndexRewriter
	if err := readWriteJPEGImage(0, reader, writer, &mpfIndex, control); err != nil {
		return err
	}
	if mpfIndex.Tree != nil {
		var iter iterData
		iter.writer = writer
		iter.control = control
		index := mpfIndex.Index
		iter.newOffsets = make([]uint32, len(index.ImageOffsets))
		err := index.ImageIterate(reader, &iter)
		if err != nil {
			return err
		}
		end, err := writer.Seek(0, io.SeekCurrent)
		if err != nil {
			return err
		}
		if err = jseg.RewriteMPF(writer, mpfIndex.Tree, mpfIndex.APP2WritePos, iter.newOffsets, uint32(end)); err != nil {
			return err
		}
	}
	return nil
}

// Process a single image in a JPEG file. A file using Multi-Picture
// Format will contain multiple images.
func readWriteJPEGImage(imageIdx uint32, reader io.ReadSeeker, writer io.WriteSeeker, mpfProcessor jseg.MPFProcessor, control ReadWriteControl) error {
	scanner, err := jseg.NewScanner(reader)
	if err != nil {
		return err
	}
	dumper, err := jseg.NewDumper(writer)
	if err != nil {
		return err
	}
	for {
		marker, buf, err := scanner.Scan()
		if err != nil {
			return err
		}
		if marker == jseg.APP0+1 {
			isExif, next := GetHeader(buf)
			if isExif {
				newTIFF, err := readWriteTIFFBuf(FileJPEG, imageIdx, buf[next:], control)
				if err != nil {
					return err
				}
				buf = append(header, newTIFF...)
			}

		}
		if marker == jseg.APP0+2 {
			_, buf, err = mpfProcessor.ProcessAPP2(writer, reader, buf)
			if err != nil {
				return err
			}
		}
		if err := dumper.Dump(marker, buf); err != nil {
			return err
		}
		if marker == jseg.EOI {
			return nil
		}
	}
}

// Create an Exif IFD and add it to a TIFF tree.
func addExifIFD(exif *Exif) {
	// Create the Exif IFD node.
	exifNode := tiff.NewIFDNode(tiff.ExifSpace)
	exifNode.Order = exif.TIFF.Order
	// Add the version field to the node.
	exifVersionData := make([]byte, 4)
	copy(exifVersionData, []byte("0230"))
	exifVersion := tiff.Field{Tag: ExifVersion, Type: tiff.UNDEFINED, Count: 4, Data: exifVersionData}
	exifNode.AddFields([]tiff.Field{exifVersion})
	// Add a ExifIFD field to the TIFF IFD. Data will be set to the right
	// offset when the tree is serialized.
	exifIFDData := make([]byte, 4)
	tiffNode := exif.TIFF
	tiffNode.AddFields([]tiff.Field{{Tag: tiff.ExifIFD, Type: tiff.LONG, Count: 1, Data: exifIFDData}})
	// Add the Exif node to the TIFF node's sub-IFD list.
	subIFD := tiff.SubIFD{tiff.ExifIFD, exifNode}
	tiffNode.SubIFDs = append(tiffNode.SubIFDs, subIFD)
	// Set the pointer in the Exif struct.
	exif.Exif = exifNode
}

func readWriteTIFFBuf(format FileFormat, imageIdx uint32, buf []byte, control ReadWriteControl) ([]byte, error) {
	exif, err := GetExifTree(buf)
	if err != nil {
		return nil, err
	}
	exif.TIFF.Fix()
	if exif.Exif == nil && control.ExifRequired != nil && control.ExifRequired.ExifRequired(format, imageIdx) == true {
		addExifIFD(exif)
	}
	exifOut := *exif
	if control.ReadWriteExif != nil {
		exifOut, err = control.ReadWriteExif.ReadWriteExif(format, imageIdx, *exif)
		if err != nil {
			return nil, err
		}
	}
	if err = exifOut.CheckMakerNote(); err != nil {
		return nil, err
	}
	if err = exifOut.MakerNoteComplexities(); err != nil {
		return nil, err
	}
	fileSize := tiff.HeaderSize + exifOut.TreeSize()
	outbuf := make([]byte, fileSize)
	tiff.PutHeader(outbuf, exifOut.TIFF.Order, tiff.HeaderSize)
	_, err = exifOut.TIFF.PutIFDTree(outbuf, tiff.HeaderSize)
	return outbuf, err
}
<|endoftext|>


<commit_before>package demux

import (
	"os"
	"sync"
	"syscall"
	"unsafe"
)

type fdset syscall.FdSet

func (s *fdset) Sys() *syscall.FdSet {
	return (*syscall.FdSet)(s)
}

func (s *fdset) Set(fd uintptr) {
	bits := 8 * unsafe.Sizeof(s.Bits[0])
	if fd >= bits*uintptr(len(s.Bits)) {
		panic("fdset: fd value too big")
	}
	n := fd / bits
	m := fd % bits
	s.Bits[n] |= 1 << m
}

func (s *fdset) IsSet(fd uintptr) bool {
	bits := 8 * unsafe.Sizeof(s.Bits[0])
	if fd >= bits*uintptr(len(s.Bits)) {
		panic("fdset: fd value too big")
	}
	n := fd / bits
	m := fd % bits
	return s.Bits[n]&(1<<m) != 0
}

type syn struct {
	pr, pw *os.File
	m      sync.Mutex
}

func (s *syn) pread() error {
	var b [1]byte
	_, err := s.pr.Read(b[:])
	return err
}

var nl = []byte{'\n'}

func (s *syn) pwrite() error {
	_, err := s.pw.Write(nl)
	return err
}

func newSyn() (*syn, error) {
	s := new(syn)
	var err error
	s.pr, s.pw, err = os.Pipe()
	if err != nil {
		return nil, err
	}
	return s, nil
}

func (s *syn) Close() {
	s.pw.Close()
	s.pr.Close()
}

// WaitRead returns true if f can be readed without blocking or false if not or
// error.
func (s *syn) WaitRead(f *os.File) (bool, error) {
	pfd := s.pr.Fd()
	ffd := f.Fd()
	nfd := 1
	if pfd < ffd {
		nfd += int(ffd)
	} else {
		nfd += int(pfd)
	}
	s.m.Lock()
	for {
		var r fdset
		r.Set(ffd)
		r.Set(pfd)
		n, err := syscall.Select(nfd, r.Sys(), nil, nil, nil)
		if err != nil {
			return false, err
		}
		if n > 0 {
			if r.IsSet(pfd) {
				// Command waits for access f.
				s.m.Unlock()
				return false, nil
			}
			return true, nil
		}
	}
}

func (s *syn) Done() {
	s.m.Unlock()
}

func (s *syn) WaitCmd() error {
	if err := s.pwrite(); err != nil {
		return err
	}
	s.m.Lock()
	return s.pread()
}

// Filter implements common functionality for all demux filters.
type Filter struct {
	data *os.File
	s    *syn
}

func newFilter(d Device, typ uintptr, p unsafe.Pointer, dvr bool) (*Filter, error) {
	f, err := os.Open(string(d))
	if err != nil {
		return nil, err
	}
	if _, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(f.Fd()),
		typ,
		uintptr(p),
	); e != 0 {
		return nil, e
	}
	if dvr {
		return &Filter{data: f}, nil
	}
	s, err := newSyn()
	if err != nil {
		return nil, err
	}
	return &Filter{data: f, s: s}, nil
}

func (f *Filter) Close() error {
	if f.s != nil {
		f.s.Close()
	}
	return f.data.Close()
}

func (f *Filter) Read(buf []byte) (int, error) {
	if f.s != nil {
		if ok, err := f.s.WaitRead(f.data); !ok {
			return 0, err
		}
		defer f.s.Done()
	}
	return f.data.Read(buf)
}

func (f *Filter) start() error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL, uintptr(f.data.Fd()), _DMX_START, 0,
	)
	if e != 0 {
		return e
	}
	return nil
}

func (f *Filter) stop() error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL, uintptr(f.data.Fd()), _DMX_STOP, 0,
	)
	if e != 0 {
		return e
	}
	return nil
}

func (f *Filter) setBufferSize(n int) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL, uintptr(f.data.Fd()),
		_DMX_SET_BUFFER_SIZE, uintptr(n),
	)
	if e != 0 {
		return e
	}
	return nil
}

func (f *Filter) addPid(pid int16) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(f.data.Fd()),
		_DMX_ADD_PID,
		uintptr(unsafe.Pointer(&pid)),
	)
	if e != 0 {
		return e
	}
	return nil
}

func (f *Filter) delPid(pid int16) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(f.data.Fd()),
		_DMX_REMOVE_PID,
		uintptr(unsafe.Pointer(&pid)),
	)
	if e != 0 {
		return e
	}
	return nil
}

func (f *Filter) Start() error {
	if f.s != nil {
		if err := f.s.WaitCmd(); err != nil {
			return err
		}
		defer f.s.Done()
	}
	return f.start()
}

func (f *Filter) Stop() error {
	if f.s != nil {
		if err := f.s.WaitCmd(); err != nil {
			return err
		}
		defer f.s.Done()
	}
	return f.stop()
}

func (f *Filter) SetBufferSize(n int) error {
	if f.s != nil {
		if err := f.s.WaitCmd(); err != nil {
			return err
		}
		defer f.s.Done()
	}
	return f.setBufferSize(n)
}

func (f *Filter) AddPid(pid int16) error {
	if f.s != nil {
		if err := f.s.WaitCmd(); err != nil {
			return err
		}
		defer f.s.Done()
	}
	return f.addPid(pid)
}

func (f *Filter) DelPid(pid int16) error {
	if f.s != nil {
		if err := f.s.WaitCmd(); err != nil {
			return err
		}
		defer f.s.Done()
	}
	return f.delPid(pid)
}
<commit_msg>linuxdvb: Change fdset panic message.<commit_after>package demux

import (
	"os"
	"sync"
	"syscall"
	"unsafe"
)

type fdset syscall.FdSet

func (s *fdset) Sys() *syscall.FdSet {
	return (*syscall.FdSet)(s)
}

func (s *fdset) Set(fd uintptr) {
	bits := 8 * unsafe.Sizeof(s.Bits[0])
	if fd >= bits*uintptr(len(s.Bits)) {
		panic("fdset: fd out of range")
	}
	n := fd / bits
	m := fd % bits
	s.Bits[n] |= 1 << m
}

func (s *fdset) IsSet(fd uintptr) bool {
	bits := 8 * unsafe.Sizeof(s.Bits[0])
	if fd >= bits*uintptr(len(s.Bits)) {
		panic("fdset: fd out of range")
	}
	n := fd / bits
	m := fd % bits
	return s.Bits[n]&(1<<m) != 0
}

type syn struct {
	pr, pw *os.File
	m      sync.Mutex
}

func (s *syn) pread() error {
	var b [1]byte
	_, err := s.pr.Read(b[:])
	return err
}

var nl = []byte{'\n'}

func (s *syn) pwrite() error {
	_, err := s.pw.Write(nl)
	return err
}

func newSyn() (*syn, error) {
	s := new(syn)
	var err error
	s.pr, s.pw, err = os.Pipe()
	if err != nil {
		return nil, err
	}
	return s, nil
}

func (s *syn) Close() {
	s.pw.Close()
	s.pr.Close()
}

// WaitRead returns true if f can be readed without blocking or false if not or
// error.
func (s *syn) WaitRead(f *os.File) (bool, error) {
	pfd := s.pr.Fd()
	ffd := f.Fd()
	nfd := 1
	if pfd < ffd {
		nfd += int(ffd)
	} else {
		nfd += int(pfd)
	}
	s.m.Lock()
	for {
		var r fdset
		r.Set(ffd)
		r.Set(pfd)
		n, err := syscall.Select(nfd, r.Sys(), nil, nil, nil)
		if err != nil {
			return false, err
		}
		if n > 0 {
			if r.IsSet(pfd) {
				// Command waits for access f.
				s.m.Unlock()
				return false, nil
			}
			return true, nil
		}
	}
}

func (s *syn) Done() {
	s.m.Unlock()
}

func (s *syn) WaitCmd() error {
	if err := s.pwrite(); err != nil {
		return err
	}
	s.m.Lock()
	return s.pread()
}

// Filter implements common functionality for all demux filters.
type Filter struct {
	data *os.File
	s    *syn
}

func newFilter(d Device, typ uintptr, p unsafe.Pointer, dvr bool) (*Filter, error) {
	f, err := os.Open(string(d))
	if err != nil {
		return nil, err
	}
	if _, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(f.Fd()),
		typ,
		uintptr(p),
	); e != 0 {
		return nil, e
	}
	if dvr {
		return &Filter{data: f}, nil
	}
	s, err := newSyn()
	if err != nil {
		return nil, err
	}
	return &Filter{data: f, s: s}, nil
}

func (f *Filter) Close() error {
	if f.s != nil {
		f.s.Close()
	}
	return f.data.Close()
}

func (f *Filter) Read(buf []byte) (int, error) {
	if f.s != nil {
		if ok, err := f.s.WaitRead(f.data); !ok {
			return 0, err
		}
		defer f.s.Done()
	}
	return f.data.Read(buf)
}

func (f *Filter) start() error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL, uintptr(f.data.Fd()), _DMX_START, 0,
	)
	if e != 0 {
		return e
	}
	return nil
}

func (f *Filter) stop() error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL, uintptr(f.data.Fd()), _DMX_STOP, 0,
	)
	if e != 0 {
		return e
	}
	return nil
}

func (f *Filter) setBufferSize(n int) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL, uintptr(f.data.Fd()),
		_DMX_SET_BUFFER_SIZE, uintptr(n),
	)
	if e != 0 {
		return e
	}
	return nil
}

func (f *Filter) addPid(pid int16) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(f.data.Fd()),
		_DMX_ADD_PID,
		uintptr(unsafe.Pointer(&pid)),
	)
	if e != 0 {
		return e
	}
	return nil
}

func (f *Filter) delPid(pid int16) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(f.data.Fd()),
		_DMX_REMOVE_PID,
		uintptr(unsafe.Pointer(&pid)),
	)
	if e != 0 {
		return e
	}
	return nil
}

func (f *Filter) Start() error {
	if f.s != nil {
		if err := f.s.WaitCmd(); err != nil {
			return err
		}
		defer f.s.Done()
	}
	return f.start()
}

func (f *Filter) Stop() error {
	if f.s != nil {
		if err := f.s.WaitCmd(); err != nil {
			return err
		}
		defer f.s.Done()
	}
	return f.stop()
}

func (f *Filter) SetBufferSize(n int) error {
	if f.s != nil {
		if err := f.s.WaitCmd(); err != nil {
			return err
		}
		defer f.s.Done()
	}
	return f.setBufferSize(n)
}

func (f *Filter) AddPid(pid int16) error {
	if f.s != nil {
		if err := f.s.WaitCmd(); err != nil {
			return err
		}
		defer f.s.Done()
	}
	return f.addPid(pid)
}

func (f *Filter) DelPid(pid int16) error {
	if f.s != nil {
		if err := f.s.WaitCmd(); err != nil {
			return err
		}
		defer f.s.Done()
	}
	return f.delPid(pid)
}
<|endoftext|>


<commit_before>package server_test

// TODO: review the http testing
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
	"time"

	"github.com/gorilla/websocket"
	"github.com/mch1307/gomotics/log"
	. "github.com/mch1307/gomotics/server"
	"github.com/mch1307/gomotics/testutil"
	"github.com/mch1307/gomotics/types"
)

var baseUrl string
var origin = "http://localhost/"
var url = "ws://localhost:8081/events"

func initStub() {
	if !testutil.IsStubRunning() {
		fmt.Println("Stub not running")
		testutil.InitStubNHC()
	}
}

func init() {
	fmt.Println("starting server test")
	baseUrl = "http://" + testutil.ConnectHost + ":8081"
	initStub()
}

func TestHealth(t *testing.T) {
	initStub()
	req, err := http.NewRequest("GET", baseUrl+"/health", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(Health)
	handler.ServeHTTP(rr, req)
	if rr.Body.String() != HealthMsg {
		t.Errorf("health test failed: got %v, expect: %v", rr.Body.String(), HealthMsg)
	}
}

// TODO: add more test cases (test non existing item)
func Test_getNhcItem(t *testing.T) {
	initStub()
	req, err := http.NewRequest("GET", baseUrl+"/api/v1/nhc/99", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(GetNhcItem)
	handler.ServeHTTP(rr, req)
	expected := "light"
	var res types.Item
	json.Unmarshal(rr.Body.Bytes(), &res)
	if res.Name != expected {
		t.Errorf("getNhcItem failed: got %v, expect: %v", res, expected)
	}
}

func Test_getNhcItems(t *testing.T) {
	initStub()
	req, err := http.NewRequest("GET", baseUrl+"/api/v1/nhc", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(GetNhcItems)
	handler.ServeHTTP(rr, req)
	var found bool
	expected := "light"
	var res []types.Item
	json.Unmarshal(rr.Body.Bytes(), &res)
	for _, val := range res {
		if val.ID == 0 {
			if val.Name == expected {
				found = true
			}
		}
	}
	if !found {
		t.Error("GetNhcItems failed, expected light record not found")
	}
}

/* func Test_nhcCmd(t *testing.T) {
	initStub()
	expected := "Success"
	url := baseUrl + "/api/v1/nhc/1/100"
	hCli := http.Client{
		Timeout: time.Second * 2,
	}
	req, err := http.NewRequest(http.MethodPost, url, nil)
	if err != nil {
		t.Fatal(err)
	}
	//	req.Header.Set("User-Agent", "Test_nhcCmd")
	rsp, getErr := hCli.Do(req)
	if getErr != nil {
		fmt.Println("Get err ", err)
	}
	got, readErr := ioutil.ReadAll(rsp.Body)
	if readErr != nil {
		fmt.Println("Read err: ", readErr)
	}
	//defer rsp.Body.Close()
	if string(got) != expected {
		t.Errorf("Test_nhcCmd failed, expecting %v, got %v", expected, string(got))
	}
} */

func TestGetNhcInfo(t *testing.T) {
	initStub()
	expected := "1.10.0.34209"
	url := baseUrl + "/api/v1/nhc/info"
	hCli := http.Client{
		Timeout: time.Second * 2,
	}
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Header.Set("User-Agent", "Test_nhcCmd")
	rsp, getErr := hCli.Do(req)
	if getErr != nil {
		fmt.Println(err)
	}
	got, readErr := ioutil.ReadAll(rsp.Body)
	if readErr != nil {
		fmt.Println("Read err: ", readErr)
	}
	var res types.NHCSystemInfo
	json.Unmarshal(got, &res)
	//defer rsp.Body.Close()
	if res.Swversion != expected {
		t.Errorf("TestGetNhcInfo failed, expecting %v, got %v", expected, res.Swversion)
	}
}

func wsDial(url string) (wsConn *websocket.Conn, ok bool, err error) {
	webS, _, err := websocket.DefaultDialer.Dial(url, nil)
	if err != nil {
		fmt.Println("error connecting ws", err)
		return webS, false, err
	}
	//fmt.Println("websocket connect ok")
	return webS, true, nil
}

func Test_tWS(t *testing.T) {
	retry := 0
	ok := false
	ctl := 0
	var err error
	var wsConn *websocket.Conn
	tests := []struct {
		name       string
		id         int
		exName     string
		exLocation string
		exState    int
	}{
		{"action0", 3, "light", "Living Room", 0},
		{"action1", 1, "power switch", "Kitchen", 100},
	}
	//fmt.Println("# tests: ", len(tests))
	var msg types.Item
	//time.Sleep(time.Second * 2)
	if wsConn, ok, err = wsDial(url); !ok {
		if retry < 11 {
			retry++
			fmt.Println("Retrying websocket connect due to error: ", err)
			fmt.Println("Attempt # ", retry)
			time.Sleep(time.Second * 1)
			Test_tWS(t)
		} else {
			fmt.Println("Could not connect after 10 attempts, err: ", err)
			return
		}
	}

	//time.Sleep(time.Second * 2)
	go func() {
		//defer ws.Close()
		//var tmp = make([]byte, 512)
		for {
			_, tmp, err := wsConn.ReadMessage()
			if err != nil {
				log.Error("read:", err)
				return
			}
			log.Error(string(tmp))
			err = json.Unmarshal(bytes.TrimSpace(tmp), &msg)
			if err != nil {
				log.Error("err parsing: ", err)
				log.Error(string(tmp))
			}
			//fmt.Println("ws reads ", msg)

		}
	}()

	time.Sleep(time.Second * 1)
	for _, tt := range tests {
		fmt.Println("start test ", tt.name)
		//ws.WriteMessage(websocket.PingMessage, nil)
		/* 		cmd := testutil.MyCmd
		   		cmd.ID = tt.id
		   		cmd.Value = tt.exState */
		//fmt.Println(cmd)
		time.Sleep(time.Millisecond * 500)
		var evts []types.Event
		var evt types.Event
		evt.ID = tt.id
		evt.Value = tt.exState
		evts = append(evts, evt)
		var nhcMessage types.Message
		nhcMessage.Event = "listactions"
		nhcMessage.Data, _ = json.Marshal(&evts)
		//Value = tt.exState
		//fmt.Println("send to router: ", &nhcMessage)
		Route(&nhcMessage)
		//db.ProcessEvent(evt)
		time.Sleep(time.Millisecond * 500)

		//fmt.Println("msg ", msg.ID)
		if msg.ID != tt.id || (msg.State != tt.exState) {
			t.Error("test failed  ", tt.name, tt.id, msg.ID, tt.exName, msg.Name, tt.exState, msg.State)
		}
		ctl++
	}
	//defer wsConn.Close()
	//fmt.Println("tests ok: ", ctl)
}

func stubNHCTCP() {
	// listen to incoming tcp connections
	l, err := net.Listen("tcp", "0.0.0.0:8000")
	if err != nil {
		fmt.Println(err)
	}
	defer l.Close()
	_, err = l.Accept()
	if err != nil {
		fmt.Println(err)
	}
}

func stubNHCUDP() {
	// listen to incoming udp packets
	fmt.Println("starting UDP stub")
	pc, err := net.ListenPacket("udp", "0.0.0.0:10000")
	if err != nil {
		log.Fatal(err)
	}
	defer pc.Close()

	//simple read
	buffer := make([]byte, 1024)
	var addr net.Addr
	_, addr, _ = pc.ReadFrom(buffer)

	//simple write
	pc.WriteTo([]byte("NHC Stub"), addr)
}

func getOutboundIP() net.IP {
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	localAddr := conn.LocalAddr().(*net.UDPAddr)

	return localAddr.IP
}

func TestDiscover(t *testing.T) {

	tests := []struct {
		name string
		want net.IP
	}{
		{"no nhc on LAN", nil},
		//{"stub nhc", getOutboundIP()},
	}
	portCheckIteration := 0
	for _, tt := range tests {
		fmt.Println("starting test ", tt.name)
		if tt.want != nil {
			go stubNHCUDP()
			go stubNHCTCP()
		}
		t.Run(tt.name, func(t *testing.T) {
		GotoTestPort:
			if testutil.IsTCPPortAvailable(18043) {
				if got := Discover(); !reflect.DeepEqual(got, tt.want) {
					t.Errorf("Discover() = %v, want %v", got, tt.want)
				}
			} else {
				portCheckIteration++
				if portCheckIteration < 21 {
					fmt.Printf("UDP 18043 busy, %v retry", portCheckIteration)
					time.Sleep(time.Millisecond * 500)
					goto GotoTestPort
				} else {
					t.Error("Discover failed to get UDP port 18043, test failed")
				}

			}
		})
	}
}
<commit_msg>add TestMain for stubs<commit_after>package server_test

// TODO: review the http testing
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
	"time"

	"github.com/gorilla/websocket"
	"github.com/mch1307/gomotics/log"
	. "github.com/mch1307/gomotics/server"
	"github.com/mch1307/gomotics/testutil"
	"github.com/mch1307/gomotics/types"
)

var baseUrl string
var origin = "http://localhost/"
var url = "ws://localhost:8081/events"

func TestMain(t *testing.T) {
	if !testutil.IsStubRunning() {
		fmt.Println("Stub not running")
		testutil.InitStubNHC()
	}
}
func initStub() {
	if !testutil.IsStubRunning() {
		fmt.Println("Stub not running")
		testutil.InitStubNHC()
	}
}

func init() {
	fmt.Println("starting server test")
	baseUrl = "http://" + testutil.ConnectHost + ":8081"
	initStub()
}

func TestHealth(t *testing.T) {
	initStub()
	req, err := http.NewRequest("GET", baseUrl+"/health", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(Health)
	handler.ServeHTTP(rr, req)
	if rr.Body.String() != HealthMsg {
		t.Errorf("health test failed: got %v, expect: %v", rr.Body.String(), HealthMsg)
	}
}

// TODO: add more test cases (test non existing item)
func Test_getNhcItem(t *testing.T) {
	initStub()
	req, err := http.NewRequest("GET", baseUrl+"/api/v1/nhc/99", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(GetNhcItem)
	handler.ServeHTTP(rr, req)
	expected := "light"
	var res types.Item
	json.Unmarshal(rr.Body.Bytes(), &res)
	if res.Name != expected {
		t.Errorf("getNhcItem failed: got %v, expect: %v", res, expected)
	}
}

func Test_getNhcItems(t *testing.T) {
	initStub()
	req, err := http.NewRequest("GET", baseUrl+"/api/v1/nhc", nil)
	if err != nil {
		t.Fatal(err)
	}
	rr := httptest.NewRecorder()
	handler := http.HandlerFunc(GetNhcItems)
	handler.ServeHTTP(rr, req)
	var found bool
	expected := "light"
	var res []types.Item
	json.Unmarshal(rr.Body.Bytes(), &res)
	for _, val := range res {
		if val.ID == 0 {
			if val.Name == expected {
				found = true
			}
		}
	}
	if !found {
		t.Error("GetNhcItems failed, expected light record not found")
	}
}

func Test_nhcCmd(t *testing.T) {
	initStub()
	expected := "Success"
	url := baseUrl + "/api/v1/nhc/1/100"
	hCli := http.Client{
		Timeout: time.Second * 2,
	}
	req, err := http.NewRequest(http.MethodPost, url, nil)
	if err != nil {
		t.Fatal(err)
	}
	//	req.Header.Set("User-Agent", "Test_nhcCmd")
	rsp, getErr := hCli.Do(req)
	if getErr != nil {
		fmt.Println("Get err ", err)
	}
	got, readErr := ioutil.ReadAll(rsp.Body)
	if readErr != nil {
		fmt.Println("Read err: ", readErr)
	}
	//defer rsp.Body.Close()
	if string(got) != expected {
		t.Errorf("Test_nhcCmd failed, expecting %v, got %v", expected, string(got))
	}
}

func TestGetNhcInfo(t *testing.T) {
	initStub()
	expected := "1.10.0.34209"
	url := baseUrl + "/api/v1/nhc/info"
	hCli := http.Client{
		Timeout: time.Second * 2,
	}
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		t.Fatal(err)
	}
	req.Header.Set("User-Agent", "Test_nhcCmd")
	rsp, getErr := hCli.Do(req)
	if getErr != nil {
		fmt.Println(err)
	}
	got, readErr := ioutil.ReadAll(rsp.Body)
	if readErr != nil {
		fmt.Println("Read err: ", readErr)
	}
	var res types.NHCSystemInfo
	json.Unmarshal(got, &res)
	//defer rsp.Body.Close()
	if res.Swversion != expected {
		t.Errorf("TestGetNhcInfo failed, expecting %v, got %v", expected, res.Swversion)
	}
}

func wsDial(url string) (wsConn *websocket.Conn, ok bool, err error) {
	webS, _, err := websocket.DefaultDialer.Dial(url, nil)
	if err != nil {
		fmt.Println("error connecting ws", err)
		return webS, false, err
	}
	//fmt.Println("websocket connect ok")
	return webS, true, nil
}

func Test_tWS(t *testing.T) {
	retry := 0
	ok := false
	ctl := 0
	var err error
	var wsConn *websocket.Conn
	tests := []struct {
		name       string
		id         int
		exName     string
		exLocation string
		exState    int
	}{
		{"action0", 3, "light", "Living Room", 0},
		{"action1", 1, "power switch", "Kitchen", 100},
	}
	//fmt.Println("# tests: ", len(tests))
	var msg types.Item
	//time.Sleep(time.Second * 2)
	if wsConn, ok, err = wsDial(url); !ok {
		if retry < 11 {
			retry++
			fmt.Println("Retrying websocket connect due to error: ", err)
			fmt.Println("Attempt # ", retry)
			time.Sleep(time.Second * 1)
			Test_tWS(t)
		} else {
			fmt.Println("Could not connect after 10 attempts, err: ", err)
			return
		}
	}

	//time.Sleep(time.Second * 2)
	go func() {
		//defer ws.Close()
		//var tmp = make([]byte, 512)
		for {
			_, tmp, err := wsConn.ReadMessage()
			if err != nil {
				log.Error("read:", err)
				return
			}
			log.Error(string(tmp))
			err = json.Unmarshal(bytes.TrimSpace(tmp), &msg)
			if err != nil {
				log.Error("err parsing: ", err)
				log.Error(string(tmp))
			}
			//fmt.Println("ws reads ", msg)

		}
	}()

	time.Sleep(time.Second * 1)
	for _, tt := range tests {
		fmt.Println("start test ", tt.name)
		//ws.WriteMessage(websocket.PingMessage, nil)
		/* 		cmd := testutil.MyCmd
		   		cmd.ID = tt.id
		   		cmd.Value = tt.exState */
		//fmt.Println(cmd)
		time.Sleep(time.Millisecond * 500)
		var evts []types.Event
		var evt types.Event
		evt.ID = tt.id
		evt.Value = tt.exState
		evts = append(evts, evt)
		var nhcMessage types.Message
		nhcMessage.Event = "listactions"
		nhcMessage.Data, _ = json.Marshal(&evts)
		//Value = tt.exState
		//fmt.Println("send to router: ", &nhcMessage)
		Route(&nhcMessage)
		//db.ProcessEvent(evt)
		time.Sleep(time.Millisecond * 500)

		//fmt.Println("msg ", msg.ID)
		if msg.ID != tt.id || (msg.State != tt.exState) {
			t.Error("test failed  ", tt.name, tt.id, msg.ID, tt.exName, msg.Name, tt.exState, msg.State)
		}
		ctl++
	}
	//defer wsConn.Close()
	//fmt.Println("tests ok: ", ctl)
}

func stubNHCTCP() {
	// listen to incoming tcp connections
	l, err := net.Listen("tcp", "0.0.0.0:8000")
	if err != nil {
		fmt.Println(err)
	}
	defer l.Close()
	_, err = l.Accept()
	if err != nil {
		fmt.Println(err)
	}
}

func stubNHCUDP() {
	// listen to incoming udp packets
	fmt.Println("starting UDP stub")
	pc, err := net.ListenPacket("udp", "0.0.0.0:10000")
	if err != nil {
		log.Fatal(err)
	}
	defer pc.Close()

	//simple read
	buffer := make([]byte, 1024)
	var addr net.Addr
	_, addr, _ = pc.ReadFrom(buffer)

	//simple write
	pc.WriteTo([]byte("NHC Stub"), addr)
}

func getOutboundIP() net.IP {
	conn, err := net.Dial("udp", "8.8.8.8:80")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	localAddr := conn.LocalAddr().(*net.UDPAddr)

	return localAddr.IP
}

func TestDiscover(t *testing.T) {

	tests := []struct {
		name string
		want net.IP
	}{
		{"no nhc on LAN", nil},
		//{"stub nhc", getOutboundIP()},
	}
	portCheckIteration := 0
	for _, tt := range tests {
		fmt.Println("starting test ", tt.name)
		if tt.want != nil {
			go stubNHCUDP()
			go stubNHCTCP()
		}
		t.Run(tt.name, func(t *testing.T) {
		GotoTestPort:
			if testutil.IsTCPPortAvailable(18043) {
				if got := Discover(); !reflect.DeepEqual(got, tt.want) {
					t.Errorf("Discover() = %v, want %v", got, tt.want)
				}
			} else {
				portCheckIteration++
				if portCheckIteration < 21 {
					fmt.Printf("UDP 18043 busy, %v retry", portCheckIteration)
					time.Sleep(time.Millisecond * 500)
					goto GotoTestPort
				} else {
					t.Error("Discover failed to get UDP port 18043, test failed")
				}

			}
		})
	}
}
<|endoftext|>


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

package server

import (
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"sync"
	"testing"

	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/storage/engine"
	"github.com/cockroachdb/cockroach/util/log"
)

var (
	s              *server
	serverTestOnce sync.Once
)

func startServer() *server {
	serverTestOnce.Do(func() {
		// We update these with the actual port once the servers
		// have been launched for the purpose of this test.
		s, err := newServer("127.0.0.1:0", "", *maxOffset)
		if err != nil {
			log.Fatal(err)
		}
		engines := []engine.Engine{engine.NewInMem(proto.Attributes{}, 1<<20)}
		if _, err := BootstrapCluster("cluster-1", engines[0]); err != nil {
			log.Fatal(err)
		}
		err = s.start(engines, "", "127.0.0.1:0", true) // TODO(spencer): should shutdown server.
		if err != nil {
			log.Fatalf("Could not start server: %s", err)
		}
		// Update the configuration variables to reflect the actual
		// ports bound.
		*httpAddr = (*s.httpListener).Addr().String()
		*rpcAddr = s.rpc.Addr().String()
		log.Infof("Test server listening on http: %s, rpc: %s", *httpAddr, *rpcAddr)
	})
	return s
}

// createTestConfigFile creates a temporary file and writes the
// testConfig yaml data to it. The caller is responsible for
// removing it. Returns the filename for a subsequent call to
// os.Remove().
func createTestConfigFile(body string) string {
	f, err := ioutil.TempFile("", "test-config")
	if err != nil {
		log.Fatalf("failed to open temporary file: %v", err)
	}
	defer f.Close()
	f.Write([]byte(body))
	return f.Name()
}

// createTempDirs creates "count" temporary directories and returns
// the paths to each as a slice.
func createTempDirs(count int, t *testing.T) []string {
	tmp := make([]string, count)
	for i := 0; i < count; i++ {
		var err error
		if tmp[i], err = ioutil.TempDir("", "_server_test"); err != nil {
			t.Fatal(err)
		}
	}
	return tmp
}

// resetTestData recursively removes all files written to the
// directories specified as parameters.
func resetTestData(dirs []string) {
	for _, dir := range dirs {
		os.RemoveAll(dir)
	}
}

// TestInitEngine tests whether the data directory string is parsed correctly.
func TestInitEngine(t *testing.T) {
	tmp := createTempDirs(5, t)
	defer resetTestData(tmp)

	testCases := []struct {
		key       string           // data directory
		expAttrs  proto.Attributes // attributes for engine
		wantError bool             // do we expect an error from this key?
		isMem     bool             // is the engine in-memory?
	}{
		{"mem=1000", proto.Attributes{Attrs: []string{"mem"}}, false, true},
		{"ssd=1000", proto.Attributes{Attrs: []string{"ssd"}}, false, true},
		{fmt.Sprintf("ssd=%s", tmp[0]), proto.Attributes{Attrs: []string{"ssd"}}, false, false},
		{fmt.Sprintf("hdd=%s", tmp[1]), proto.Attributes{Attrs: []string{"hdd"}}, false, false},
		{fmt.Sprintf("mem=%s", tmp[2]), proto.Attributes{Attrs: []string{"mem"}}, false, false},
		{fmt.Sprintf("abc=%s", tmp[3]), proto.Attributes{Attrs: []string{"abc"}}, false, false},
		{fmt.Sprintf("hdd:7200rpm=%s", tmp[4]), proto.Attributes{Attrs: []string{"hdd", "7200rpm"}}, false, false},
		{"", proto.Attributes{}, true, false},
		{"  ", proto.Attributes{}, true, false},
		{"arbitrarystring", proto.Attributes{}, true, false},
		{"mem=", proto.Attributes{}, true, false},
		{"ssd=", proto.Attributes{}, true, false},
		{"hdd=", proto.Attributes{}, true, false},
	}
	for _, spec := range testCases {
		engines, err := initEngines(spec.key)
		if err == nil {
			if spec.wantError {
				t.Fatalf("invalid engine spec '%v' erroneously accepted: %+v", spec.key, spec)
			}
			if len(engines) != 1 {
				t.Fatalf("unexpected number of engines: %d: %+v", len(engines), spec)
			}
			e := engines[0]
			if e.Attrs().SortedString() != spec.expAttrs.SortedString() {
				t.Errorf("wrong engine attributes, expected %v but got %v: %+v", spec.expAttrs, e.Attrs(), spec)
			}
			_, ok := e.(*engine.InMem)
			if spec.isMem != ok {
				t.Errorf("expected in memory? %t, got %t: %+v", spec.isMem, ok, spec)
			}
		} else if !spec.wantError {
			t.Errorf("expected no error, got %v: %+v", err, spec)
		}
	}
}

// TestInitEngines tests whether multiple engines specified as a
// single comma-separated list are parsed correctly.
func TestInitEngines(t *testing.T) {
	tmp := createTempDirs(2, t)
	defer resetTestData(tmp)

	stores := fmt.Sprintf("mem=1000,mem:ddr3=1000,ssd=%s,hdd:7200rpm=%s", tmp[0], tmp[1])
	expEngines := []struct {
		attrs proto.Attributes
		isMem bool
	}{
		{proto.Attributes{Attrs: []string{"mem"}}, true},
		{proto.Attributes{Attrs: []string{"mem", "ddr3"}}, true},
		{proto.Attributes{Attrs: []string{"ssd"}}, false},
		{proto.Attributes{Attrs: []string{"hdd", "7200rpm"}}, false},
	}

	engines, err := initEngines(stores)
	if err != nil {
		t.Fatal(err)
	}
	if len(engines) != len(expEngines) {
		t.Errorf("number of engines parsed %d != expected %d", len(engines), len(expEngines))
	}
	for i, e := range engines {
		if e.Attrs().SortedString() != expEngines[i].attrs.SortedString() {
			t.Errorf("wrong engine attributes, expected %v but got %v: %+v", expEngines[i].attrs, e.Attrs(), expEngines[i])
		}
		_, ok := e.(*engine.InMem)
		if expEngines[i].isMem != ok {
			t.Errorf("expected in memory? %t, got %t: %+v", expEngines[i].isMem, ok, expEngines[i])
		}
	}
}

// TestHealthz verifies that /_admin/healthz does, in fact, return "ok"
// as expected.
func TestHealthz(t *testing.T) {
	startServer()
	url := "http://" + *httpAddr + "/_admin/healthz"
	resp, err := http.Get(url)
	if err != nil {
		t.Fatalf("error requesting healthz at %s: %s", url, err)
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("could not read response body: %s", err)
	}
	expected := "ok"
	if !strings.Contains(string(b), expected) {
		t.Errorf("expected body to contain %q, got %q", expected, string(b))
	}
}

// TestGzip hits the /_admin/healthz endpoint while explicitly disabling
// decompression on a custom client's Transport and setting it
// conditionally via the request's Accept-Encoding headers.
func TestGzip(t *testing.T) {
	startServer()
	client := http.Client{
		Transport: &http.Transport{
			Proxy:              http.ProxyFromEnvironment,
			DisableCompression: true,
		},
	}
	req, err := http.NewRequest("GET", "http://"+*httpAddr+"/_admin/healthz", nil)
	if err != nil {
		t.Fatalf("could not create request: %s", err)
	}
	resp, err := client.Do(req)
	if err != nil {
		t.Fatalf("could not make request to %s: %s", req.URL, err)
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("could not read response body: %s", err)
	}
	expected := "ok"
	if !strings.Contains(string(b), expected) {
		t.Errorf("expected body to contain %q, got %q", expected, string(b))
	}
	// Test for gzip explicitly.
	req.Header.Set("Accept-Encoding", "gzip")
	resp, err = client.Do(req)
	if err != nil {
		t.Fatalf("could not make request to %s: %s", req.URL, err)
	}
	defer resp.Body.Close()
	gz, err := gzip.NewReader(resp.Body)
	if err != nil {
		t.Fatalf("could not create new gzip reader: %s", err)
	}
	b, err = ioutil.ReadAll(gz)
	if err != nil {
		t.Fatalf("could not read gzipped response body: %s", err)
	}
	if !strings.Contains(string(b), expected) {
		t.Errorf("expected body to contain %q, got %q", expected, string(b))
	}
}
<commit_msg>test multi-range Scan and DeleteRange<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Andrew Bonventre (andybons@gmail.com)

package server

import (
	"compress/gzip"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"sync"
	"testing"

	"github.com/cockroachdb/cockroach/client"
	"github.com/cockroachdb/cockroach/kv"
	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/storage"
	"github.com/cockroachdb/cockroach/storage/engine"
	"github.com/cockroachdb/cockroach/util/log"
)

var (
	s              *server
	serverTestOnce sync.Once
)

// Start a test server. The server will be initialized with an
// in-memory engine and will execute a split at key "m" so that
// it will end up having two logical ranges.
func startServer() *server {
	serverTestOnce.Do(func() {
		// We update these with the actual port once the servers
		// have been launched for the purpose of this test.
		var err error
		s, err = newServer("127.0.0.1:0", "", *maxOffset)
		if err != nil {
			log.Fatal(err)
		}
		engines := []engine.Engine{engine.NewInMem(proto.Attributes{}, 1<<20)}
		if _, err := BootstrapCluster("cluster-1", engines[0]); err != nil {
			log.Fatal(err)
		}
		err = s.start(engines, "", "127.0.0.1:0", true) // TODO(spencer): should shutdown server.
		if err != nil {
			log.Fatalf("Could not start server: %s", err)
		}
		// Update the configuration variables to reflect the actual
		// ports bound.
		*httpAddr = (*s.httpListener).Addr().String()
		*rpcAddr = s.rpc.Addr().String()
		log.Infof("Test server listening on http: %s, rpc: %s", *httpAddr, *rpcAddr)
		if err := s.node.db.Call(proto.AdminSplit,
			&proto.AdminSplitRequest{
				RequestHeader: proto.RequestHeader{
					Key: proto.Key("m"),
				},
				SplitKey: proto.Key("m"),
			}, &proto.AdminSplitResponse{}); err != nil {
			log.Fatal(err)
		}
	})
	return s
}

// createTestConfigFile creates a temporary file and writes the
// testConfig yaml data to it. The caller is responsible for
// removing it. Returns the filename for a subsequent call to
// os.Remove().
func createTestConfigFile(body string) string {
	f, err := ioutil.TempFile("", "test-config")
	if err != nil {
		log.Fatalf("failed to open temporary file: %v", err)
	}
	defer f.Close()
	f.Write([]byte(body))
	return f.Name()
}

// createTempDirs creates "count" temporary directories and returns
// the paths to each as a slice.
func createTempDirs(count int, t *testing.T) []string {
	tmp := make([]string, count)
	for i := 0; i < count; i++ {
		var err error
		if tmp[i], err = ioutil.TempDir("", "_server_test"); err != nil {
			t.Fatal(err)
		}
	}
	return tmp
}

// resetTestData recursively removes all files written to the
// directories specified as parameters.
func resetTestData(dirs []string) {
	for _, dir := range dirs {
		os.RemoveAll(dir)
	}
}

// TestInitEngine tests whether the data directory string is parsed correctly.
func TestInitEngine(t *testing.T) {
	tmp := createTempDirs(5, t)
	defer resetTestData(tmp)

	testCases := []struct {
		key       string           // data directory
		expAttrs  proto.Attributes // attributes for engine
		wantError bool             // do we expect an error from this key?
		isMem     bool             // is the engine in-memory?
	}{
		{"mem=1000", proto.Attributes{Attrs: []string{"mem"}}, false, true},
		{"ssd=1000", proto.Attributes{Attrs: []string{"ssd"}}, false, true},
		{fmt.Sprintf("ssd=%s", tmp[0]), proto.Attributes{Attrs: []string{"ssd"}}, false, false},
		{fmt.Sprintf("hdd=%s", tmp[1]), proto.Attributes{Attrs: []string{"hdd"}}, false, false},
		{fmt.Sprintf("mem=%s", tmp[2]), proto.Attributes{Attrs: []string{"mem"}}, false, false},
		{fmt.Sprintf("abc=%s", tmp[3]), proto.Attributes{Attrs: []string{"abc"}}, false, false},
		{fmt.Sprintf("hdd:7200rpm=%s", tmp[4]), proto.Attributes{Attrs: []string{"hdd", "7200rpm"}}, false, false},
		{"", proto.Attributes{}, true, false},
		{"  ", proto.Attributes{}, true, false},
		{"arbitrarystring", proto.Attributes{}, true, false},
		{"mem=", proto.Attributes{}, true, false},
		{"ssd=", proto.Attributes{}, true, false},
		{"hdd=", proto.Attributes{}, true, false},
	}
	for _, spec := range testCases {
		engines, err := initEngines(spec.key)
		if err == nil {
			if spec.wantError {
				t.Fatalf("invalid engine spec '%v' erroneously accepted: %+v", spec.key, spec)
			}
			if len(engines) != 1 {
				t.Fatalf("unexpected number of engines: %d: %+v", len(engines), spec)
			}
			e := engines[0]
			if e.Attrs().SortedString() != spec.expAttrs.SortedString() {
				t.Errorf("wrong engine attributes, expected %v but got %v: %+v", spec.expAttrs, e.Attrs(), spec)
			}
			_, ok := e.(*engine.InMem)
			if spec.isMem != ok {
				t.Errorf("expected in memory? %t, got %t: %+v", spec.isMem, ok, spec)
			}
		} else if !spec.wantError {
			t.Errorf("expected no error, got %v: %+v", err, spec)
		}
	}
}

// TestInitEngines tests whether multiple engines specified as a
// single comma-separated list are parsed correctly.
func TestInitEngines(t *testing.T) {
	tmp := createTempDirs(2, t)
	defer resetTestData(tmp)

	stores := fmt.Sprintf("mem=1000,mem:ddr3=1000,ssd=%s,hdd:7200rpm=%s", tmp[0], tmp[1])
	expEngines := []struct {
		attrs proto.Attributes
		isMem bool
	}{
		{proto.Attributes{Attrs: []string{"mem"}}, true},
		{proto.Attributes{Attrs: []string{"mem", "ddr3"}}, true},
		{proto.Attributes{Attrs: []string{"ssd"}}, false},
		{proto.Attributes{Attrs: []string{"hdd", "7200rpm"}}, false},
	}

	engines, err := initEngines(stores)
	if err != nil {
		t.Fatal(err)
	}
	if len(engines) != len(expEngines) {
		t.Errorf("number of engines parsed %d != expected %d", len(engines), len(expEngines))
	}
	for i, e := range engines {
		if e.Attrs().SortedString() != expEngines[i].attrs.SortedString() {
			t.Errorf("wrong engine attributes, expected %v but got %v: %+v", expEngines[i].attrs, e.Attrs(), expEngines[i])
		}
		_, ok := e.(*engine.InMem)
		if expEngines[i].isMem != ok {
			t.Errorf("expected in memory? %t, got %t: %+v", expEngines[i].isMem, ok, expEngines[i])
		}
	}
}

// TestHealthz verifies that /_admin/healthz does, in fact, return "ok"
// as expected.
func TestHealthz(t *testing.T) {
	startServer()
	url := "http://" + *httpAddr + "/_admin/healthz"
	resp, err := http.Get(url)
	if err != nil {
		t.Fatalf("error requesting healthz at %s: %s", url, err)
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("could not read response body: %s", err)
	}
	expected := "ok"
	if !strings.Contains(string(b), expected) {
		t.Errorf("expected body to contain %q, got %q", expected, string(b))
	}
}

// TestGzip hits the /_admin/healthz endpoint while explicitly disabling
// decompression on a custom client's Transport and setting it
// conditionally via the request's Accept-Encoding headers.
func TestGzip(t *testing.T) {
	startServer()
	client := http.Client{
		Transport: &http.Transport{
			Proxy:              http.ProxyFromEnvironment,
			DisableCompression: true,
		},
	}
	req, err := http.NewRequest("GET", "http://"+*httpAddr+"/_admin/healthz", nil)
	if err != nil {
		t.Fatalf("could not create request: %s", err)
	}
	resp, err := client.Do(req)
	if err != nil {
		t.Fatalf("could not make request to %s: %s", req.URL, err)
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("could not read response body: %s", err)
	}
	expected := "ok"
	if !strings.Contains(string(b), expected) {
		t.Errorf("expected body to contain %q, got %q", expected, string(b))
	}
	// Test for gzip explicitly.
	req.Header.Set("Accept-Encoding", "gzip")
	resp, err = client.Do(req)
	if err != nil {
		t.Fatalf("could not make request to %s: %s", req.URL, err)
	}
	defer resp.Body.Close()
	gz, err := gzip.NewReader(resp.Body)
	if err != nil {
		t.Fatalf("could not create new gzip reader: %s", err)
	}
	b, err = ioutil.ReadAll(gz)
	if err != nil {
		t.Fatalf("could not read gzipped response body: %s", err)
	}
	if !strings.Contains(string(b), expected) {
		t.Errorf("expected body to contain %q, got %q", expected, string(b))
	}
}

func TestMultiRangeScanDeleteRange(t *testing.T) {
	startServer()
	ds := kv.NewDistSender(s.gossip)

	writes := [][]byte{[]byte("a"), []byte("z")}
	var call *client.Call
	for i, k := range writes {
		call = &client.Call{
			Method: proto.Put,
			Args: &proto.PutRequest{
				RequestHeader: proto.RequestHeader{
					User: storage.UserRoot,
					Key:  k,
				},
				Value: proto.Value{
					Bytes: k,
				},
			},
			Reply: &proto.PutResponse{},
		}
		ds.Send(call)
		if err := call.Reply.Header().GoError(); err != nil {
			t.Fatal(err)
		}
		scan := &client.Call{
			Method: proto.Scan,
			Args: &proto.ScanRequest{
				RequestHeader: proto.RequestHeader{
					User:   storage.UserRoot,
					Key:    writes[0],
					EndKey: proto.Key(writes[len(writes)-1]).Next(),
					// TODO(Tobias): Why is this necessary? If I skip this,
					// then the Scan() will end up reading with a timestamp
					// that's slightly behind the one of the Puts above,
					// and not see the inserts.
					Timestamp: call.Reply.Header().Timestamp,
				},
			},
			Reply: &proto.ScanResponse{},
		}
		ds.Send(scan)
		if err := scan.Reply.Header().GoError(); err != nil {
			t.Fatal(err)
		}
		if rows := scan.Reply.(*proto.ScanResponse).Rows; len(rows) != i+1 {
			t.Fatalf("expected %d rows, but got %d", i+1, len(rows))
		}
	}
	del := &client.Call{
		Method: proto.DeleteRange,
		Args: &proto.DeleteRangeRequest{
			RequestHeader: proto.RequestHeader{
				User:   storage.UserRoot,
				Key:    writes[0],
				EndKey: proto.Key(writes[len(writes)-1]).Next(),
			},
		},
		Reply: &proto.DeleteRangeResponse{},
	}
	ds.Send(del)
	if err := del.Reply.Header().GoError(); err != nil {
		t.Fatal(err)
	}
	scan := &client.Call{
		Method: proto.Scan,
		Args: &proto.ScanRequest{
			RequestHeader: proto.RequestHeader{
				User:   storage.UserRoot,
				Key:    writes[0],
				EndKey: proto.Key(writes[len(writes)-1]).Next(),
				// TODO(Tobias): ditto.
				Timestamp: del.Reply.Header().Timestamp,
			},
		},
		Reply: &proto.ScanResponse{},
	}
	ds.Send(scan)
	if err := scan.Reply.Header().GoError(); err != nil {
		t.Fatal(err)
	}
	if rows := scan.Reply.(*proto.ScanResponse).Rows; len(rows) > 0 {
		t.Fatalf("scan after delete returned rows: %v", rows)
	}
}
<|endoftext|>


<commit_before>package integration_test

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/onsi/gomega/ghttp"
	"github.com/vito/go-sse/sse"

	"github.com/concourse/atc"
	"github.com/concourse/atc/event"
)

var _ = Describe("Watching", func() {
	var atcServer *ghttp.Server
	var streaming chan struct{}
	var events chan atc.Event

	BeforeEach(func() {
		atcServer = ghttp.NewServer()
		streaming = make(chan struct{})
		events = make(chan atc.Event)
	})

	eventsHandler := func() http.HandlerFunc {
		return ghttp.CombineHandlers(
			ghttp.VerifyRequest("GET", "/api/v1/builds/3/events"),
			func(w http.ResponseWriter, r *http.Request) {
				flusher := w.(http.Flusher)

				w.Header().Add("Content-Type", "text/event-stream; charset=utf-8")
				w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
				w.Header().Add("Connection", "keep-alive")

				w.WriteHeader(http.StatusOK)

				flusher.Flush()

				close(streaming)

				id := 0

				for e := range events {
					payload, err := json.Marshal(event.Message{e})
					Expect(err).NotTo(HaveOccurred())

					event := sse.Event{
						ID:   fmt.Sprintf("%d", id),
						Name: "event",
						Data: payload,
					}

					err = event.Write(w)
					Expect(err).NotTo(HaveOccurred())

					flusher.Flush()

					id++
				}

				err := sse.Event{
					Name: "end",
				}.Write(w)
				Expect(err).NotTo(HaveOccurred())
			},
		)
	}

	watch := func(args ...string) {
		watchWithArgs := append([]string{"watch"}, args...)

		flyCmd := exec.Command(flyPath, append([]string{"-t", atcServer.URL()}, watchWithArgs...)...)

		sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
		Expect(err).NotTo(HaveOccurred())

		Eventually(streaming).Should(BeClosed())

		events <- event.Log{Payload: "sup"}

		Eventually(sess.Out).Should(gbytes.Say("sup"))

		close(events)

		<-sess.Exited
		Expect(sess.ExitCode()).To(Equal(0))
	}

	Context("with no arguments", func() {
		BeforeEach(func() {
			atcServer.AppendHandlers(
				ghttp.CombineHandlers(
					ghttp.VerifyRequest("GET", "/api/v1/builds"),
					ghttp.RespondWithJSONEncoded(200, []atc.Build{
						{ID: 4, Name: "1", Status: "started", JobName: "some-job"},
						{ID: 3, Name: "3", Status: "started"},
						{ID: 2, Name: "2", Status: "started"},
						{ID: 1, Name: "1", Status: "finished"},
					}),
				),
				eventsHandler(),
			)
		})

		It("watches the most recent one-off build", func() {
			watch()
		})
	})

	Context("with a specific job and pipeline", func() {
		Context("when the job has a next build", func() {
			BeforeEach(func() {
				didStream := make(chan struct{})
				streaming = didStream

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/api/v1/pipelines/some-pipeline/jobs/some-job"),
						ghttp.RespondWithJSONEncoded(200, atc.Job{
							NextBuild: &atc.Build{
								ID:      3,
								Name:    "3",
								Status:  "started",
								JobName: "some-job",
							},
							FinishedBuild: &atc.Build{
								ID:      2,
								Name:    "2",
								Status:  "failed",
								JobName: "some-job",
							},
						}),
					),
					eventsHandler(),
				)
			})

			It("watches the job's next build", func() {
				watch("--job", "some-pipeline/some-job")
			})
		})

		Context("when the job only has a finished build", func() {
			BeforeEach(func() {
				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/api/v1/pipelines/main/jobs/some-job"),
						ghttp.RespondWithJSONEncoded(200, atc.Job{
							NextBuild: nil,
							FinishedBuild: &atc.Build{
								ID:      3,
								Name:    "3",
								Status:  "failed",
								JobName: "some-job",
							},
						}),
					),
					eventsHandler(),
				)
			})

			It("watches the job's finished build", func() {
				watch("--job", "some-job")
			})
		})

		Context("with a specific build of the job", func() {
			BeforeEach(func() {
				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/api/v1/pipelines/main/jobs/some-job/builds/3"),
						ghttp.RespondWithJSONEncoded(200, atc.Build{
							ID:      3,
							Name:    "3",
							Status:  "failed",
							JobName: "some-job",
						}),
					),
					eventsHandler(),
				)
			})

			It("watches the given build", func() {
				watch("--job", "some-job", "--build", "3")
			})
		})
	})
})
<commit_msg>Backfilling test.<commit_after>package integration_test

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/onsi/gomega/ghttp"
	"github.com/vito/go-sse/sse"

	"github.com/concourse/atc"
	"github.com/concourse/atc/event"
)

var _ = Describe("Watching", func() {
	var atcServer *ghttp.Server
	var streaming chan struct{}
	var events chan atc.Event

	BeforeEach(func() {
		atcServer = ghttp.NewServer()
		streaming = make(chan struct{})
		events = make(chan atc.Event)
	})

	eventsHandler := func() http.HandlerFunc {
		return ghttp.CombineHandlers(
			ghttp.VerifyRequest("GET", "/api/v1/builds/3/events"),
			func(w http.ResponseWriter, r *http.Request) {
				flusher := w.(http.Flusher)

				w.Header().Add("Content-Type", "text/event-stream; charset=utf-8")
				w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
				w.Header().Add("Connection", "keep-alive")

				w.WriteHeader(http.StatusOK)

				flusher.Flush()

				close(streaming)

				id := 0

				for e := range events {
					payload, err := json.Marshal(event.Message{e})
					Expect(err).NotTo(HaveOccurred())

					event := sse.Event{
						ID:   fmt.Sprintf("%d", id),
						Name: "event",
						Data: payload,
					}

					err = event.Write(w)
					Expect(err).NotTo(HaveOccurred())

					flusher.Flush()

					id++
				}

				err := sse.Event{
					Name: "end",
				}.Write(w)
				Expect(err).NotTo(HaveOccurred())
			},
		)
	}

	watch := func(args ...string) {
		watchWithArgs := append([]string{"watch"}, args...)

		flyCmd := exec.Command(flyPath, append([]string{"-t", atcServer.URL()}, watchWithArgs...)...)

		sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
		Expect(err).NotTo(HaveOccurred())

		Eventually(streaming).Should(BeClosed())

		events <- event.Log{Payload: "sup"}

		Eventually(sess.Out).Should(gbytes.Say("sup"))

		close(events)

		<-sess.Exited
		Expect(sess.ExitCode()).To(Equal(0))
	}

	Context("with no arguments", func() {
		BeforeEach(func() {
			atcServer.AppendHandlers(
				ghttp.CombineHandlers(
					ghttp.VerifyRequest("GET", "/api/v1/builds"),
					ghttp.RespondWithJSONEncoded(200, []atc.Build{
						{ID: 4, Name: "1", Status: "started", JobName: "some-job"},
						{ID: 3, Name: "3", Status: "started"},
						{ID: 2, Name: "2", Status: "started"},
						{ID: 1, Name: "1", Status: "finished"},
					}),
				),
				eventsHandler(),
			)
		})

		It("watches the most recent one-off build", func() {
			watch()
		})
	})

	Context("with a specific job and pipeline", func() {
		Context("when the job has no builds", func() {
			BeforeEach(func() {
				didStream := make(chan struct{})
				streaming = didStream

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/api/v1/pipelines/some-pipeline/jobs/some-job"),
						ghttp.RespondWithJSONEncoded(200, atc.Job{}),
					),
					eventsHandler(),
				)
			})

			It("returns an error and exits", func() {
				flyCmd := exec.Command(flyPath, "-t", atcServer.URL(), "watch", "--job", "some-pipeline/some-job")
				sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
				Expect(err).NotTo(HaveOccurred())

				Eventually(sess.Err).Should(gbytes.Say("job has no builds"))
				Expect(sess.ExitCode()).To(Equal(1))
			})
		})

		Context("when the job has a next build", func() {
			BeforeEach(func() {
				didStream := make(chan struct{})
				streaming = didStream

				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/api/v1/pipelines/some-pipeline/jobs/some-job"),
						ghttp.RespondWithJSONEncoded(200, atc.Job{
							NextBuild: &atc.Build{
								ID:      3,
								Name:    "3",
								Status:  "started",
								JobName: "some-job",
							},
							FinishedBuild: &atc.Build{
								ID:      2,
								Name:    "2",
								Status:  "failed",
								JobName: "some-job",
							},
						}),
					),
					eventsHandler(),
				)
			})

			It("watches the job's next build", func() {
				watch("--job", "some-pipeline/some-job")
			})
		})

		Context("when the job only has a finished build", func() {
			BeforeEach(func() {
				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/api/v1/pipelines/main/jobs/some-job"),
						ghttp.RespondWithJSONEncoded(200, atc.Job{
							NextBuild: nil,
							FinishedBuild: &atc.Build{
								ID:      3,
								Name:    "3",
								Status:  "failed",
								JobName: "some-job",
							},
						}),
					),
					eventsHandler(),
				)
			})

			It("watches the job's finished build", func() {
				watch("--job", "some-job")
			})
		})

		Context("with a specific build of the job", func() {
			BeforeEach(func() {
				atcServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/api/v1/pipelines/main/jobs/some-job/builds/3"),
						ghttp.RespondWithJSONEncoded(200, atc.Build{
							ID:      3,
							Name:    "3",
							Status:  "failed",
							JobName: "some-job",
						}),
					),
					eventsHandler(),
				)
			})

			It("watches the given build", func() {
				watch("--job", "some-job", "--build", "3")
			})
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 affine

// add is deprecated
func add(lhs, rhs []float64, dim int) []float64 {
	result := make([]float64, len(lhs))
	for i := 0; i < dim-1; i++ {
		for j := 0; j < dim; j++ {
			result[i*dim+j] = lhs[i*dim+j] + rhs[i*dim+j]
		}
	}
	return result
}

func mulSquare(lhs, rhs []float32, dim int) []float32 {
	result := make([]float32, len(lhs))
	for i := 0; i < dim; i++ {
		for j := 0; j < dim; j++ {
			e := float32(0.0)
			for k := 0; k < dim; k++ {
				e += lhs[i*dim+k] * rhs[k*dim+j]
			}
			result[i*dim+j] = e
		}
	}
	return result
}
<commit_msg>affine: Remove unused function<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 affine

func mulSquare(lhs, rhs []float32, dim int) []float32 {
	result := make([]float32, len(lhs))
	for i := 0; i < dim; i++ {
		for j := 0; j < dim; j++ {
			e := float32(0.0)
			for k := 0; k < dim; k++ {
				e += lhs[i*dim+k] * rhs[k*dim+j]
			}
			result[i*dim+j] = e
		}
	}
	return result
}
<|endoftext|>