<commit_before>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// 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 cmd

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"github.com/service-exposer/exposer"
	"github.com/service-exposer/exposer/listener/utils"
	"github.com/service-exposer/exposer/protocal/auth"
	"github.com/service-exposer/exposer/service"
	"github.com/spf13/cobra"
	"github.com/urfave/negroni"
)

// daemonCmd represents the daemon command
var daemonCmd = &cobra.Command{
	Use:   "daemon",
	Short: "The daemon is server-side of exposer",
}

func init() {
	RootCmd.AddCommand(daemonCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// daemonCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// daemonCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
	var (
		addr       = "0.0.0.0:9000"
		enableTLS  = false
		https_cert = ""
		https_key  = ""
	)
	daemonCmd.Flags().StringVarP(&addr, "addr", "a", addr, "listen address")
	daemonCmd.Flags().BoolVarP(&enableTLS, "https", "", enableTLS, "enable TLS")
	daemonCmd.Flags().StringVarP(&https_cert, "https-cert", "", https_cert, "TLS certificate")
	daemonCmd.Flags().StringVarP(&https_key, "https-key", "", https_key, "TLS key")

	daemonCmd.Run = func(cmd *cobra.Command, args []string) {
		ln, err := net.Listen("tcp", addr)
		if err != nil {
			fmt.Fprintln(os.Stderr, "listen", addr, "failure", err)
			os.Exit(-1)
		}
		defer ln.Close()

		var schema = "http"
		var tlsConf *tls.Config
		if enableTLS {
			cert, err := tls.LoadX509KeyPair(https_cert, https_key)
			if err != nil {
				exit(-5, "LoadX509KeyPair:", err)
			}
			tlsConf = &tls.Config{
				Certificates: []tls.Certificate{cert},
			}

			// replace ln to tls.Listener
			ln = tls.NewListener(ln, tlsConf)
			defer ln.Close()
			schema = "https"
		}
		log.Print("listen ", fmt.Sprintf("%s://%s/", schema, ln.Addr()))

		wsln, wsconnHandler, err := utils.WebsocketHandlerListener(ln.Addr())
		if err != nil {
			fmt.Fprintln(os.Stderr, "listen ws", ln.Addr(), "failure", err)
			os.Exit(-2)
		}
		defer wsln.Close()

		serviceRouter := service.NewRouter()

		r := mux.NewRouter()

		r.Path("/api/services").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			services := serviceRouter.All()

			result := make(map[string]*json.RawMessage)
			for _, s := range services {
				s.Attribute().View(func(attr service.Attribute) error {
					data, err := json.Marshal(attr)
					if err != nil {
						return err
					}

					rawmsg := json.RawMessage(data)
					result[s.Name()] = &rawmsg
					return nil
				})
			}

			json.NewEncoder(w).Encode(&result)

		}).Methods("GET")

		r.PathPrefix("/service/{name}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			vars := mux.Vars(r)
			var (
				name = vars["name"]
			)

			s := serviceRouter.Get(name)
			if s == nil {
				http.Error(w, "service is not exist", 404)
				return
			}

			var attr service.Attribute
			s.Attribute().View(func(a service.Attribute) error {
				attr = a
				return nil
			})

			if !attr.HTTP.Is {
				http.Error(w, "service is not a HTTP service", 404)
				return
			}

			if r.URL.Path == "/service/"+name {
				http.Redirect(w, r, "/service/"+name+"/", 302)
				return
			}

			hj, ok := w.(http.Hijacker)
			if !ok {
				http.Error(w, "webserver doesn't support hijacking", 500)
				return
			}

			client, clientbufrw, err := hj.Hijack()
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}

			server, err := s.Open()
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}

			go func(r *http.Request) {
				var err error
				for err == nil {
					subPath := r.URL.Path[len("/service/"+name):]
					if subPath == "" {
						client.Close()
						server.Close()
						return
					}
					if subPath[0] != '/' {
						subPath = "/" + subPath
					}
					url, _ := url.Parse(subPath)

					r.URL = url
					if attr.HTTP.Host != "" {
						r.Host = attr.HTTP.Host
					}
					r.Header.Set("X-Origin-IP", client.RemoteAddr().String())

					r.Write(server)

					if r.Header.Get("Upgrade") != "" {
						break
					}
					r, err = http.ReadRequest(clientbufrw.Reader)
				}

				io.Copy(server, clientbufrw)
				client.Close()
			}(r)

			io.Copy(clientbufrw, server)
			server.Close()
		})

		n := negroni.New()

		// ws
		n.UseFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
			if strings.HasPrefix(r.URL.Path, "/service/") {
				next(w, r)
				return
			}

			connection := r.Header.Get("Connection")
			upgrade := r.Header.Get("Upgrade")
			if connection == "Upgrade" && upgrade == "websocket" {
				wsconnHandler.ServeHTTP(w, r)
				return
			}

			next(w, r)
		})

		// auth
		n.UseFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
			if !strings.HasPrefix(r.URL.Path, "/api/") {
				next(w, r)
				return
			}

			auth := r.Header.Get("Authorization")
			if auth != key {
				w.WriteHeader(401)
				fmt.Fprintln(w, "Please set Header Authorization as Key")
				return
			}

			next(w, r)
		})

		n.UseHandler(r)

		go func() {
			server := &http.Server{
				ReadTimeout:  30 * time.Second,
				WriteTimeout: 30 * time.Second,
				Handler:      n,
				TLSConfig:    tlsConf,
			}

			err := server.Serve(ln)
			if err != nil {
				fmt.Fprintln(os.Stderr, "HTTP server shutdown. occur error:", err)
			}
		}()
		exposer.Serve(wsln, func(conn net.Conn) exposer.ProtocalHandler {
			proto := exposer.NewProtocal(conn)
			proto.On = auth.ServerSide(serviceRouter, func(k string) bool {
				return k == key
			})
			return proto
		})
	}
}
<commit_msg>clear deadline of hijacked conns inside cmd daemon<commit_after>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// 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 cmd

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"github.com/service-exposer/exposer"
	"github.com/service-exposer/exposer/listener/utils"
	"github.com/service-exposer/exposer/protocal/auth"
	"github.com/service-exposer/exposer/service"
	"github.com/spf13/cobra"
	"github.com/urfave/negroni"
)

// daemonCmd represents the daemon command
var daemonCmd = &cobra.Command{
	Use:   "daemon",
	Short: "The daemon is server-side of exposer",
}

func init() {
	RootCmd.AddCommand(daemonCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// daemonCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// daemonCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
	var (
		addr       = "0.0.0.0:9000"
		enableTLS  = false
		https_cert = ""
		https_key  = ""
	)
	daemonCmd.Flags().StringVarP(&addr, "addr", "a", addr, "listen address")
	daemonCmd.Flags().BoolVarP(&enableTLS, "https", "", enableTLS, "enable TLS")
	daemonCmd.Flags().StringVarP(&https_cert, "https-cert", "", https_cert, "TLS certificate")
	daemonCmd.Flags().StringVarP(&https_key, "https-key", "", https_key, "TLS key")

	daemonCmd.Run = func(cmd *cobra.Command, args []string) {
		ln, err := net.Listen("tcp", addr)
		if err != nil {
			fmt.Fprintln(os.Stderr, "listen", addr, "failure", err)
			os.Exit(-1)
		}
		defer ln.Close()

		var schema = "http"
		var tlsConf *tls.Config
		if enableTLS {
			cert, err := tls.LoadX509KeyPair(https_cert, https_key)
			if err != nil {
				exit(-5, "LoadX509KeyPair:", err)
			}
			tlsConf = &tls.Config{
				Certificates: []tls.Certificate{cert},
			}

			// replace ln to tls.Listener
			ln = tls.NewListener(ln, tlsConf)
			defer ln.Close()
			schema = "https"
		}
		log.Print("listen ", fmt.Sprintf("%s://%s/", schema, ln.Addr()))

		wsln, wsconnHandler, err := utils.WebsocketHandlerListener(ln.Addr())
		if err != nil {
			fmt.Fprintln(os.Stderr, "listen ws", ln.Addr(), "failure", err)
			os.Exit(-2)
		}
		defer wsln.Close()

		serviceRouter := service.NewRouter()

		r := mux.NewRouter()

		r.Path("/api/services").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			services := serviceRouter.All()

			result := make(map[string]*json.RawMessage)
			for _, s := range services {
				s.Attribute().View(func(attr service.Attribute) error {
					data, err := json.Marshal(attr)
					if err != nil {
						return err
					}

					rawmsg := json.RawMessage(data)
					result[s.Name()] = &rawmsg
					return nil
				})
			}

			json.NewEncoder(w).Encode(&result)

		}).Methods("GET")

		r.PathPrefix("/service/{name}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			vars := mux.Vars(r)
			var (
				name = vars["name"]
			)

			s := serviceRouter.Get(name)
			if s == nil {
				http.Error(w, "service is not exist", 404)
				return
			}

			var attr service.Attribute
			s.Attribute().View(func(a service.Attribute) error {
				attr = a
				return nil
			})

			if !attr.HTTP.Is {
				http.Error(w, "service is not a HTTP service", 404)
				return
			}

			if r.URL.Path == "/service/"+name {
				http.Redirect(w, r, "/service/"+name+"/", 302)
				return
			}

			hj, ok := w.(http.Hijacker)
			if !ok {
				http.Error(w, "webserver doesn't support hijacking", 500)
				return
			}

			client, clientbufrw, err := hj.Hijack()
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}
			// clear up deadline that maybe set by http.Server
			client.SetDeadline(time.Time{})

			server, err := s.Open()
			if err != nil {
				http.Error(w, err.Error(), 500)
				return
			}

			go func(r *http.Request) {
				var err error
				for err == nil {
					subPath := r.URL.Path[len("/service/"+name):]
					if subPath == "" {
						client.Close()
						server.Close()
						return
					}
					if subPath[0] != '/' {
						subPath = "/" + subPath
					}
					url, _ := url.Parse(subPath)

					r.URL = url
					if attr.HTTP.Host != "" {
						r.Host = attr.HTTP.Host
					}
					r.Header.Set("X-Origin-IP", client.RemoteAddr().String())

					r.Write(server)

					if r.Header.Get("Upgrade") != "" {
						break
					}
					r, err = http.ReadRequest(clientbufrw.Reader)
				}

				io.Copy(server, clientbufrw)
				client.Close()
			}(r)

			io.Copy(clientbufrw, server)
			server.Close()
		})

		n := negroni.New()

		// ws
		n.UseFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
			if strings.HasPrefix(r.URL.Path, "/service/") {
				next(w, r)
				return
			}

			connection := r.Header.Get("Connection")
			upgrade := r.Header.Get("Upgrade")
			if connection == "Upgrade" && upgrade == "websocket" {
				wsconnHandler.ServeHTTP(w, r)
				return
			}

			next(w, r)
		})

		// auth
		n.UseFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
			if !strings.HasPrefix(r.URL.Path, "/api/") {
				next(w, r)
				return
			}

			auth := r.Header.Get("Authorization")
			if auth != key {
				w.WriteHeader(401)
				fmt.Fprintln(w, "Please set Header Authorization as Key")
				return
			}

			next(w, r)
		})

		n.UseHandler(r)

		go func() {
			server := &http.Server{
				ReadTimeout:  30 * time.Second,
				WriteTimeout: 30 * time.Second,
				Handler:      n,
				TLSConfig:    tlsConf,
			}

			err := server.Serve(ln)
			if err != nil {
				fmt.Fprintln(os.Stderr, "HTTP server shutdown. occur error:", err)
			}
		}()
		exposer.Serve(wsln, func(conn net.Conn) exposer.ProtocalHandler {
			proto := exposer.NewProtocal(conn)
			proto.On = auth.ServerSide(serviceRouter, func(k string) bool {
				return k == key
			})
			return proto
		})
	}
}
<|endoftext|>


<commit_before>/* Copyright (c) 2014-2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"crypto/rand"
	"encoding/hex"
	"errors"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

type FileStore struct {
	sync.RWMutex
	cache map[ID]fileCache

	dir   string
	stats Stats
}

type fileCache struct {
	reading sync.WaitGroup
	header  Header
	path    string
}

type FileContent struct {
	file    *os.File
	reading *sync.WaitGroup
}

func (c FileContent) Read(p []byte) (n int, err error) {
	return c.file.Read(p)
}

func (c FileContent) ReadAt(p []byte, off int64) (n int, err error) {
	return c.file.ReadAt(p, off)
}

func (c FileContent) Seek(offset int64, whence int) (int64, error) {
	return c.file.Seek(offset, whence)
}

func (c FileContent) Close() error {
	err := c.file.Close()
	c.reading.Done()
	return err
}

func newFileStore(dir string) (s *FileStore, err error) {
	if err = os.MkdirAll(dir, 0700); err != nil {
		return nil, err
	}
	if err = os.Chdir(dir); err != nil {
		return nil, err
	}
	s = new(FileStore)
	s.dir = dir
	s.cache = make(map[ID]fileCache)
	for i := 0; i < 256; i++ {
		if err = s.setupSubdir(byte(i)); err != nil {
			return nil, err
		}
	}
	return
}

func (s *FileStore) Get(id ID) (Content, *Header, error) {
	s.RLock()
	defer s.RUnlock()
	cached, e := s.cache[id]
	if !e {
		return nil, nil, ErrPasteNotFound
	}
	f, err := os.Open(cached.path)
	if err != nil {
		return nil, nil, err
	}
	cached.reading.Add(1)
	return FileContent{f, &cached.reading}, &cached.header, nil
}

func (s *FileStore) Put(content []byte) (id ID, err error) {
	s.Lock()
	defer s.Unlock()
	size := int64(len(content))
	if !s.stats.hasSpaceFor(size) {
		return id, ErrReachedMax
	}
	if id, err = s.randomID(); err != nil {
		return
	}
	hexID := id.String()
	pastePath := path.Join(hexID[:2], hexID[2:])
	pasteFile, err := os.OpenFile(pastePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
	if err != nil {
		return
	}
	defer pasteFile.Close()
	if _, err = pasteFile.Write(content); err != nil {
		return
	}
	s.stats.makeSpaceFor(size)
	s.cache[id] = fileCache{
		header: genHeader(id, time.Now(), size),
		path:   pastePath,
	}
	return id, nil
}

func (s *FileStore) Delete(id ID) error {
	s.Lock()
	defer s.Unlock()
	cached, e := s.cache[id]
	if !e {
		return ErrPasteNotFound
	}
	delete(s.cache, id)
	s.stats.freeSpace(cached.header.Size)
	cached.reading.Wait()
	if err := os.Remove(cached.path); err != nil {
		return err
	}
	return nil
}

func (s *FileStore) Recover(pastePath string, fileInfo os.FileInfo, err error) error {
	if err != nil {
		return err
	}
	if fileInfo.IsDir() {
		return nil
	}
	parts := strings.Split(pastePath, string(filepath.Separator))
	if len(parts) != 2 {
		return errors.New("invalid number of directories at " + pastePath)
	}
	hexID := parts[0] + parts[1]
	id, err := IDFromString(hexID)
	if err != nil {
		return err
	}
	modTime := fileInfo.ModTime()
	deathTime := modTime.Add(lifeTime)
	if lifeTime > 0 {
		if deathTime.Before(startTime) {
			return os.Remove(pastePath)
		}
	}
	size := fileInfo.Size()
	s.Lock()
	defer s.Unlock()
	if !s.stats.hasSpaceFor(size) {
		return ErrReachedMax
	}
	s.stats.makeSpaceFor(size)
	lifeLeft := deathTime.Sub(startTime)
	cached := fileCache{
		header: genHeader(id, modTime, size),
		path:   pastePath,
	}
	s.cache[id] = cached
	SetupPasteDeletion(s, id, lifeLeft)
	return nil
}

func (s *FileStore) randomID() (id ID, err error) {
	for try := 0; try < randTries; try++ {
		if _, err := rand.Read(id[:]); err != nil {
			continue
		}
		if _, e := s.cache[id]; !e {
			return id, nil
		}
	}
	return id, ErrNoUnusedIDFound
}

func (s *FileStore) setupSubdir(h byte) error {
	dir := hex.EncodeToString([]byte{h})
	if stat, err := os.Stat(dir); err == nil {
		if !stat.IsDir() {
			return fmt.Errorf("%s/%s exists but is not a directory", s.dir, dir)
		}
		if err := filepath.Walk(dir, s.Recover); err != nil {
			return fmt.Errorf("cannot recover data directory %s/%s: %s", s.dir, dir, err)
		}
	} else if err := os.Mkdir(dir, 0700); err != nil {
		return fmt.Errorf("cannot create data directory %s/%s: %s", s.dir, dir, err)
	}
	return nil
}

func (s *FileStore) Report() string {
	s.Lock()
	defer s.Unlock()
	return s.stats.Report()
}
<commit_msg>storage_fs: catch more errors when writing to a file<commit_after>/* Copyright (c) 2014-2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"crypto/rand"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

type FileStore struct {
	sync.RWMutex
	cache map[ID]fileCache

	dir   string
	stats Stats
}

type fileCache struct {
	reading sync.WaitGroup
	header  Header
	path    string
}

type FileContent struct {
	file    *os.File
	reading *sync.WaitGroup
}

func (c FileContent) Read(p []byte) (n int, err error) {
	return c.file.Read(p)
}

func (c FileContent) ReadAt(p []byte, off int64) (n int, err error) {
	return c.file.ReadAt(p, off)
}

func (c FileContent) Seek(offset int64, whence int) (int64, error) {
	return c.file.Seek(offset, whence)
}

func (c FileContent) Close() error {
	err := c.file.Close()
	c.reading.Done()
	return err
}

func newFileStore(dir string) (s *FileStore, err error) {
	if err = os.MkdirAll(dir, 0700); err != nil {
		return nil, err
	}
	if err = os.Chdir(dir); err != nil {
		return nil, err
	}
	s = new(FileStore)
	s.dir = dir
	s.cache = make(map[ID]fileCache)
	for i := 0; i < 256; i++ {
		if err = s.setupSubdir(byte(i)); err != nil {
			return nil, err
		}
	}
	return
}

func (s *FileStore) Get(id ID) (Content, *Header, error) {
	s.RLock()
	defer s.RUnlock()
	cached, e := s.cache[id]
	if !e {
		return nil, nil, ErrPasteNotFound
	}
	f, err := os.Open(cached.path)
	if err != nil {
		return nil, nil, err
	}
	cached.reading.Add(1)
	return FileContent{f, &cached.reading}, &cached.header, nil
}

func writeNewFile(filename string, data []byte) error {
	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
	if err != nil {
		return err
	}
	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	if err1 := f.Close(); err == nil {
		err = err1
	}
	return err
}

func (s *FileStore) Put(content []byte) (id ID, err error) {
	s.Lock()
	defer s.Unlock()
	size := int64(len(content))
	if !s.stats.hasSpaceFor(size) {
		return id, ErrReachedMax
	}
	if id, err = s.randomID(); err != nil {
		return
	}
	hexID := id.String()
	pastePath := path.Join(hexID[:2], hexID[2:])
	if err = writeNewFile(pastePath, content); err != nil {
		return
	}
	s.stats.makeSpaceFor(size)
	s.cache[id] = fileCache{
		header: genHeader(id, time.Now(), size),
		path:   pastePath,
	}
	return id, nil
}

func (s *FileStore) Delete(id ID) error {
	s.Lock()
	defer s.Unlock()
	cached, e := s.cache[id]
	if !e {
		return ErrPasteNotFound
	}
	delete(s.cache, id)
	s.stats.freeSpace(cached.header.Size)
	cached.reading.Wait()
	if err := os.Remove(cached.path); err != nil {
		return err
	}
	return nil
}

func (s *FileStore) Recover(pastePath string, fileInfo os.FileInfo, err error) error {
	if err != nil {
		return err
	}
	if fileInfo.IsDir() {
		return nil
	}
	parts := strings.Split(pastePath, string(filepath.Separator))
	if len(parts) != 2 {
		return errors.New("invalid number of directories at " + pastePath)
	}
	hexID := parts[0] + parts[1]
	id, err := IDFromString(hexID)
	if err != nil {
		return err
	}
	modTime := fileInfo.ModTime()
	deathTime := modTime.Add(lifeTime)
	if lifeTime > 0 {
		if deathTime.Before(startTime) {
			return os.Remove(pastePath)
		}
	}
	size := fileInfo.Size()
	s.Lock()
	defer s.Unlock()
	if !s.stats.hasSpaceFor(size) {
		return ErrReachedMax
	}
	s.stats.makeSpaceFor(size)
	lifeLeft := deathTime.Sub(startTime)
	cached := fileCache{
		header: genHeader(id, modTime, size),
		path:   pastePath,
	}
	s.cache[id] = cached
	SetupPasteDeletion(s, id, lifeLeft)
	return nil
}

func (s *FileStore) randomID() (id ID, err error) {
	for try := 0; try < randTries; try++ {
		if _, err := rand.Read(id[:]); err != nil {
			continue
		}
		if _, e := s.cache[id]; !e {
			return id, nil
		}
	}
	return id, ErrNoUnusedIDFound
}

func (s *FileStore) setupSubdir(h byte) error {
	dir := hex.EncodeToString([]byte{h})
	if stat, err := os.Stat(dir); err == nil {
		if !stat.IsDir() {
			return fmt.Errorf("%s/%s exists but is not a directory", s.dir, dir)
		}
		if err := filepath.Walk(dir, s.Recover); err != nil {
			return fmt.Errorf("cannot recover data directory %s/%s: %s", s.dir, dir, err)
		}
	} else if err := os.Mkdir(dir, 0700); err != nil {
		return fmt.Errorf("cannot create data directory %s/%s: %s", s.dir, dir, err)
	}
	return nil
}

func (s *FileStore) Report() string {
	s.Lock()
	defer s.Unlock()
	return s.stats.Report()
}
<|endoftext|>


<commit_before>// Copyright e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sys

import "github.com/e-XpertSolutions/f5-rest-client/f5"

// CryptoCertConfigList holds a list of CryptoCert configuration.
type CryptoCertConfigList struct {
	Items    []CryptoCertConfig `json:"items,omitempty"`
	Kind     string             `json:"kind,omitempty"`
	SelfLink string             `json:"selflink,omitempty"`
}

// CryptoCertConfig holds the configuration of a single CryptoCert.
type CryptoCertConfig struct {
	APIRawValues struct {
		CertificateKeySize string `json:"certificateKeySize,omitempty"`
		Expiration         string `json:"expiration,omitempty"`
		PublicKeyType      string `json:"publicKeyType,omitempty"`
	} `json:"apiRawValues,omitempty"`
	Country      string `json:"country,omitempty"`
	FullPath     string `json:"fullPath,omitempty"`
	Generation   int    `json:"generation,omitempty"`
	Kind         string `json:"kind,omitempty"`
	Name         string `json:"name,omitempty"`
	Organization string `json:"organization,omitempty"`
	Ou           string `json:"ou,omitempty"`
	SelfLink     string `json:"selfLink,omitempty"`
}

// CryptoCertEndpoint represents the REST resource for managing CryptoCert.
const CryptoCertEndpoint = "/crypto/cert"

// CryptoCertResource provides an API to manage CryptoCert configurations.
type CryptoCertResource struct {
	c *f5.Client
}

// ListAll  lists all the CryptoCert configurations.
func (r *CryptoCertResource) ListAll() (*CryptoCertConfigList, error) {
	var list CryptoCertConfigList
	if err := r.c.ReadQuery(BasePath+CryptoCertEndpoint, &list); err != nil {
		return nil, err
	}
	return &list, nil
}

// Get a single CryptoCert configuration identified by id.
func (r *CryptoCertResource) Get(id string) (*CryptoCertConfig, error) {
	var item CryptoCertConfig
	if err := r.c.ReadQuery(BasePath+CryptoCertEndpoint, &item); err != nil {
		return nil, err
	}
	return &item, nil
}

// Create a new CryptoCert configuration.
func (r *CryptoCertResource) Create(item CryptoCertConfig) error {
	if err := r.c.ModQuery("POST", BasePath+CryptoCertEndpoint, item); err != nil {
		return err
	}
	return nil
}

// Edit a CryptoCert configuration identified by id.
func (r *CryptoCertResource) Edit(id string, item CryptoCertConfig) error {
	if err := r.c.ModQuery("PUT", BasePath+CryptoCertEndpoint+"/"+id, item); err != nil {
		return err
	}
	return nil
}

// Delete a single CryptoCert configuration identified by id.
func (r *CryptoCertResource) Delete(id string) error {
	if err := r.c.ModQuery("DELETE", BasePath+CryptoCertEndpoint+"/"+id, nil); err != nil {
		return err
	}
	return nil
}
<commit_msg>f5/sys: add CommonName and Fingerprint to the CryptoCertConfig<commit_after>// Copyright e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sys

import "github.com/e-XpertSolutions/f5-rest-client/f5"

// CryptoCertConfigList holds a list of CryptoCert configuration.
type CryptoCertConfigList struct {
	Items    []CryptoCertConfig `json:"items,omitempty"`
	Kind     string             `json:"kind,omitempty"`
	SelfLink string             `json:"selflink,omitempty"`
}

// CryptoCertConfig holds the configuration of a single CryptoCert.
type CryptoCertConfig struct {
	APIRawValues struct {
		CertificateKeySize string `json:"certificateKeySize,omitempty"`
		Expiration         string `json:"expiration,omitempty"`
		PublicKeyType      string `json:"publicKeyType,omitempty"`
	} `json:"apiRawValues,omitempty"`
	Country      string `json:"country,omitempty"`
	CommonName   string `json:"commonName,omitempty"`
	Fingerprint  string `json:"fingerprint,omitempty"`
	FullPath     string `json:"fullPath,omitempty"`
	Generation   int    `json:"generation,omitempty"`
	Kind         string `json:"kind,omitempty"`
	Name         string `json:"name,omitempty"`
	Organization string `json:"organization,omitempty"`
	Ou           string `json:"ou,omitempty"`
	SelfLink     string `json:"selfLink,omitempty"`
}

// CryptoCertEndpoint represents the REST resource for managing CryptoCert.
const CryptoCertEndpoint = "/crypto/cert"

// CryptoCertResource provides an API to manage CryptoCert configurations.
type CryptoCertResource struct {
	c *f5.Client
}

// ListAll  lists all the CryptoCert configurations.
func (r *CryptoCertResource) ListAll() (*CryptoCertConfigList, error) {
	var list CryptoCertConfigList
	if err := r.c.ReadQuery(BasePath+CryptoCertEndpoint, &list); err != nil {
		return nil, err
	}
	return &list, nil
}

// Get a single CryptoCert configuration identified by id.
func (r *CryptoCertResource) Get(id string) (*CryptoCertConfig, error) {
	var item CryptoCertConfig
	if err := r.c.ReadQuery(BasePath+CryptoCertEndpoint, &item); err != nil {
		return nil, err
	}
	return &item, nil
}

// Create a new CryptoCert configuration.
func (r *CryptoCertResource) Create(item CryptoCertConfig) error {
	if err := r.c.ModQuery("POST", BasePath+CryptoCertEndpoint, item); err != nil {
		return err
	}
	return nil
}

// Edit a CryptoCert configuration identified by id.
func (r *CryptoCertResource) Edit(id string, item CryptoCertConfig) error {
	if err := r.c.ModQuery("PUT", BasePath+CryptoCertEndpoint+"/"+id, item); err != nil {
		return err
	}
	return nil
}

// Delete a single CryptoCert configuration identified by id.
func (r *CryptoCertResource) Delete(id string) error {
	if err := r.c.ModQuery("DELETE", BasePath+CryptoCertEndpoint+"/"+id, nil); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package basal_test

import (
	"time"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"

	"github.com/tidepool-org/platform/data/types/basal"
	dataTypesBasalTest "github.com/tidepool-org/platform/data/types/basal/test"
	dataTypesTest "github.com/tidepool-org/platform/data/types/test"
	errorsTest "github.com/tidepool-org/platform/errors/test"
	"github.com/tidepool-org/platform/pointer"
	"github.com/tidepool-org/platform/structure"
	structureValidator "github.com/tidepool-org/platform/structure/validator"
)

var _ = Describe("Basal", func() {
	It("Type is expected", func() {
		Expect(basal.Type).To(Equal("basal"))
	})

	Context("New", func() {
		It("creates a new datum with all values initialized", func() {
			deliveryType := dataTypesTest.NewType()
			datum := basal.New(deliveryType)
			Expect(datum.Type).To(Equal("basal"))
			Expect(datum.DeliveryType).To(Equal(deliveryType))
		})
	})

	Context("with new datum", func() {
		var deliveryType string
		var datum basal.Basal

		BeforeEach(func() {
			deliveryType = dataTypesTest.NewType()
			datum = basal.New(deliveryType)
		})

		Context("Meta", func() {
			It("returns the meta with delivery type", func() {
				Expect(datum.Meta()).To(Equal(&basal.Meta{Type: "basal", DeliveryType: deliveryType}))
			})
		})
	})

	Context("Basal", func() {
		Context("Parse", func() {
			// TODO
		})

		Context("Validate", func() {
			DescribeTable("validates the datum",
				func(mutator func(datum *basal.Basal), expectedErrors ...error) {
					datum := dataTypesBasalTest.NewBasal()
					mutator(datum)
					dataTypesTest.ValidateWithExpectedOrigins(datum, structure.Origins(), expectedErrors...)
				},
				Entry("succeeds",
					func(datum *basal.Basal) {},
				),
				Entry("type missing",
					func(datum *basal.Basal) { datum.Type = "" },
					errorsTest.WithPointerSource(structureValidator.ErrorValueEmpty(), "/type"),
				),
				Entry("type invalid",
					func(datum *basal.Basal) { datum.Type = "invalid" },
					errorsTest.WithPointerSource(structureValidator.ErrorValueNotEqualTo("invalid", "basal"), "/type"),
				),
				Entry("type basal",
					func(datum *basal.Basal) { datum.Type = "basal" },
				),
				Entry("delivery type missing",
					func(datum *basal.Basal) { datum.DeliveryType = "" },
					errorsTest.WithPointerSource(structureValidator.ErrorValueEmpty(), "/deliveryType"),
				),
				Entry("delivery type valid",
					func(datum *basal.Basal) { datum.DeliveryType = dataTypesTest.NewType() },
				),
				Entry("multiple errors",
					func(datum *basal.Basal) {
						datum.Type = "invalid"
						datum.DeliveryType = ""
					},
					errorsTest.WithPointerSource(structureValidator.ErrorValueNotEqualTo("invalid", "basal"), "/type"),
					errorsTest.WithPointerSource(structureValidator.ErrorValueEmpty(), "/deliveryType"),
				),
			)
		})

		Context("IdentityFields", func() {
			var datum *basal.Basal

			BeforeEach(func() {
				datum = dataTypesBasalTest.NewBasal()
			})

			It("returns error if user id is missing", func() {
				datum.UserID = nil
				identityFields, err := datum.IdentityFields()
				Expect(err).To(MatchError("user id is missing"))
				Expect(identityFields).To(BeEmpty())
			})

			It("returns error if user id is empty", func() {
				datum.UserID = pointer.FromString("")
				identityFields, err := datum.IdentityFields()
				Expect(err).To(MatchError("user id is empty"))
				Expect(identityFields).To(BeEmpty())
			})

			It("returns error if delivery type is empty", func() {
				datum.DeliveryType = ""
				identityFields, err := datum.IdentityFields()
				Expect(err).To(MatchError("delivery type is empty"))
				Expect(identityFields).To(BeEmpty())
			})

			It("returns the expected identity fields", func() {
				identityFields, err := datum.IdentityFields()
				Expect(err).ToNot(HaveOccurred())
				Expect(identityFields).To(Equal([]string{*datum.UserID, *datum.DeviceID, (*datum.Time).Format(time.RFC3339Nano), datum.Type, datum.DeliveryType}))
			})
		})
	})

	Context("ParseDeliveryType", func() {
		// TODO
	})
})
<commit_msg>more formatting<commit_after>package basal_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo/extensions/table"
	. "github.com/onsi/gomega"
	"time"

	"github.com/tidepool-org/platform/data/types/basal"
	dataTypesBasalTest "github.com/tidepool-org/platform/data/types/basal/test"
	dataTypesTest "github.com/tidepool-org/platform/data/types/test"
	errorsTest "github.com/tidepool-org/platform/errors/test"
	"github.com/tidepool-org/platform/pointer"
	"github.com/tidepool-org/platform/structure"
	structureValidator "github.com/tidepool-org/platform/structure/validator"
)

var _ = Describe("Basal", func() {
	It("Type is expected", func() {
		Expect(basal.Type).To(Equal("basal"))
	})

	Context("New", func() {
		It("creates a new datum with all values initialized", func() {
			deliveryType := dataTypesTest.NewType()
			datum := basal.New(deliveryType)
			Expect(datum.Type).To(Equal("basal"))
			Expect(datum.DeliveryType).To(Equal(deliveryType))
		})
	})

	Context("with new datum", func() {
		var deliveryType string
		var datum basal.Basal

		BeforeEach(func() {
			deliveryType = dataTypesTest.NewType()
			datum = basal.New(deliveryType)
		})

		Context("Meta", func() {
			It("returns the meta with delivery type", func() {
				Expect(datum.Meta()).To(Equal(&basal.Meta{Type: "basal", DeliveryType: deliveryType}))
			})
		})
	})

	Context("Basal", func() {
		Context("Parse", func() {
			// TODO
		})

		Context("Validate", func() {
			DescribeTable("validates the datum",
				func(mutator func(datum *basal.Basal), expectedErrors ...error) {
					datum := dataTypesBasalTest.NewBasal()
					mutator(datum)
					dataTypesTest.ValidateWithExpectedOrigins(datum, structure.Origins(), expectedErrors...)
				},
				Entry("succeeds",
					func(datum *basal.Basal) {},
				),
				Entry("type missing",
					func(datum *basal.Basal) { datum.Type = "" },
					errorsTest.WithPointerSource(structureValidator.ErrorValueEmpty(), "/type"),
				),
				Entry("type invalid",
					func(datum *basal.Basal) { datum.Type = "invalid" },
					errorsTest.WithPointerSource(structureValidator.ErrorValueNotEqualTo("invalid", "basal"), "/type"),
				),
				Entry("type basal",
					func(datum *basal.Basal) { datum.Type = "basal" },
				),
				Entry("delivery type missing",
					func(datum *basal.Basal) { datum.DeliveryType = "" },
					errorsTest.WithPointerSource(structureValidator.ErrorValueEmpty(), "/deliveryType"),
				),
				Entry("delivery type valid",
					func(datum *basal.Basal) { datum.DeliveryType = dataTypesTest.NewType() },
				),
				Entry("multiple errors",
					func(datum *basal.Basal) {
						datum.Type = "invalid"
						datum.DeliveryType = ""
					},
					errorsTest.WithPointerSource(structureValidator.ErrorValueNotEqualTo("invalid", "basal"), "/type"),
					errorsTest.WithPointerSource(structureValidator.ErrorValueEmpty(), "/deliveryType"),
				),
			)
		})

		Context("IdentityFields", func() {
			var datum *basal.Basal

			BeforeEach(func() {
				datum = dataTypesBasalTest.NewBasal()
			})

			It("returns error if user id is missing", func() {
				datum.UserID = nil
				identityFields, err := datum.IdentityFields()
				Expect(err).To(MatchError("user id is missing"))
				Expect(identityFields).To(BeEmpty())
			})

			It("returns error if user id is empty", func() {
				datum.UserID = pointer.FromString("")
				identityFields, err := datum.IdentityFields()
				Expect(err).To(MatchError("user id is empty"))
				Expect(identityFields).To(BeEmpty())
			})

			It("returns error if delivery type is empty", func() {
				datum.DeliveryType = ""
				identityFields, err := datum.IdentityFields()
				Expect(err).To(MatchError("delivery type is empty"))
				Expect(identityFields).To(BeEmpty())
			})

			It("returns the expected identity fields", func() {
				identityFields, err := datum.IdentityFields()
				Expect(err).ToNot(HaveOccurred())
				Expect(identityFields).To(Equal([]string{*datum.UserID, *datum.DeviceID, (*datum.Time).Format(time.RFC3339Nano), datum.Type, datum.DeliveryType}))
			})
		})
	})

	Context("ParseDeliveryType", func() {
		// TODO
	})
})
<|endoftext|>


<commit_before>// Copyright 2014 The goyy Authors.  All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package xsql_test

import (
	"gopkg.in/goyy/goyy.v0/comm/log"
	"gopkg.in/goyy/goyy.v0/data/dialect"
	"gopkg.in/goyy/goyy.v0/data/domain"
	"gopkg.in/goyy/goyy.v0/data/entity"
	"gopkg.in/goyy/goyy.v0/util/times"
	"strconv"
	"testing"
)

var created = times.NowUnix()

func buildUser(i string) entity.Interface {
	user := NewUser()
	user.SetCode(i)
	user.SetName(i)
	user.SetPassword(i)
	user.SetMemo(i)
	user.SetGenre(i)
	user.SetStatus(i)
	user.SetRoles(i)
	user.SetPosts(i)
	user.SetOrg(i)
	user.SetArea(i)
	user.SetCreater(i)
	user.SetCreated(created)
	user.SetModifier(i)
	user.SetModified(times.NowUnix())
	user.SetVersion(0)
	user.SetDeletion(0)
	return user
}

func TestSessionDelete(t *testing.T) {
	log.SetPriority(log.Perror)
	var dml string
	if session.DBType() == dialect.MYSQL {
		dml = "delete from users where version = ?"
	} else {
		dml = "delete from users where version = :1"
	}
	session.Exec(dml, 0)
}

func TestSessionInsert(t *testing.T) {
	session.Insert(buildUser("01"))
	session.Insert(buildUser("02"))
	session.Insert(buildUser("03"))
	session.Insert(buildUser("04"))
	session.Insert(buildUser("05"))
	session.Insert(buildUser("06"))
	session.Insert(buildUser("07"))
	session.Insert(buildUser("08"))
	session.Insert(buildUser("09"))
	session.Insert(buildUser("10"))
	session.Insert(buildUser("11"))
	session.Insert(buildUser("12"))
	session.Insert(buildUser("13"))
	session.Insert(buildUser("14"))
	session.Insert(buildUser("15"))
	session.Insert(buildUser("16"))
	session.Insert(buildUser("17"))
	session.Insert(buildUser("18"))
	session.Insert(buildUser("19"))
	session.Insert(buildUser("20"))
	session.Insert(buildUser("21"))
	session.Insert(buildUser("22"))
	session.Insert(buildUser("23"))
	session.Insert(buildUser("24"))
	session.Insert(buildUser("25"))
}

func TestSessionGet(t *testing.T) {
	user := NewUser()
	user.SetId("aa")
	expected := "aa"
	if _ = session.Get(user); user.Name() != expected {
		t.Errorf(`session.Get():"%v", want:"%v"`, user.Name(), expected)
	}
}

func TestSessionSelectOne(t *testing.T) {
	s, _ := domain.NewSift("sNameEQ", "11")
	user := NewUser()
	err := session.SelectOne(user, s)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := "11"
	if out := user.Creater(); out != expected {
		t.Errorf(`session.SelectOne():"%v", want:"%v"`, out, expected)
	}
}

func TestSessionSelectList(t *testing.T) {
	s1, _ := domain.NewSift("sNameGT", "11")
	s2, _ := domain.NewSift("sVersionEQ", "0")
	s3, _ := domain.NewSift("sNameOA", "asc")
	users := NewUserEntities(20)
	err := session.SelectList(users, s1, s2, s3)
	if err != nil {
		t.Error(err.Error())
		return
	}
	got := 14
	if out := users.Len(); out != got {
		t.Errorf(`session.SelectList().Len():"%v", want:"%v"`, out, got)
	}
	expected := "12"
	if out := users.Index(0).(*User).Name(); out != expected {
		t.Errorf(`session.SelectList().Index(0):"%v", want:"%v"`, out, expected)
	}
}

func TestSessionSelectPage(t *testing.T) {
	sVersionEQ, _ := domain.NewSift("sVersionEQ", "0")
	sIdOA, _ := domain.NewSift("sIdOA", "asc")
	pageable := domain.NewPageable(2, 10)
	content := NewUserEntities(30)
	out, err := session.SelectPage(content, pageable, sVersionEQ, sIdOA)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := 25
	if out.TotalElements() != expected {
		t.Errorf(`page.TotalElements():"%v", want:"%v"`, out.TotalElements(), expected)
	}
	expected = 3
	if out.TotalPages() != expected {
		t.Errorf(`page.TotalPages():"%v", want:"%v"`, out.TotalPages(), expected)
	}
	expected = 2
	if out.PageNo() != expected {
		t.Errorf(`page.PageNo():"%v", want:"%v"`, out.PageNo(), expected)
	}
	expected = 10
	if out.PageSize() != expected {
		t.Errorf(`page.PageSize():"%v", want:"%v"`, out.PageSize(), expected)
	}
	want := "11"
	name := out.Content().Index(0).(*User).Name()
	if name != want {
		t.Errorf(`page.Content():"%v", want:"%v"`, name, want)
	}
}

func TestSessionQueryRows(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select * from users where name like ?"
	} else {
		dql = "select * from users where name like :1"
	}
	users := NewUserEntities(30)
	err := session.Query(dql, "2%").Rows(users)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := 6
	if out := users.Len(); out != expected {
		t.Errorf(`query.Rows():"%v", want:"%v"`, out, expected)
	}
	for i := 0; i < users.Len(); i++ {
		want := strconv.Itoa(20 + i)
		if out := users.Value(i); out.Code() != want {
			t.Errorf(`get(%v).Code():"%v", want:"%v"`, i, out.Code(), want)
		}
	}
}

func TestSessionQueryRow(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select * from users where name = ?"
	} else {
		dql = "select * from users where name = :1"
	}
	user := NewUser()
	err := session.Query(dql, "12").Row(user)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := "12"
	if out := user.Creater(); out != expected {
		t.Errorf(`query.Row():"%v", want:"%v"`, out, expected)
	}
}

func TestSessionQueryInt(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select count(*) from users where name like ?"
	} else {
		dql = "select count(*) from users where name like :1"
	}
	out, err := session.Query(dql, "1%").Int()
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := 10
	if out != expected {
		t.Errorf(`query.Int():"%v", want:"%v"`, out, expected)
	}
}

func TestSessionQueryStr(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select code from users where name = ?"
	} else {
		dql = "select code from users where name = :1"
	}
	out, err := session.Query(dql, "03").Str()
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := "03"
	if out != expected {
		t.Errorf(`query.Str():"%v", want:"%v"`, out, expected)
	}
}

func TestSessionQueryTime(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select created from users where name = ?"
	} else {
		dql = "select created from users where name = :1"
	}
	out, err := session.Query(dql, "03").Int()
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := times.FormatUnixYYMDHMS(created)
	if times.FormatUnixYYMDHMS(int64(out)) != expected {
		t.Errorf(`query.Time():"%v", want:"%v"`, times.FormatUnixYYMDHMS(int64(out)), expected)
	}
}

func TestSessionQueryPage(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select * from users where version = ? order by id"
	} else {
		dql = "select * from users where version = :1 order by id"
	}
	pageable := domain.NewPageable(2, 10)
	content := NewUserEntities(30)
	out, err := session.Query(dql, 0).Page(content, pageable)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := 25
	if out.TotalElements() != expected {
		t.Errorf(`page.TotalElements():"%v", want:"%v"`, out.TotalElements(), expected)
	}
	expected = 3
	if out.TotalPages() != expected {
		t.Errorf(`page.TotalPages():"%v", want:"%v"`, out.TotalPages(), expected)
	}
	expected = 2
	if out.PageNo() != expected {
		t.Errorf(`page.PageNo():"%v", want:"%v"`, out.PageNo(), expected)
	}
	expected = 10
	if out.PageSize() != expected {
		t.Errorf(`page.PageSize():"%v", want:"%v"`, out.PageSize(), expected)
	}
	want := "11"
	name := out.Content().Index(0).(*User).Name()
	if name != want {
		t.Errorf(`page.Content():"%v", want:"%v"`, name, want)
	}
}

func TestSessionUpdate(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select * from users where name = ?"
	} else {
		dql = "select * from users where name = :1"
	}
	user := NewUser()
	err := session.Query(dql, "22").Row(user)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := "user2"
	user.SetCode(expected)
	session.Update(user)
	user = NewUser()
	err = session.Query(dql, "22").Row(user)
	if err != nil {
		t.Error(err.Error())
		return
	}
	if out := user.Code(); out != expected {
		t.Errorf(`query.Update:"%v", want:"%v"`, out, expected)
	}
}

func TestSessionDisable(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select * from users where name = ?"
	} else {
		dql = "select * from users where name = :1"
	}
	user := NewUser()
	err := session.Query(dql, "23").Row(user)
	if err != nil {
		t.Error(err.Error())
		return
	}
	session.Disable(user)
	expected := entity.DeletionDisable
	user = NewUser()
	err = session.Query(dql, "23").Row(user)
	if err != nil {
		t.Error(err.Error())
		return
	}
	if out := user.Deletion(); out != expected {
		t.Errorf(`query.Disable():"%v", want:"%v"`, out, expected)
	}
}
<commit_msg>Add TestSessionQueryIn func<commit_after>// Copyright 2014 The goyy Authors.  All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package xsql_test

import (
	"gopkg.in/goyy/goyy.v0/comm/log"
	"gopkg.in/goyy/goyy.v0/data/dialect"
	"gopkg.in/goyy/goyy.v0/data/domain"
	"gopkg.in/goyy/goyy.v0/data/entity"
	"gopkg.in/goyy/goyy.v0/util/times"
	"strconv"
	"testing"
)

var created = times.NowUnix()

func buildUser(i string) entity.Interface {
	user := NewUser()
	user.SetCode(i)
	user.SetName(i)
	user.SetPassword(i)
	user.SetMemo(i)
	user.SetGenre(i)
	user.SetStatus(i)
	user.SetRoles(i)
	user.SetPosts(i)
	user.SetOrg(i)
	user.SetArea(i)
	user.SetCreater(i)
	user.SetCreated(created)
	user.SetModifier(i)
	user.SetModified(times.NowUnix())
	user.SetVersion(0)
	user.SetDeletion(0)
	return user
}

func TestSessionDelete(t *testing.T) {
	log.SetPriority(log.Perror)
	var dml string
	if session.DBType() == dialect.MYSQL {
		dml = "delete from users where version = ?"
	} else {
		dml = "delete from users where version = :1"
	}
	session.Exec(dml, 0)
}

func TestSessionInsert(t *testing.T) {
	session.Insert(buildUser("01"))
	session.Insert(buildUser("02"))
	session.Insert(buildUser("03"))
	session.Insert(buildUser("04"))
	session.Insert(buildUser("05"))
	session.Insert(buildUser("06"))
	session.Insert(buildUser("07"))
	session.Insert(buildUser("08"))
	session.Insert(buildUser("09"))
	session.Insert(buildUser("10"))
	session.Insert(buildUser("11"))
	session.Insert(buildUser("12"))
	session.Insert(buildUser("13"))
	session.Insert(buildUser("14"))
	session.Insert(buildUser("15"))
	session.Insert(buildUser("16"))
	session.Insert(buildUser("17"))
	session.Insert(buildUser("18"))
	session.Insert(buildUser("19"))
	session.Insert(buildUser("20"))
	session.Insert(buildUser("21"))
	session.Insert(buildUser("22"))
	session.Insert(buildUser("23"))
	session.Insert(buildUser("24"))
	session.Insert(buildUser("25"))
}

func TestSessionGet(t *testing.T) {
	user := NewUser()
	user.SetId("aa")
	expected := "aa"
	if _ = session.Get(user); user.Name() != expected {
		t.Errorf(`session.Get():"%v", want:"%v"`, user.Name(), expected)
	}
}

func TestSessionSelectOne(t *testing.T) {
	s, _ := domain.NewSift("sNameEQ", "11")
	user := NewUser()
	err := session.SelectOne(user, s)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := "11"
	if out := user.Creater(); out != expected {
		t.Errorf(`session.SelectOne():"%v", want:"%v"`, out, expected)
	}
}

func TestSessionSelectList(t *testing.T) {
	s1, _ := domain.NewSift("sNameGT", "11")
	s2, _ := domain.NewSift("sVersionEQ", "0")
	s3, _ := domain.NewSift("sNameOA", "asc")
	users := NewUserEntities(20)
	err := session.SelectList(users, s1, s2, s3)
	if err != nil {
		t.Error(err.Error())
		return
	}
	got := 14
	if out := users.Len(); out != got {
		t.Errorf(`session.SelectList().Len():"%v", want:"%v"`, out, got)
	}
	expected := "12"
	if out := users.Index(0).(*User).Name(); out != expected {
		t.Errorf(`session.SelectList().Index(0):"%v", want:"%v"`, out, expected)
	}
}

func TestSessionSelectPage(t *testing.T) {
	sVersionEQ, _ := domain.NewSift("sVersionEQ", "0")
	sIdOA, _ := domain.NewSift("sIdOA", "asc")
	pageable := domain.NewPageable(2, 10)
	content := NewUserEntities(30)
	out, err := session.SelectPage(content, pageable, sVersionEQ, sIdOA)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := 25
	if out.TotalElements() != expected {
		t.Errorf(`page.TotalElements():"%v", want:"%v"`, out.TotalElements(), expected)
	}
	expected = 3
	if out.TotalPages() != expected {
		t.Errorf(`page.TotalPages():"%v", want:"%v"`, out.TotalPages(), expected)
	}
	expected = 2
	if out.PageNo() != expected {
		t.Errorf(`page.PageNo():"%v", want:"%v"`, out.PageNo(), expected)
	}
	expected = 10
	if out.PageSize() != expected {
		t.Errorf(`page.PageSize():"%v", want:"%v"`, out.PageSize(), expected)
	}
	want := "11"
	name := out.Content().Index(0).(*User).Name()
	if name != want {
		t.Errorf(`page.Content():"%v", want:"%v"`, name, want)
	}
}

func TestSessionQueryRows(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select * from users where name like ?"
	} else {
		dql = "select * from users where name like :1"
	}
	users := NewUserEntities(30)
	err := session.Query(dql, "2%").Rows(users)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := 6
	if out := users.Len(); out != expected {
		t.Errorf(`query.Rows():"%v", want:"%v"`, out, expected)
	}
	for i := 0; i < users.Len(); i++ {
		want := strconv.Itoa(20 + i)
		if out := users.Value(i); out.Code() != want {
			t.Errorf(`get(%v).Code():"%v", want:"%v"`, i, out.Code(), want)
		}
	}
}

func TestSessionQueryRow(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select * from users where name = ?"
	} else {
		dql = "select * from users where name = :1"
	}
	user := NewUser()
	err := session.Query(dql, "12").Row(user)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := "12"
	if out := user.Creater(); out != expected {
		t.Errorf(`query.Row():"%v", want:"%v"`, out, expected)
	}
}

func TestSessionQueryInt(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select count(*) from users where name like ?"
	} else {
		dql = "select count(*) from users where name like :1"
	}
	out, err := session.Query(dql, "1%").Int()
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := 10
	if out != expected {
		t.Errorf(`query.Int():"%v", want:"%v"`, out, expected)
	}
}

func TestSessionQueryStr(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select code from users where name = ?"
	} else {
		dql = "select code from users where name = :1"
	}
	out, err := session.Query(dql, "03").Str()
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := "03"
	if out != expected {
		t.Errorf(`query.Str():"%v", want:"%v"`, out, expected)
	}
}

func TestSessionQueryTime(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select created from users where name = ?"
	} else {
		dql = "select created from users where name = :1"
	}
	out, err := session.Query(dql, "03").Int()
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := times.FormatUnixYYMDHMS(created)
	if times.FormatUnixYYMDHMS(int64(out)) != expected {
		t.Errorf(`query.Time():"%v", want:"%v"`, times.FormatUnixYYMDHMS(int64(out)), expected)
	}
}

func TestSessionQueryIn(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select count(*) from users where name in (?,?)"
	} else {
		dql = "select count(*) from users where name in (:1,:2)"
	}
	out, err := session.Query(dql, "01", "02").Int()
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := 2
	if out != expected {
		t.Errorf(`query:in:"%v", want:"%v"`, out, expected)
	}
}

func TestSessionQueryPage(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select * from users where version = ? order by id"
	} else {
		dql = "select * from users where version = :1 order by id"
	}
	pageable := domain.NewPageable(2, 10)
	content := NewUserEntities(30)
	out, err := session.Query(dql, 0).Page(content, pageable)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := 25
	if out.TotalElements() != expected {
		t.Errorf(`page.TotalElements():"%v", want:"%v"`, out.TotalElements(), expected)
	}
	expected = 3
	if out.TotalPages() != expected {
		t.Errorf(`page.TotalPages():"%v", want:"%v"`, out.TotalPages(), expected)
	}
	expected = 2
	if out.PageNo() != expected {
		t.Errorf(`page.PageNo():"%v", want:"%v"`, out.PageNo(), expected)
	}
	expected = 10
	if out.PageSize() != expected {
		t.Errorf(`page.PageSize():"%v", want:"%v"`, out.PageSize(), expected)
	}
	want := "11"
	name := out.Content().Index(0).(*User).Name()
	if name != want {
		t.Errorf(`page.Content():"%v", want:"%v"`, name, want)
	}
}

func TestSessionUpdate(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select * from users where name = ?"
	} else {
		dql = "select * from users where name = :1"
	}
	user := NewUser()
	err := session.Query(dql, "22").Row(user)
	if err != nil {
		t.Error(err.Error())
		return
	}
	expected := "user2"
	user.SetCode(expected)
	session.Update(user)
	user = NewUser()
	err = session.Query(dql, "22").Row(user)
	if err != nil {
		t.Error(err.Error())
		return
	}
	if out := user.Code(); out != expected {
		t.Errorf(`query.Update:"%v", want:"%v"`, out, expected)
	}
}

func TestSessionDisable(t *testing.T) {
	var dql string
	if session.DBType() == dialect.MYSQL {
		dql = "select * from users where name = ?"
	} else {
		dql = "select * from users where name = :1"
	}
	user := NewUser()
	err := session.Query(dql, "23").Row(user)
	if err != nil {
		t.Error(err.Error())
		return
	}
	session.Disable(user)
	expected := entity.DeletionDisable
	user = NewUser()
	err = session.Query(dql, "23").Row(user)
	if err != nil {
		t.Error(err.Error())
		return
	}
	if out := user.Deletion(); out != expected {
		t.Errorf(`query.Disable():"%v", want:"%v"`, out, expected)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fakestorage

import (
	"compress/gzip"
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httptest"
	"sync"

	"cloud.google.com/go/storage"
	"github.com/fsouza/fake-gcs-server/internal/backend"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"google.golang.org/api/option"
)

const defaultPublicHost = "storage.googleapis.com"

// Server is the fake server.
//
// It provides a fake implementation of the Google Cloud Storage API.
type Server struct {
	backend     backend.Storage
	uploads     sync.Map
	transport   http.RoundTripper
	ts          *httptest.Server
	mux         *mux.Router
	options     Options
	externalURL string
	publicHost  string
}

// NewServer creates a new instance of the server, pre-loaded with the given
// objects.
func NewServer(objects []Object) *Server {
	s, _ := NewServerWithOptions(Options{
		InitialObjects: objects,
	})
	return s
}

// NewServerWithHostPort creates a new server that listens on a custom host and port
//
// Deprecated: use NewServerWithOptions.
func NewServerWithHostPort(objects []Object, host string, port uint16) (*Server, error) {
	return NewServerWithOptions(Options{
		InitialObjects: objects,
		Host:           host,
		Port:           port,
	})
}

// Options are used to configure the server on creation.
type Options struct {
	InitialObjects []Object
	StorageRoot    string
	Scheme         string
	Host           string
	Port           uint16

	// when set to true, the server will not actually start a TCP listener,
	// client requests will get processed by an internal mocked transport.
	NoListener bool

	// Optional external URL, such as https://gcs.127.0.0.1.nip.io:4443
	// Returned in the Location header for resumable uploads
	// The "real" value is https://www.googleapis.com, the JSON API
	// The default is whatever the server is bound to, such as https://0.0.0.0:4443
	ExternalURL string

	// Optional URL for public access
	// An example is "storage.gcs.127.0.0.1.nip.io:4443", which will configure
	// the server to serve objects at:
	// https://storage.gcs.127.0.0.1.nip.io:4443/<bucket>/<object>
	// https://<bucket>.storage.gcs.127.0.0.1.nip.io:4443>/<bucket>/<object>
	// If unset, the default is "storage.googleapis.com", the XML API
	PublicHost string

	// Optional list of headers to add to the CORS header allowlist
	// An example is "X-Goog-Meta-Uploader", which will allow a
	// custom metadata header named "X-Goog-Meta-Uploader" to be
	// sent through the browser
	AllowedCORSHeaders []string

	// Destination for writing log.
	Writer io.Writer
}

// NewServerWithOptions creates a new server configured according to the
// provided options.
func NewServerWithOptions(options Options) (*Server, error) {
	s, err := newServer(options)
	if err != nil {
		return nil, err
	}

	allowedHeaders := []string{"Content-Type", "Content-Encoding", "Range"}
	allowedHeaders = append(allowedHeaders, options.AllowedCORSHeaders...)

	cors := handlers.CORS(
		handlers.AllowedMethods([]string{
			http.MethodHead,
			http.MethodGet,
			http.MethodPost,
			http.MethodPut,
			http.MethodPatch,
			http.MethodDelete,
		}),
		handlers.AllowedHeaders(allowedHeaders),
		handlers.AllowedOrigins([]string{"*"}),
		handlers.AllowCredentials(),
	)

	handler := cors(s.mux)
	if options.Writer != nil {
		handler = handlers.LoggingHandler(options.Writer, handler)
	}
	handler = requestCompressHandler(handler)
	s.transport = &muxTransport{handler: handler}
	if options.NoListener {
		return s, nil
	}

	s.ts = httptest.NewUnstartedServer(handler)
	startFunc := s.ts.StartTLS
	if options.Scheme == "http" {
		startFunc = s.ts.Start
	}
	if options.Port != 0 {
		addr := fmt.Sprintf("%s:%d", options.Host, options.Port)
		l, err := net.Listen("tcp", addr)
		if err != nil {
			return nil, err
		}
		s.ts.Listener.Close()
		s.ts.Listener = l
	}
	startFunc()

	return s, nil
}

func newServer(options Options) (*Server, error) {
	backendObjects := toBackendObjects(options.InitialObjects)
	var backendStorage backend.Storage
	var err error
	if options.StorageRoot != "" {
		backendStorage, err = backend.NewStorageFS(backendObjects, options.StorageRoot)
	} else {
		backendStorage = backend.NewStorageMemory(backendObjects)
	}
	if err != nil {
		return nil, err
	}
	publicHost := options.PublicHost
	if publicHost == "" {
		publicHost = defaultPublicHost
	}
	s := Server{
		backend:     backendStorage,
		uploads:     sync.Map{},
		externalURL: options.ExternalURL,
		publicHost:  publicHost,
		options:     options,
	}
	s.buildMuxer()
	return &s, nil
}

func (s *Server) buildMuxer() {
	const apiPrefix = "/storage/v1"
	s.mux = mux.NewRouter()

	routers := []*mux.Router{
		s.mux.PathPrefix(apiPrefix).Subrouter(),
		s.mux.Host(s.publicHost).PathPrefix(apiPrefix).Subrouter(),
	}

	for _, r := range routers {
		r.Path("/b").Methods("GET").HandlerFunc(jsonToHTTPHandler(s.listBuckets))
		r.Path("/b").Methods("POST").HandlerFunc(jsonToHTTPHandler(s.createBucketByPost))
		r.Path("/b/{bucketName}").Methods("GET").HandlerFunc(jsonToHTTPHandler(s.getBucket))
		r.Path("/b/{bucketName}").Methods("DELETE").HandlerFunc(jsonToHTTPHandler(s.deleteBucket))
		r.Path("/b/{bucketName}/o").Methods("GET").HandlerFunc(jsonToHTTPHandler(s.listObjects))
		r.Path("/b/{bucketName}/o").Methods("POST").HandlerFunc(jsonToHTTPHandler(s.insertObject))
		r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("PATCH").HandlerFunc(jsonToHTTPHandler(s.patchObject))
		r.Path("/b/{bucketName}/o/{objectName:.+}/acl").Methods("GET").HandlerFunc(jsonToHTTPHandler(s.listObjectACL))
		r.Path("/b/{bucketName}/o/{objectName:.+}/acl/{entity}").Methods("PUT").HandlerFunc(jsonToHTTPHandler(s.setObjectACL))
		r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("GET").HandlerFunc(s.getObject)
		r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("DELETE").HandlerFunc(jsonToHTTPHandler(s.deleteObject))
		r.Path("/b/{sourceBucket}/o/{sourceObject:.+}/rewriteTo/b/{destinationBucket}/o/{destinationObject:.+}").HandlerFunc(jsonToHTTPHandler(s.rewriteObject))
	}

	bucketHost := fmt.Sprintf("{bucketName}.%s", s.publicHost)
	s.mux.Host(bucketHost).Path("/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
	s.mux.Path("/download/storage/v1/b/{bucketName}/o/{objectName:.+}").Methods("GET").HandlerFunc(s.downloadObject)
	s.mux.Path("/upload/storage/v1/b/{bucketName}/o").Methods("POST").HandlerFunc(jsonToHTTPHandler(s.insertObject))
	s.mux.Path("/upload/resumable/{uploadId}").Methods("PUT", "POST").HandlerFunc(jsonToHTTPHandler(s.uploadFileContent))

	s.mux.Host(s.publicHost).Path("/{bucketName}/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
	s.mux.Host("{bucketName:.+}").Path("/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)

	// Signed URL Uploads
	s.mux.Host(s.publicHost).Path("/{bucketName}/{objectName:.+}").Methods("POST", "PUT").HandlerFunc(jsonToHTTPHandler(s.insertObject))
	s.mux.Host(bucketHost).Path("/{objectName:.+}").Methods("POST", "PUT").HandlerFunc(jsonToHTTPHandler(s.insertObject))
	s.mux.Host("{bucketName:.+}").Path("/{objectName:.+}").Methods("POST", "PUT").HandlerFunc(jsonToHTTPHandler(s.insertObject))
}

// Stop stops the server, closing all connections.
func (s *Server) Stop() {
	if s.ts != nil {
		if transport, ok := s.transport.(*http.Transport); ok {
			transport.CloseIdleConnections()
		}
		s.ts.Close()
	}
}

// URL returns the server URL.
func (s *Server) URL() string {
	if s.externalURL != "" {
		return s.externalURL
	}
	if s.ts != nil {
		return s.ts.URL
	}
	return ""
}

// PublicURL returns the server's public download URL.
func (s *Server) PublicURL() string {
	return fmt.Sprintf("%s://%s", s.scheme(), s.publicHost)
}

func (s *Server) scheme() string {
	if s.options.Scheme == "http" {
		return "http"
	}
	return "https"
}

// HTTPClient returns an HTTP client configured to talk to the server.
func (s *Server) HTTPClient() *http.Client {
	return &http.Client{Transport: s.transport}
}

// Client returns a GCS client configured to talk to the server.
func (s *Server) Client() *storage.Client {
	client, _ := storage.NewClient(context.Background(), option.WithHTTPClient(s.HTTPClient()))
	return client
}

func requestCompressHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("content-encoding") == "gzip" {
			gzipReader, err := gzip.NewReader(r.Body)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			r.Body = gzipReader
		}
		h.ServeHTTP(w, r)
	})
}
<commit_msg>added POST endpoint for acls, see https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/insert (#498)<commit_after>// Copyright 2017 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fakestorage

import (
	"compress/gzip"
	"context"
	"fmt"
	"io"
	"net"
	"net/http"
	"net/http/httptest"
	"sync"

	"cloud.google.com/go/storage"
	"github.com/fsouza/fake-gcs-server/internal/backend"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"google.golang.org/api/option"
)

const defaultPublicHost = "storage.googleapis.com"

// Server is the fake server.
//
// It provides a fake implementation of the Google Cloud Storage API.
type Server struct {
	backend     backend.Storage
	uploads     sync.Map
	transport   http.RoundTripper
	ts          *httptest.Server
	mux         *mux.Router
	options     Options
	externalURL string
	publicHost  string
}

// NewServer creates a new instance of the server, pre-loaded with the given
// objects.
func NewServer(objects []Object) *Server {
	s, _ := NewServerWithOptions(Options{
		InitialObjects: objects,
	})
	return s
}

// NewServerWithHostPort creates a new server that listens on a custom host and port
//
// Deprecated: use NewServerWithOptions.
func NewServerWithHostPort(objects []Object, host string, port uint16) (*Server, error) {
	return NewServerWithOptions(Options{
		InitialObjects: objects,
		Host:           host,
		Port:           port,
	})
}

// Options are used to configure the server on creation.
type Options struct {
	InitialObjects []Object
	StorageRoot    string
	Scheme         string
	Host           string
	Port           uint16

	// when set to true, the server will not actually start a TCP listener,
	// client requests will get processed by an internal mocked transport.
	NoListener bool

	// Optional external URL, such as https://gcs.127.0.0.1.nip.io:4443
	// Returned in the Location header for resumable uploads
	// The "real" value is https://www.googleapis.com, the JSON API
	// The default is whatever the server is bound to, such as https://0.0.0.0:4443
	ExternalURL string

	// Optional URL for public access
	// An example is "storage.gcs.127.0.0.1.nip.io:4443", which will configure
	// the server to serve objects at:
	// https://storage.gcs.127.0.0.1.nip.io:4443/<bucket>/<object>
	// https://<bucket>.storage.gcs.127.0.0.1.nip.io:4443>/<bucket>/<object>
	// If unset, the default is "storage.googleapis.com", the XML API
	PublicHost string

	// Optional list of headers to add to the CORS header allowlist
	// An example is "X-Goog-Meta-Uploader", which will allow a
	// custom metadata header named "X-Goog-Meta-Uploader" to be
	// sent through the browser
	AllowedCORSHeaders []string

	// Destination for writing log.
	Writer io.Writer
}

// NewServerWithOptions creates a new server configured according to the
// provided options.
func NewServerWithOptions(options Options) (*Server, error) {
	s, err := newServer(options)
	if err != nil {
		return nil, err
	}

	allowedHeaders := []string{"Content-Type", "Content-Encoding", "Range"}
	allowedHeaders = append(allowedHeaders, options.AllowedCORSHeaders...)

	cors := handlers.CORS(
		handlers.AllowedMethods([]string{
			http.MethodHead,
			http.MethodGet,
			http.MethodPost,
			http.MethodPut,
			http.MethodPatch,
			http.MethodDelete,
		}),
		handlers.AllowedHeaders(allowedHeaders),
		handlers.AllowedOrigins([]string{"*"}),
		handlers.AllowCredentials(),
	)

	handler := cors(s.mux)
	if options.Writer != nil {
		handler = handlers.LoggingHandler(options.Writer, handler)
	}
	handler = requestCompressHandler(handler)
	s.transport = &muxTransport{handler: handler}
	if options.NoListener {
		return s, nil
	}

	s.ts = httptest.NewUnstartedServer(handler)
	startFunc := s.ts.StartTLS
	if options.Scheme == "http" {
		startFunc = s.ts.Start
	}
	if options.Port != 0 {
		addr := fmt.Sprintf("%s:%d", options.Host, options.Port)
		l, err := net.Listen("tcp", addr)
		if err != nil {
			return nil, err
		}
		s.ts.Listener.Close()
		s.ts.Listener = l
	}
	startFunc()

	return s, nil
}

func newServer(options Options) (*Server, error) {
	backendObjects := toBackendObjects(options.InitialObjects)
	var backendStorage backend.Storage
	var err error
	if options.StorageRoot != "" {
		backendStorage, err = backend.NewStorageFS(backendObjects, options.StorageRoot)
	} else {
		backendStorage = backend.NewStorageMemory(backendObjects)
	}
	if err != nil {
		return nil, err
	}
	publicHost := options.PublicHost
	if publicHost == "" {
		publicHost = defaultPublicHost
	}
	s := Server{
		backend:     backendStorage,
		uploads:     sync.Map{},
		externalURL: options.ExternalURL,
		publicHost:  publicHost,
		options:     options,
	}
	s.buildMuxer()
	return &s, nil
}

func (s *Server) buildMuxer() {
	const apiPrefix = "/storage/v1"
	s.mux = mux.NewRouter()

	routers := []*mux.Router{
		s.mux.PathPrefix(apiPrefix).Subrouter(),
		s.mux.Host(s.publicHost).PathPrefix(apiPrefix).Subrouter(),
	}

	for _, r := range routers {
		r.Path("/b").Methods("GET").HandlerFunc(jsonToHTTPHandler(s.listBuckets))
		r.Path("/b").Methods("POST").HandlerFunc(jsonToHTTPHandler(s.createBucketByPost))
		r.Path("/b/{bucketName}").Methods("GET").HandlerFunc(jsonToHTTPHandler(s.getBucket))
		r.Path("/b/{bucketName}").Methods("DELETE").HandlerFunc(jsonToHTTPHandler(s.deleteBucket))
		r.Path("/b/{bucketName}/o").Methods("GET").HandlerFunc(jsonToHTTPHandler(s.listObjects))
		r.Path("/b/{bucketName}/o").Methods("POST").HandlerFunc(jsonToHTTPHandler(s.insertObject))
		r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("PATCH").HandlerFunc(jsonToHTTPHandler(s.patchObject))
		r.Path("/b/{bucketName}/o/{objectName:.+}/acl").Methods("GET").HandlerFunc(jsonToHTTPHandler(s.listObjectACL))
		r.Path("/b/{bucketName}/o/{objectName:.+}/acl").Methods("POST").HandlerFunc(jsonToHTTPHandler(s.setObjectACL))
		r.Path("/b/{bucketName}/o/{objectName:.+}/acl/{entity}").Methods("PUT").HandlerFunc(jsonToHTTPHandler(s.setObjectACL))
		r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("GET").HandlerFunc(s.getObject)
		r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("DELETE").HandlerFunc(jsonToHTTPHandler(s.deleteObject))
		r.Path("/b/{sourceBucket}/o/{sourceObject:.+}/rewriteTo/b/{destinationBucket}/o/{destinationObject:.+}").HandlerFunc(jsonToHTTPHandler(s.rewriteObject))
	}

	bucketHost := fmt.Sprintf("{bucketName}.%s", s.publicHost)
	s.mux.Host(bucketHost).Path("/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
	s.mux.Path("/download/storage/v1/b/{bucketName}/o/{objectName:.+}").Methods("GET").HandlerFunc(s.downloadObject)
	s.mux.Path("/upload/storage/v1/b/{bucketName}/o").Methods("POST").HandlerFunc(jsonToHTTPHandler(s.insertObject))
	s.mux.Path("/upload/resumable/{uploadId}").Methods("PUT", "POST").HandlerFunc(jsonToHTTPHandler(s.uploadFileContent))

	s.mux.Host(s.publicHost).Path("/{bucketName}/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
	s.mux.Host("{bucketName:.+}").Path("/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)

	// Signed URL Uploads
	s.mux.Host(s.publicHost).Path("/{bucketName}/{objectName:.+}").Methods("POST", "PUT").HandlerFunc(jsonToHTTPHandler(s.insertObject))
	s.mux.Host(bucketHost).Path("/{objectName:.+}").Methods("POST", "PUT").HandlerFunc(jsonToHTTPHandler(s.insertObject))
	s.mux.Host("{bucketName:.+}").Path("/{objectName:.+}").Methods("POST", "PUT").HandlerFunc(jsonToHTTPHandler(s.insertObject))
}

// Stop stops the server, closing all connections.
func (s *Server) Stop() {
	if s.ts != nil {
		if transport, ok := s.transport.(*http.Transport); ok {
			transport.CloseIdleConnections()
		}
		s.ts.Close()
	}
}

// URL returns the server URL.
func (s *Server) URL() string {
	if s.externalURL != "" {
		return s.externalURL
	}
	if s.ts != nil {
		return s.ts.URL
	}
	return ""
}

// PublicURL returns the server's public download URL.
func (s *Server) PublicURL() string {
	return fmt.Sprintf("%s://%s", s.scheme(), s.publicHost)
}

func (s *Server) scheme() string {
	if s.options.Scheme == "http" {
		return "http"
	}
	return "https"
}

// HTTPClient returns an HTTP client configured to talk to the server.
func (s *Server) HTTPClient() *http.Client {
	return &http.Client{Transport: s.transport}
}

// Client returns a GCS client configured to talk to the server.
func (s *Server) Client() *storage.Client {
	client, _ := storage.NewClient(context.Background(), option.WithHTTPClient(s.HTTPClient()))
	return client
}

func requestCompressHandler(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("content-encoding") == "gzip" {
			gzipReader, err := gzip.NewReader(r.Body)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			r.Body = gzipReader
		}
		h.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package dnsprovider

import (
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/google/uuid"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/aws/aws-sdk-go/service/route53/route53iface"

	"github.com/kubernetes-incubator/external-dns/endpoint"
	"github.com/kubernetes-incubator/external-dns/plan"
)

type AWSProvider struct {
	Client route53iface.Route53API
	DryRun bool
}

// Zones returns the list of hosted zones.
func (p *AWSProvider) Zones() ([]string, error) {
	zones := []string{}

	resp, err := p.Client.ListHostedZones(&route53.ListHostedZonesInput{})
	if err != nil {
		return zones, err
	}

	for _, zone := range resp.HostedZones {
		zones = append(zones, *zone.Name)
	}

	return zones, nil
}

// Zone returns a single zone given a DNS name.
func (p *AWSProvider) Zone(dnsName string) (*route53.HostedZone, error) {
	params := &route53.ListHostedZonesByNameInput{
		DNSName: aws.String(dnsName),
	}

	resp, err := p.Client.ListHostedZonesByName(params)
	if err != nil {
		return nil, err
	}

	if len(resp.HostedZones) != 1 {
		return nil, fmt.Errorf("not exactly one hosted zone found by name, got %d", len(resp.HostedZones))
	}

	return resp.HostedZones[0], nil
}

// CreateZone creates a hosted zone given a name.
func (p *AWSProvider) CreateZone(name string) (*route53.HostedZone, error) {
	params := &route53.CreateHostedZoneInput{
		CallerReference: aws.String(uuid.New().String()),
		Name:            aws.String(name),
	}

	resp, err := p.Client.CreateHostedZone(params)
	if err != nil {
		return nil, err
	}

	return resp.HostedZone, nil
}

// DeleteZone deletes a hosted zone given a name.
func (p *AWSProvider) DeleteZone(name string) error {
	params := &route53.DeleteHostedZoneInput{
		Id: aws.String(name),
	}

	_, err := p.Client.DeleteHostedZone(params)
	if err != nil {
		return err
	}

	return nil
}

// Records returns the list of records in a given hosted zone.
func (p *AWSProvider) Records(zone string) ([]endpoint.Endpoint, error) {
	hostedZone, err := p.Zone(zone)
	if err != nil {
		return nil, err
	}

	params := &route53.ListResourceRecordSetsInput{
		HostedZoneId: hostedZone.Id,
	}

	resp, err := p.Client.ListResourceRecordSets(params)
	if err != nil {
		return nil, err
	}

	endpoints := []endpoint.Endpoint{}

	for _, r := range resp.ResourceRecordSets {
		if *r.Type != "A" {
			continue
		}

		for _, rr := range r.ResourceRecords {
			endpoint := endpoint.Endpoint{
				DNSName: *r.Name,
				Target:  *rr.Value,
			}

			endpoints = append(endpoints, endpoint)
		}
	}

	return endpoints, nil
}

// CreateRecords creates a given set of DNS records in the given hosted zone.
func (p *AWSProvider) CreateRecords(zone string, records []endpoint.Endpoint) error {
	hostedZone, err := p.Zone(zone)
	if err != nil {
		return err
	}

	changes := []*route53.Change{}

	for _, record := range records {
		change := &route53.Change{
			Action: aws.String(route53.ChangeActionUpsert),
			ResourceRecordSet: &route53.ResourceRecordSet{
				Name: aws.String(record.DNSName),
				ResourceRecords: []*route53.ResourceRecord{
					{
						Value: aws.String(record.Target),
					},
				},
				TTL:  aws.Int64(300),
				Type: aws.String(route53.RRTypeA),
			},
		}

		changes = append(changes, change)
	}

	params := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: hostedZone.Id,
		ChangeBatch: &route53.ChangeBatch{
			Changes: changes,
		},
	}

	if p.DryRun {
		log.Infof("Creating records: %#v", params.ChangeBatch.Changes)
		return nil
	}

	_, err = p.Client.ChangeResourceRecordSets(params)
	if err != nil {
		return err
	}

	return nil
}

// UpdateRecords updates a given set of old records to a new set of records in a given hosted zone.
func (p *AWSProvider) UpdateRecords(zone string, newRecords, _ []endpoint.Endpoint) error {
	hostedZone, err := p.Zone(zone)
	if err != nil {
		return err
	}

	changes := []*route53.Change{}

	for _, record := range newRecords {
		change := &route53.Change{
			Action: aws.String(route53.ChangeActionUpsert),
			ResourceRecordSet: &route53.ResourceRecordSet{
				Name: aws.String(record.DNSName),
				ResourceRecords: []*route53.ResourceRecord{
					{
						Value: aws.String(record.Target),
					},
				},
				TTL:  aws.Int64(300),
				Type: aws.String(route53.RRTypeA),
			},
		}

		changes = append(changes, change)
	}

	params := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: hostedZone.Id,
		ChangeBatch: &route53.ChangeBatch{
			Changes: changes,
		},
	}

	if p.DryRun {
		log.Infof("Updating records: %#v", params.ChangeBatch.Changes)
		return nil
	}

	_, err = p.Client.ChangeResourceRecordSets(params)
	if err != nil {
		return err
	}

	return nil
}

// DeleteRecords deletes a given set of DNS records in a given zone.
func (p *AWSProvider) DeleteRecords(zone string, records []endpoint.Endpoint) error {
	hostedZone, err := p.Zone(zone)
	if err != nil {
		return err
	}

	changes := []*route53.Change{}

	for _, record := range records {
		change := &route53.Change{
			Action: aws.String(route53.ChangeActionDelete),
			ResourceRecordSet: &route53.ResourceRecordSet{
				Name: aws.String(record.DNSName),
				ResourceRecords: []*route53.ResourceRecord{
					{
						Value: aws.String(record.Target),
					},
				},
				TTL:  aws.Int64(300),
				Type: aws.String(route53.RRTypeA),
			},
		}

		changes = append(changes, change)
	}

	params := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: hostedZone.Id,
		ChangeBatch: &route53.ChangeBatch{
			Changes: changes,
		},
	}

	if p.DryRun {
		log.Infof("Deleting records: %#v", params.ChangeBatch.Changes)
		return nil
	}

	_, err = p.Client.ChangeResourceRecordSets(params)
	if err != nil {
		return err
	}

	return nil
}

// ApplyChanges applies a given set of changes in a given zone.
func (p *AWSProvider) ApplyChanges(zone string, changes *plan.Changes) error {
	err := p.CreateRecords(zone, changes.Create)
	if err != nil {
		return err
	}

	err = p.UpdateRecords(zone, changes.UpdateNew, changes.UpdateOld)
	if err != nil {
		return err
	}

	err = p.DeleteRecords(zone, changes.Delete)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>chore(dnsprovider): add boilerplate headers<commit_after>/*
Copyright 2017 The Kubernetes 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.
*/

package dnsprovider

import (
	"fmt"

	log "github.com/Sirupsen/logrus"
	"github.com/google/uuid"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/aws/aws-sdk-go/service/route53/route53iface"

	"github.com/kubernetes-incubator/external-dns/endpoint"
	"github.com/kubernetes-incubator/external-dns/plan"
)

type AWSProvider struct {
	Client route53iface.Route53API
	DryRun bool
}

// Zones returns the list of hosted zones.
func (p *AWSProvider) Zones() ([]string, error) {
	zones := []string{}

	resp, err := p.Client.ListHostedZones(&route53.ListHostedZonesInput{})
	if err != nil {
		return zones, err
	}

	for _, zone := range resp.HostedZones {
		zones = append(zones, *zone.Name)
	}

	return zones, nil
}

// Zone returns a single zone given a DNS name.
func (p *AWSProvider) Zone(dnsName string) (*route53.HostedZone, error) {
	params := &route53.ListHostedZonesByNameInput{
		DNSName: aws.String(dnsName),
	}

	resp, err := p.Client.ListHostedZonesByName(params)
	if err != nil {
		return nil, err
	}

	if len(resp.HostedZones) != 1 {
		return nil, fmt.Errorf("not exactly one hosted zone found by name, got %d", len(resp.HostedZones))
	}

	return resp.HostedZones[0], nil
}

// CreateZone creates a hosted zone given a name.
func (p *AWSProvider) CreateZone(name string) (*route53.HostedZone, error) {
	params := &route53.CreateHostedZoneInput{
		CallerReference: aws.String(uuid.New().String()),
		Name:            aws.String(name),
	}

	resp, err := p.Client.CreateHostedZone(params)
	if err != nil {
		return nil, err
	}

	return resp.HostedZone, nil
}

// DeleteZone deletes a hosted zone given a name.
func (p *AWSProvider) DeleteZone(name string) error {
	params := &route53.DeleteHostedZoneInput{
		Id: aws.String(name),
	}

	_, err := p.Client.DeleteHostedZone(params)
	if err != nil {
		return err
	}

	return nil
}

// Records returns the list of records in a given hosted zone.
func (p *AWSProvider) Records(zone string) ([]endpoint.Endpoint, error) {
	hostedZone, err := p.Zone(zone)
	if err != nil {
		return nil, err
	}

	params := &route53.ListResourceRecordSetsInput{
		HostedZoneId: hostedZone.Id,
	}

	resp, err := p.Client.ListResourceRecordSets(params)
	if err != nil {
		return nil, err
	}

	endpoints := []endpoint.Endpoint{}

	for _, r := range resp.ResourceRecordSets {
		if *r.Type != "A" {
			continue
		}

		for _, rr := range r.ResourceRecords {
			endpoint := endpoint.Endpoint{
				DNSName: *r.Name,
				Target:  *rr.Value,
			}

			endpoints = append(endpoints, endpoint)
		}
	}

	return endpoints, nil
}

// CreateRecords creates a given set of DNS records in the given hosted zone.
func (p *AWSProvider) CreateRecords(zone string, records []endpoint.Endpoint) error {
	hostedZone, err := p.Zone(zone)
	if err != nil {
		return err
	}

	changes := []*route53.Change{}

	for _, record := range records {
		change := &route53.Change{
			Action: aws.String(route53.ChangeActionUpsert),
			ResourceRecordSet: &route53.ResourceRecordSet{
				Name: aws.String(record.DNSName),
				ResourceRecords: []*route53.ResourceRecord{
					{
						Value: aws.String(record.Target),
					},
				},
				TTL:  aws.Int64(300),
				Type: aws.String(route53.RRTypeA),
			},
		}

		changes = append(changes, change)
	}

	params := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: hostedZone.Id,
		ChangeBatch: &route53.ChangeBatch{
			Changes: changes,
		},
	}

	if p.DryRun {
		log.Infof("Creating records: %#v", params.ChangeBatch.Changes)
		return nil
	}

	_, err = p.Client.ChangeResourceRecordSets(params)
	if err != nil {
		return err
	}

	return nil
}

// UpdateRecords updates a given set of old records to a new set of records in a given hosted zone.
func (p *AWSProvider) UpdateRecords(zone string, newRecords, _ []endpoint.Endpoint) error {
	hostedZone, err := p.Zone(zone)
	if err != nil {
		return err
	}

	changes := []*route53.Change{}

	for _, record := range newRecords {
		change := &route53.Change{
			Action: aws.String(route53.ChangeActionUpsert),
			ResourceRecordSet: &route53.ResourceRecordSet{
				Name: aws.String(record.DNSName),
				ResourceRecords: []*route53.ResourceRecord{
					{
						Value: aws.String(record.Target),
					},
				},
				TTL:  aws.Int64(300),
				Type: aws.String(route53.RRTypeA),
			},
		}

		changes = append(changes, change)
	}

	params := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: hostedZone.Id,
		ChangeBatch: &route53.ChangeBatch{
			Changes: changes,
		},
	}

	if p.DryRun {
		log.Infof("Updating records: %#v", params.ChangeBatch.Changes)
		return nil
	}

	_, err = p.Client.ChangeResourceRecordSets(params)
	if err != nil {
		return err
	}

	return nil
}

// DeleteRecords deletes a given set of DNS records in a given zone.
func (p *AWSProvider) DeleteRecords(zone string, records []endpoint.Endpoint) error {
	hostedZone, err := p.Zone(zone)
	if err != nil {
		return err
	}

	changes := []*route53.Change{}

	for _, record := range records {
		change := &route53.Change{
			Action: aws.String(route53.ChangeActionDelete),
			ResourceRecordSet: &route53.ResourceRecordSet{
				Name: aws.String(record.DNSName),
				ResourceRecords: []*route53.ResourceRecord{
					{
						Value: aws.String(record.Target),
					},
				},
				TTL:  aws.Int64(300),
				Type: aws.String(route53.RRTypeA),
			},
		}

		changes = append(changes, change)
	}

	params := &route53.ChangeResourceRecordSetsInput{
		HostedZoneId: hostedZone.Id,
		ChangeBatch: &route53.ChangeBatch{
			Changes: changes,
		},
	}

	if p.DryRun {
		log.Infof("Deleting records: %#v", params.ChangeBatch.Changes)
		return nil
	}

	_, err = p.Client.ChangeResourceRecordSets(params)
	if err != nil {
		return err
	}

	return nil
}

// ApplyChanges applies a given set of changes in a given zone.
func (p *AWSProvider) ApplyChanges(zone string, changes *plan.Changes) error {
	err := p.CreateRecords(zone, changes.Create)
	if err != nil {
		return err
	}

	err = p.UpdateRecords(zone, changes.UpdateNew, changes.UpdateOld)
	if err != nil {
		return err
	}

	err = p.DeleteRecords(zone, changes.Delete)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package test

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"path/filepath"
	"runtime"
	"strconv"
	"testing"
	_ "github.com/UserStack/ustackweb/routers"

	"github.com/astaxie/beego"
	. "github.com/smartystreets/goconvey/convey"
	"github.com/UserStack/ustackweb/backend"
	"github.com/UserStack/ustackweb/models"
)

func init() {
	_, file, _, _ := runtime.Caller(1)
	apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".."+string(filepath.Separator))))
	backend.Type = backend.Remote
	models.Users().Create("admin", "admin")
	beego.TestBeegoInit(apppath)
}

type Session struct {
	username string
}

func recordRequest(r *http.Request, session *Session) *httptest.ResponseRecorder {
	w := httptest.NewRecorder()
	if session != nil {
		s := beego.GlobalSessions.SessionStart(w, r)
		s.Set("username", session.username)
	}
	beego.BeeApp.Handlers.ServeHTTP(w, r)
	// beego.Trace("testing", "TestMain", "Code[%d]\n%s", w.Code, w.Body.String())
	return w
}

func getRequest(method string, urlStr string, session *Session) *httptest.ResponseRecorder {
	r, _ := http.NewRequest(method, urlStr, nil)
	return recordRequest(r, session)
}

func postRequest(method string, resourcePath string, data *url.Values, session *Session) *httptest.ResponseRecorder {
	u, _ := url.ParseRequestURI("/")
	u.Path = resourcePath
	urlStr := fmt.Sprintf("%v", u)

	r, _ := http.NewRequest(method, urlStr, bytes.NewBufferString(data.Encode()))
	r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	r.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
	return recordRequest(r, session)
}

// TestMain is a sample to run an endpoint test
func TestMain(t *testing.T) {
	var nilSession *Session
	adminSession := &Session{username: "admin"}

	Convey("Redirect to Sign In\n", t, func() {
		response := getRequest("GET", "/", nilSession)
		Convey("Redirect", func() {
			So(response.Code, ShouldEqual, 302)
			So(response.HeaderMap.Get("Location"), ShouldEqual, "/sign_in")
		})
	})

	Convey("Redirect to Profile when already Signed In\n", t, func() {
		response := getRequest("GET", "/", adminSession)
		Convey("Redirect", func() {
			So(response.Code, ShouldEqual, 200)
			So(response.Body.String(), ShouldContainSubstring, "Home")
		})
	})

	Convey("Shows Sign In\n", t, func() {
		response := getRequest("GET", "/sign_in", nilSession)
		Convey("Redirect", func() {
			So(response.Code, ShouldEqual, 200)
			So(response.Body.String(), ShouldContainSubstring, "Sign In")
		})
	})

	Convey("Successful Sign In\n", t, func() {
		data := url.Values{}
		data.Add("Username", "admin")
		data.Add("Password", "admin")
		response := postRequest("POST", "/sign_in", &data, nilSession)
		Convey("Redirect", func() {
			So(response.Code, ShouldEqual, 302)
			So(response.HeaderMap.Get("Location"), ShouldEqual, "/")
		})
	})

	Convey("Failed Sign In\n", t, func() {
		data := url.Values{}
		data.Add("Username", "adminx")
		data.Add("Password", "barx")
		response := postRequest("POST", "/sign_in", &data, nilSession)
		Convey("Redirect", func() {
			So(response.Code, ShouldEqual, 302)
			So(response.HeaderMap.Get("Location"), ShouldEqual, "/sign_in")
		})
	})

	Convey("Users without Sign In\n", t, func() {
		response := postRequest("GET", "/users", &url.Values{}, nilSession)
		Convey("Render", func() {
			So(response.Code, ShouldEqual, 302)
			So(response.HeaderMap.Get("Location"), ShouldEqual, "/sign_in")
		})
	})

	Convey("Users\n", t, func() {
		response := postRequest("GET", "/users", &url.Values{}, adminSession)
		Convey("Render", func() {
			So(response.Code, ShouldEqual, 200)
		})
	})

	Convey("Create User\n", t, func() {
		data := url.Values{}
		data.Add("Username", "mikes")
		data.Add("Password", "micke")
		response := postRequest("POST", "/users", &data, adminSession)
		Convey("Render", func() {
			So(response.Code, ShouldEqual, 302)
			So(response.HeaderMap.Get("Location"), ShouldStartWith, "/users/")
		})
	})

	Convey("Create User Error\n", t, func() {
		response := postRequest("POST", "/users", &url.Values{}, adminSession)
		Convey("Render", func() {
			So(response.Code, ShouldEqual, 200)
			So(response.Body.String(), ShouldContainSubstring, "Could not create user")
		})
	})
}
<commit_msg>Use inmemory backend in tests.<commit_after>package test

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"path/filepath"
	"runtime"
	"strconv"
	"testing"
	_ "github.com/UserStack/ustackweb/routers"

	"github.com/astaxie/beego"
	. "github.com/smartystreets/goconvey/convey"
	"github.com/UserStack/ustackweb/backend"
	"github.com/UserStack/ustackweb/models"
)

func init() {
	_, file, _, _ := runtime.Caller(1)
	apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".."+string(filepath.Separator))))
	backend.Type = backend.Memory
	models.Users().Create("admin", "admin")
	beego.TestBeegoInit(apppath)
}

type Session struct {
	username string
}

func recordRequest(r *http.Request, session *Session) *httptest.ResponseRecorder {
	w := httptest.NewRecorder()
	if session != nil {
		s := beego.GlobalSessions.SessionStart(w, r)
		s.Set("username", session.username)
	}
	beego.BeeApp.Handlers.ServeHTTP(w, r)
	// beego.Trace("testing", "TestMain", "Code[%d]\n%s", w.Code, w.Body.String())
	return w
}

func getRequest(method string, urlStr string, session *Session) *httptest.ResponseRecorder {
	r, _ := http.NewRequest(method, urlStr, nil)
	return recordRequest(r, session)
}

func postRequest(method string, resourcePath string, data *url.Values, session *Session) *httptest.ResponseRecorder {
	u, _ := url.ParseRequestURI("/")
	u.Path = resourcePath
	urlStr := fmt.Sprintf("%v", u)

	r, _ := http.NewRequest(method, urlStr, bytes.NewBufferString(data.Encode()))
	r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	r.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
	return recordRequest(r, session)
}

// TestMain is a sample to run an endpoint test
func TestMain(t *testing.T) {
	var nilSession *Session
	adminSession := &Session{username: "admin"}

	Convey("Redirect to Sign In\n", t, func() {
		response := getRequest("GET", "/", nilSession)
		Convey("Redirect", func() {
			So(response.Code, ShouldEqual, 302)
			So(response.HeaderMap.Get("Location"), ShouldEqual, "/sign_in")
		})
	})

	Convey("Redirect to Profile when already Signed In\n", t, func() {
		response := getRequest("GET", "/", adminSession)
		Convey("Redirect", func() {
			So(response.Code, ShouldEqual, 200)
			So(response.Body.String(), ShouldContainSubstring, "Home")
		})
	})

	Convey("Shows Sign In\n", t, func() {
		response := getRequest("GET", "/sign_in", nilSession)
		Convey("Redirect", func() {
			So(response.Code, ShouldEqual, 200)
			So(response.Body.String(), ShouldContainSubstring, "Sign In")
		})
	})

	Convey("Successful Sign In\n", t, func() {
		data := url.Values{}
		data.Add("Username", "admin")
		data.Add("Password", "admin")
		response := postRequest("POST", "/sign_in", &data, nilSession)
		Convey("Redirect", func() {
			So(response.Code, ShouldEqual, 302)
			So(response.HeaderMap.Get("Location"), ShouldEqual, "/")
		})
	})

	Convey("Failed Sign In\n", t, func() {
		data := url.Values{}
		data.Add("Username", "adminx")
		data.Add("Password", "barx")
		response := postRequest("POST", "/sign_in", &data, nilSession)
		Convey("Redirect", func() {
			So(response.Code, ShouldEqual, 302)
			So(response.HeaderMap.Get("Location"), ShouldEqual, "/sign_in")
		})
	})

	Convey("Users without Sign In\n", t, func() {
		response := postRequest("GET", "/users", &url.Values{}, nilSession)
		Convey("Render", func() {
			So(response.Code, ShouldEqual, 302)
			So(response.HeaderMap.Get("Location"), ShouldEqual, "/sign_in")
		})
	})

	Convey("Users\n", t, func() {
		response := postRequest("GET", "/users", &url.Values{}, adminSession)
		Convey("Render", func() {
			So(response.Code, ShouldEqual, 200)
		})
	})

	Convey("Create User\n", t, func() {
		data := url.Values{}
		data.Add("Username", "mikes")
		data.Add("Password", "micke")
		response := postRequest("POST", "/users", &data, adminSession)
		Convey("Render", func() {
			So(response.Code, ShouldEqual, 302)
			So(response.HeaderMap.Get("Location"), ShouldStartWith, "/users/")
		})
	})

	Convey("Create User Error\n", t, func() {
		response := postRequest("POST", "/users", &url.Values{}, adminSession)
		Convey("Render", func() {
			So(response.Code, ShouldEqual, 200)
			So(response.Body.String(), ShouldContainSubstring, "Could not create user")
		})
	})
}
<|endoftext|>


<commit_before>package main

import (
	log "github.com/Sirupsen/logrus"
	"sync"
)

// TODO the locks on queryList shouldn't be necessary
// since I'm using map-wide locks already anyway
type queryList struct {
	sync.RWMutex
	queries       []*ForwardedQuery
	nonnilInArray int
}

func (ql *queryList) containsQuery(q *ForwardedQuery) bool {
	ql.RLock()
	defer ql.RUnlock()
	for _, q2 := range ql.queries {
		if q2 == nil {
			return false // All nil entries come after all valid ones
		} else if q == q2 {
			return true
		}
	}
	return false
}

func (ql *queryList) addQuery(q *ForwardedQuery) {
	ql.Lock()
	defer ql.Unlock()
	if ql.nonnilInArray < len(ql.queries) {
		ql.queries[ql.nonnilInArray] = q
		ql.nonnilInArray += 1
	} else {
		ql.nonnilInArray += 1
		ql.queries = append(ql.queries, q)
	}
}

func (ql *queryList) removeQuery(q *ForwardedQuery) {
	ql.Lock()
	defer ql.Unlock()
	if ql.nonnilInArray == 1 {
		// last remaining query
		if ql.queries[0] == q {
			ql.nonnilInArray = 0
			ql.queries[0] = nil
			return
		}
	} else {
		// TODO should also probably resize down here if size falls below some point
		for qIdx, current := range ql.queries {
			if current == q {
				ql.nonnilInArray -= 1
				if qIdx != ql.nonnilInArray {
					ql.queries[qIdx] = ql.queries[ql.nonnilInArray]
				}
				ql.queries[ql.nonnilInArray] = nil
			}
		}
	}
	log.WithFields(log.Fields{
		"query": q, "queryList": ql,
	}).Error("Attempted to remove query from queryList that doesn't contain it")
}
<commit_msg>Get that query list into info<commit_after>package main

import (
	log "github.com/Sirupsen/logrus"
	"sync"
)

// TODO the locks on queryList shouldn't be necessary
// since I'm using map-wide locks already anyway
type queryList struct {
	sync.RWMutex
	queries       []*ForwardedQuery
	nonnilInArray int
}

func (ql *queryList) containsQuery(q *ForwardedQuery) bool {
	ql.RLock()
	defer ql.RUnlock()
	for _, q2 := range ql.queries {
		if q2 == nil {
			return false // All nil entries come after all valid ones
		} else if q == q2 {
			return true
		}
	}
	return false
}

func (ql *queryList) addQuery(q *ForwardedQuery) {
	ql.Lock()
	defer ql.Unlock()
	if ql.nonnilInArray < len(ql.queries) {
		ql.queries[ql.nonnilInArray] = q
		ql.nonnilInArray += 1
	} else {
		ql.nonnilInArray += 1
		ql.queries = append(ql.queries, q)
	}
}

func (ql *queryList) removeQuery(q *ForwardedQuery) {
	ql.Lock()
	defer ql.Unlock()
	if ql.nonnilInArray == 1 {
		// last remaining query
		if ql.queries[0] == q {
			ql.nonnilInArray = 0
			ql.queries[0] = nil
			return
		}
	} else {
		// TODO should also probably resize down here if size falls below some point
		for qIdx, current := range ql.queries {
			if current == q {
				ql.nonnilInArray -= 1
				if qIdx != ql.nonnilInArray {
					ql.queries[qIdx] = ql.queries[ql.nonnilInArray]
				}
				ql.queries[ql.nonnilInArray] = nil
			}
		}
	}
	log.WithFields(log.Fields{
		"query": q, "queryList": ql,
	}).Info("Attempted to remove query from queryList that doesn't contain it")
}
<|endoftext|>