<commit_before>package engineio

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"fmt"
	"github.com/googollee/go-engine.io/polling"
	"github.com/googollee/go-engine.io/websocket"
	"net/http"
	"sync/atomic"
	"time"
)

type config struct {
	PingTimeout   time.Duration
	PingInterval  time.Duration
	MaxConnection int
	AllowRequest  func(*http.Request) error
	AllowUpgrades bool
	Cookie        string
	NewId         func(r *http.Request) string
}

// Server is the server of engine.io.
type Server struct {
	config            config
	socketChan        chan Conn
	serverSessions    Sessions
	creaters          transportCreaters
	currentConnection int32
}

// NewServer returns the server suppported given transports. If transports is nil, server will use ["polling", "websocket"] as default.
func NewServer(transports []string) (*Server, error) {
	if transports == nil {
		transports = []string{"polling", "websocket"}
	}
	creaters := make(transportCreaters)
	for _, t := range transports {
		switch t {
		case "polling":
			creaters[t] = polling.Creater
		case "websocket":
			creaters[t] = websocket.Creater
		default:
			return nil, InvalidError
		}
	}
	return &Server{
		config: config{
			PingTimeout:   60000 * time.Millisecond,
			PingInterval:  25000 * time.Millisecond,
			MaxConnection: 1000,
			AllowRequest:  func(*http.Request) error { return nil },
			AllowUpgrades: true,
			Cookie:        "io",
			NewId:         newId,
		},
		socketChan:     make(chan Conn),
		serverSessions: newServerSessions(),
		creaters:       creaters,
	}, nil
}

// SetPingTimeout sets the timeout of ping. When time out, server will close connection. Default is 60s.
func (s *Server) SetPingTimeout(t time.Duration) {
	s.config.PingTimeout = t
}

// SetPingInterval sets the interval of ping. Default is 25s.
func (s *Server) SetPingInterval(t time.Duration) {
	s.config.PingInterval = t
}

// SetMaxConnection sets the max connetion. Default is 1000.
func (s *Server) SetMaxConnection(n int) {
	s.config.MaxConnection = n
}

// SetAllowRequest sets the middleware function when establish connection. If it return non-nil, connection won't be established. Default will allow all request.
func (s *Server) SetAllowRequest(f func(*http.Request) error) {
	s.config.AllowRequest = f
}

// SetAllowUpgrades sets whether server allows transport upgrade. Default is true.
func (s *Server) SetAllowUpgrades(allow bool) {
	s.config.AllowUpgrades = allow
}

// SetCookie sets the name of cookie which used by engine.io. Default is "io".
func (s *Server) SetCookie(prefix string) {
	s.config.Cookie = prefix
}

// SetNewId sets the callback func to generate new connection id. By default, id is generated from remote addr + current time stamp
func (s *Server) SetNewId(f func(*http.Request) string) {
	s.config.NewId = f
}

// SetSessions sets the Sessions which controll server's session. Default sessions is single process manager. You can custom it as load balance.
func (s *Server) SetSessions(sessions Sessions) {
	s.serverSessions = sessions
}

// ServeHTTP handles http request.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	cookies := r.Cookies()
	sid := r.URL.Query().Get("sid")
	conn := s.serverSessions.Get(sid).(*serverConn)
	if conn == nil {
		if sid != "" {
			http.Error(w, "invalid sid", http.StatusBadRequest)
			return
		}

		if err := s.config.AllowRequest(r); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		n := atomic.AddInt32(&s.currentConnection, 1)
		if int(n) > s.config.MaxConnection {
			http.Error(w, "too many connections", http.StatusServiceUnavailable)
			return
		}

		sid = s.config.NewId(r)

		var err error
		conn, err = newServerConn(sid, w, r, s)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		s.serverSessions.Set(sid, conn)
		cookies = append(cookies, &http.Cookie{
			Name:  s.config.Cookie,
			Value: sid,
		})

		s.socketChan <- conn
	}
	for _, c := range cookies {
		w.Header().Set("Set-Cookie", c.String())
	}
	conn.ServeHTTP(w, r)
}

// Accept returns Conn when client connect to server.
func (s *Server) Accept() (Conn, error) {
	return <-s.socketChan, nil
}

func (s *Server) configure() config {
	return s.config
}

func (s *Server) transports() transportCreaters {
	return s.creaters
}

func (s *Server) onClose(id string) {
	s.serverSessions.Remove(id)
	atomic.AddInt32(&s.currentConnection, -1)
}

func newId(r *http.Request) string {
	hash := fmt.Sprintf("%s %s", r.RemoteAddr, time.Now())
	buf := bytes.NewBuffer(nil)
	sum := md5.Sum([]byte(hash))
	encoder := base64.NewEncoder(base64.URLEncoding, buf)
	encoder.Write(sum[:])
	encoder.Close()
	return buf.String()[:20]
}
<commit_msg>rename<commit_after>package engineio

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"fmt"
	"github.com/googollee/go-engine.io/polling"
	"github.com/googollee/go-engine.io/websocket"
	"net/http"
	"sync/atomic"
	"time"
)

type config struct {
	PingTimeout   time.Duration
	PingInterval  time.Duration
	MaxConnection int
	AllowRequest  func(*http.Request) error
	AllowUpgrades bool
	Cookie        string
	NewId         func(r *http.Request) string
}

// Server is the server of engine.io.
type Server struct {
	config            config
	socketChan        chan Conn
	serverSessions    Sessions
	creaters          transportCreaters
	currentConnection int32
}

// NewServer returns the server suppported given transports. If transports is nil, server will use ["polling", "websocket"] as default.
func NewServer(transports []string) (*Server, error) {
	if transports == nil {
		transports = []string{"polling", "websocket"}
	}
	creaters := make(transportCreaters)
	for _, t := range transports {
		switch t {
		case "polling":
			creaters[t] = polling.Creater
		case "websocket":
			creaters[t] = websocket.Creater
		default:
			return nil, InvalidError
		}
	}
	return &Server{
		config: config{
			PingTimeout:   60000 * time.Millisecond,
			PingInterval:  25000 * time.Millisecond,
			MaxConnection: 1000,
			AllowRequest:  func(*http.Request) error { return nil },
			AllowUpgrades: true,
			Cookie:        "io",
			NewId:         newId,
		},
		socketChan:     make(chan Conn),
		serverSessions: newServerSessions(),
		creaters:       creaters,
	}, nil
}

// SetPingTimeout sets the timeout of ping. When time out, server will close connection. Default is 60s.
func (s *Server) SetPingTimeout(t time.Duration) {
	s.config.PingTimeout = t
}

// SetPingInterval sets the interval of ping. Default is 25s.
func (s *Server) SetPingInterval(t time.Duration) {
	s.config.PingInterval = t
}

// SetMaxConnection sets the max connetion. Default is 1000.
func (s *Server) SetMaxConnection(n int) {
	s.config.MaxConnection = n
}

// SetAllowRequest sets the middleware function when establish connection. If it return non-nil, connection won't be established. Default will allow all request.
func (s *Server) SetAllowRequest(f func(*http.Request) error) {
	s.config.AllowRequest = f
}

// SetAllowUpgrades sets whether server allows transport upgrade. Default is true.
func (s *Server) SetAllowUpgrades(allow bool) {
	s.config.AllowUpgrades = allow
}

// SetCookie sets the name of cookie which used by engine.io. Default is "io".
func (s *Server) SetCookie(prefix string) {
	s.config.Cookie = prefix
}

// SetNewId sets the callback func to generate new connection id. By default, id is generated from remote addr + current time stamp
func (s *Server) SetNewId(f func(*http.Request) string) {
	s.config.NewId = f
}

// SetSessionsManager sets the sessions as server's session manager. Default sessions is single process manager. You can custom it as load balance.
func (s *Server) SetSessionsManager(sessions Sessions) {
	s.serverSessions = sessions
}

// ServeHTTP handles http request.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	cookies := r.Cookies()
	sid := r.URL.Query().Get("sid")
	conn := s.serverSessions.Get(sid).(*serverConn)
	if conn == nil {
		if sid != "" {
			http.Error(w, "invalid sid", http.StatusBadRequest)
			return
		}

		if err := s.config.AllowRequest(r); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		n := atomic.AddInt32(&s.currentConnection, 1)
		if int(n) > s.config.MaxConnection {
			http.Error(w, "too many connections", http.StatusServiceUnavailable)
			return
		}

		sid = s.config.NewId(r)

		var err error
		conn, err = newServerConn(sid, w, r, s)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		s.serverSessions.Set(sid, conn)
		cookies = append(cookies, &http.Cookie{
			Name:  s.config.Cookie,
			Value: sid,
		})

		s.socketChan <- conn
	}
	for _, c := range cookies {
		w.Header().Set("Set-Cookie", c.String())
	}
	conn.ServeHTTP(w, r)
}

// Accept returns Conn when client connect to server.
func (s *Server) Accept() (Conn, error) {
	return <-s.socketChan, nil
}

func (s *Server) configure() config {
	return s.config
}

func (s *Server) transports() transportCreaters {
	return s.creaters
}

func (s *Server) onClose(id string) {
	s.serverSessions.Remove(id)
	atomic.AddInt32(&s.currentConnection, -1)
}

func newId(r *http.Request) string {
	hash := fmt.Sprintf("%s %s", r.RemoteAddr, time.Now())
	buf := bytes.NewBuffer(nil)
	sum := md5.Sum([]byte(hash))
	encoder := base64.NewEncoder(base64.URLEncoding, buf)
	encoder.Write(sum[:])
	encoder.Close()
	return buf.String()[:20]
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	"github.com/codegangsta/martini"
	//     "github.com/martini-contrib/gzip"
	"github.com/PuerkitoBio/throttled"
	"github.com/PuerkitoBio/throttled/store"
)

const (
	LOCAL_IMAGES_PATH = "local-images"

	THROTTLING_RATE_ENV_VAR = "PIXLSERV_THROTTLING_RATE"
	DEFAULT_THROTTING_RATE  = 60 // Requests per min
)

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

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

	// Initialise storage
	storageInit()

	// Read config
	throttlingRatePerMinStr := os.Getenv(THROTTLING_RATE_ENV_VAR)
	throttlingRatePerMin := DEFAULT_THROTTING_RATE
	if throttlingRatePerMinStr != "" {
		throttlingRatePerMin, err = strconv.Atoi(throttlingRatePerMinStr)
		if err != nil {
			throttlingRatePerMin = DEFAULT_THROTTING_RATE
		}
	}

	// Run the server
	m := martini.Classic()
	//     m.Use(gzip.All())
	if throttlingRatePerMin > 0 {
		m.Use(throttler(throttlingRatePerMin))
	}
	m.Get("/image/:parameters/**", func(params martini.Params) (int, string) {
		parameters, err := parseParameters(params["parameters"])
		if err != nil {
			return http.StatusBadRequest, err.Error()
		}
		log.Println("Parameters:", parameters)
		baseImagePath := params["_1"]

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

			return http.StatusOK, buffer.String()
		}

		// Load the original image and process it
		if !imageExists(baseImagePath) {
			return http.StatusNotFound, "Image not found: " + baseImagePath
		} else {
			img, format, err := loadImage(baseImagePath)
			if err != nil {
				return http.StatusInternalServerError, err.Error()
			}

			imgNew := transformCropAndResize(img, parameters)

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

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

			return http.StatusOK, buffer.String()
		}
	})
	go m.Run()

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

	// Clean up
	cacheCleanUp()
	storageCleanUp()
}

func throttler(perMinRate int) http.Handler {
	t := throttled.RateLimit(throttled.PerMin(perMinRate), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(1000))
	return t.Throttle(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Nothing needed here
	}))
}
<commit_msg>Updated link to Martini, removed gzipping<commit_after>package main

import (
	"bytes"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"

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

const (
	LOCAL_IMAGES_PATH = "local-images"

	THROTTLING_RATE_ENV_VAR = "PIXLSERV_THROTTLING_RATE"
	DEFAULT_THROTTING_RATE  = 60 // Requests per min
)

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

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

	// Initialise storage
	storageInit()

	// Read config
	throttlingRatePerMinStr := os.Getenv(THROTTLING_RATE_ENV_VAR)
	throttlingRatePerMin := DEFAULT_THROTTING_RATE
	if throttlingRatePerMinStr != "" {
		throttlingRatePerMin, err = strconv.Atoi(throttlingRatePerMinStr)
		if err != nil {
			throttlingRatePerMin = DEFAULT_THROTTING_RATE
		}
	}

	// Run the server
	m := martini.Classic()
	if throttlingRatePerMin > 0 {
		m.Use(throttler(throttlingRatePerMin))
	}
	m.Get("/image/:parameters/**", func(params martini.Params) (int, string) {
		parameters, err := parseParameters(params["parameters"])
		if err != nil {
			return http.StatusBadRequest, err.Error()
		}
		log.Println("Parameters:", parameters)
		baseImagePath := params["_1"]

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

			return http.StatusOK, buffer.String()
		}

		// Load the original image and process it
		if !imageExists(baseImagePath) {
			return http.StatusNotFound, "Image not found: " + baseImagePath
		} else {
			img, format, err := loadImage(baseImagePath)
			if err != nil {
				return http.StatusInternalServerError, err.Error()
			}

			imgNew := transformCropAndResize(img, parameters)

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

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

			return http.StatusOK, buffer.String()
		}
	})
	go m.Run()

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

	// Clean up
	cacheCleanUp()
	storageCleanUp()
}

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


<commit_before>package main

import (
	"fmt"
	"github.com/speps/go-hashids"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const DEFAULT_RESPONSE = `{"default": "response"}`
const MAX_DELAY = 99
const MIN_STATUS = 100
const MAX_STATUS = 599

var REDIRECT_STATUSES map[int]bool = map[int]bool{301: true, 302: true}
var EMPTY_HEADERS map[string]string = make(map[string]string)

const MAX_GENERATE_SITE_NAME_ATTEMPTS = 5
const DURATION_BETWEEN_GENERATE_SITE_NAME_ATTEMPTS = time.Duration(10) * time.Millisecond

type Server struct {
	config  *Config
	storage *Storage
	hasher  *hashids.HashID
}

func NewServer(config *Config) *Server {
	storage, err := NewStorage(config.driver, config.dataSource)
	if err != nil {
		log.Fatal(err)
	}

	server := &Server{config: config, storage: storage,
		hasher: newHasher(config.siteSalt, config.minSiteLength)}
	if config.createDefaultRules {
		server.createDefaultRules()
	}
	return server
}

func newHasher(salt string, minLength int) *hashids.HashID {
	hd := hashids.NewData()
	hd.Salt = salt
	hd.MinLength = minLength
	hd.Alphabet = "abcdefghijklmnopqrstuvwxyz1234567890"
	return hashids.NewWithData(hd)
}

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	log.Printf("%s %s", req.Method, req.URL.Path)
	switch {
	case server.isOptions(req):
   allowCrossDomainRequests(w, req)
	case server.isCreateSite(req):
		server.createSite(w, req)
	case server.isChangeSite(req):
		server.createRuleFromRequest(server.GetKey(req), w, req)
	default:
    allowCrossDomainRequests(w, req)
		server.respondFromRule(w, req)
	}
}

func (server *Server) isOptions(req *http.Request) bool {
  return req.Method == "OPTIONS"
}

// TODO: check crossbrowser compatibility
func allowCrossDomainRequests(w http.ResponseWriter, req *http.Request) {
  header := w.Header()
  header.Set("Access-Control-Allow-Origin", "*")
  header.Set("Access-Control-Allow-Credentials", "true")
  header["Access-Control-Allow-Headers"] = req.Header["Access-Control-Request-Headers"]
}

func (server *Server) isCreateSite(req *http.Request) bool {
  if req.Method != "POST" {
    return false
  }

  if server.isInSingleSiteMode() {
    return false
  }
  return GetSubdomain(req.Host) == "create"
}

func (server *Server) isInSingleSiteMode() bool {
  return server.config.singleDomainUrlPath != ""
}

func (server *Server) respondFromRule(w http.ResponseWriter, req *http.Request) {
	rule, found, err := server.storage.FindRuleMatching(server.GetKey(req), req)
	if err != nil {
		log.Print(err)
		http.Error(w, "Internal error. For real.", 500)
		return
	}

	if found {
		ApplyRule(rule, w)
	} else {
		http.Error(w, "No rule. For real.", 404)
	}
}

func (server *Server) isChangeSite(r *http.Request) bool {
	if r.Method != "POST" {
		return false
	}
	if server.isInSingleSiteMode() {
		return strings.HasPrefix(r.URL.Path, server.config.singleDomainUrlPath)

	}
	return strings.HasPrefix(GetSubdomain(r.Host), "admin-")
}

func (server *Server) GetKey(req *http.Request) string {
	if server.isInSingleSiteMode() {
		return ""
	}
	if server.isChangeSite(req) {
		return strings.TrimPrefix(GetSubdomain(req.Host), "admin-")
	}
	return GetSubdomain(req.Host)
}

func GetSubdomain(url string) string {
	return strings.Split(url, ".")[0]
}

func (server *Server) createSite(w http.ResponseWriter, req *http.Request) {
	site, err := server.generateSiteName(MAX_GENERATE_SITE_NAME_ATTEMPTS)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	server.createRuleFromRequest(site, w, req)
}

func (server *Server) createRuleFromRequest(subdomain string, w http.ResponseWriter, r *http.Request) {
	payload, err := ioutil.ReadAll(r.Body)
	if err != nil {
		io.WriteString(w, fmt.Sprintf("ERROR: %s", err))
		return
	}

	values, err := url.ParseQuery(r.URL.RawQuery)
	if err != nil {
		io.WriteString(w, fmt.Sprintf("ERROR: %s", err))
		return
	}
	delay := 0
	path := server.GetConfigPath(r)
	delay, _ = strconv.Atoi(values.Get("delay"))
	err = server.storage.UpsertRule(&Rule{site: subdomain, responseStatus: http.StatusOK, headers: EMPTY_HEADERS,
		path: path, method: values.Get("method"),
		responseBody: string(payload), delay: time.Duration(delay) * time.Second})
	log.Print(err)
	io.WriteString(w, fmt.Sprintf("Created domain %s\n", subdomain))
}

func (server *Server) GetConfigPath(r *http.Request) string {
	if server.isInSingleSiteMode() {
		return "/" + strings.TrimPrefix(r.URL.Path, server.config.singleDomainUrlPath)
	}
	return r.URL.Path
}

func (server *Server) generateSiteName(maxAttempts uint) (string, error) {
	var err error
	for ; maxAttempts > 0; maxAttempts-- {
		site, err := server.generateSiteNameFrom(time.Now().UTC().UnixNano())
		if err != nil {
			break
		}
		err = server.storage.CreateSite(site)
		if err == nil {
			return site, nil
		}
		time.Sleep(DURATION_BETWEEN_GENERATE_SITE_NAME_ATTEMPTS)
	}
	return "", err
}

func (server *Server) generateSiteNameFrom(nanoseconds int64) (string, error) {
	totalSeconds := int(nanoseconds / 1000000000)
	millisecondsPart := int((nanoseconds / 1000000) % 1000)
	return server.hasher.Encode([]int{totalSeconds, millisecondsPart})
}

func ApplyRule(rule *Rule, w http.ResponseWriter) {
	log.Printf("sleeping for %v", rule.delay)
	time.Sleep(rule.delay)

	addHeaders(rule.headers, w)
	w.WriteHeader(rule.responseStatus)
	io.WriteString(w, rule.responseBody)
}

func addHeaders(headers map[string]string, w http.ResponseWriter) {
	responseHeader := w.Header()
	for key, value := range headers {
		responseHeader.Add(key, value)
	}
}

func (server *Server) createDefaultRules() {
	server.createDelayRules()
	server.createStatusRules()
}

func (server *Server) createDelayRules() {
	for i := 0; i <= MAX_DELAY; i++ {
		delaySite := strconv.Itoa(i)
		delay := time.Duration(i) * time.Second

		server.storage.UpsertRule(&Rule{site: delaySite, headers: EMPTY_HEADERS, delay: delay,
			responseStatus: http.StatusOK, responseBody: DEFAULT_RESPONSE,
		})
	}
}

func (server *Server) createStatusRules() {
	for i := MIN_STATUS; i <= MAX_STATUS; i++ {
		statusSite := strconv.Itoa(i)
		headers := server.headersForStatus(i)
		server.storage.UpsertRule(&Rule{site: statusSite, responseStatus: i,
			headers: headers, responseBody: DEFAULT_RESPONSE})
	}
}

func (server *Server) headersForStatus(status int) map[string]string {
	_, isRedirect := REDIRECT_STATUSES[status]
	if isRedirect {
		// TODO: check that protocol-independent location is legal HTTP
		// TODO: header should respect current port
		host := fmt.Sprintf("//0.goslow.link")
		return map[string]string{"Location": host}
	}
	return EMPTY_HEADERS
}

func (server *Server) ListenAndServe() error {
	log.Printf("listening on %s", server.config.address)
	return http.ListenAndServe(server.config.address, server)
}
<commit_msg>Refactor server<commit_after>package main

import (
	"fmt"
	"github.com/speps/go-hashids"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
  "errors"
)

const DEFAULT_RESPONSE = `{"default": "response"}`
const MAX_DELAY = 99
const MIN_STATUS = 100
const MAX_STATUS = 599

const CREATE_SUBDOMAIN_NAME = "create"
const BUG_REPORTS_EMAIL = "codumentary.com@gmail.com"

var REDIRECT_STATUSES map[int]bool = map[int]bool{301: true, 302: true}
var EMPTY_HEADERS map[string]string = make(map[string]string)

const MAX_GENERATE_SITE_NAME_ATTEMPTS = 5
const DURATION_BETWEEN_GENERATE_SITE_NAME_ATTEMPTS = time.Duration(10) * time.Millisecond

type Server struct {
	config  *Config
	storage *Storage
	hasher  *hashids.HashID
}

func NewServer(config *Config) *Server {
	storage, err := NewStorage(config.driver, config.dataSource)
	if err != nil {
		log.Fatal(err)
	}

	server := &Server{config: config, storage: storage,
		hasher: newHasher(config.siteSalt, config.minSiteLength)}
	if config.createDefaultRules {
		server.createDefaultRules()
	}
	return server
}

func newHasher(salt string, minLength int) *hashids.HashID {
	hd := hashids.NewData()
	hd.Salt = salt
	hd.MinLength = minLength
	hd.Alphabet = "abcdefghijklmnopqrstuvwxyz1234567890"
	return hashids.NewWithData(hd)
}

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	log.Printf("%s %s", req.Method, req.URL.Path)
	switch {
	case server.isOptions(req):
		allowCrossDomainRequests(w, req)
	case server.isCreateSite(req):
		server.createSite(w, req)
	case server.isChangeSite(req):
		server.changeSite(server.GetKey(req), req)
	default:
		allowCrossDomainRequests(w, req)
		server.respondFromRule(w, req)
	}
}

func (server *Server) isOptions(req *http.Request) bool {
	return req.Method == "OPTIONS"
}

// TODO: check crossbrowser compatibility
func allowCrossDomainRequests(w http.ResponseWriter, req *http.Request) {
	header := w.Header()
	header.Set("Access-Control-Allow-Origin", "*")
	header.Set("Access-Control-Allow-Credentials", "true")
	header["Access-Control-Allow-Headers"] = req.Header["Access-Control-Request-Headers"]
}

func (server *Server) isCreateSite(req *http.Request) bool {
	if req.Method != "POST" {
		return false
	}

	if server.isInSingleSiteMode() {
		return false
	}
	return getSubdomain(req.Host) == CREATE_SUBDOMAIN_NAME
}

func (server *Server) isInSingleSiteMode() bool {
	return server.config.singleDomainUrlPath != ""
}

func getSubdomain(url string) string {
	return strings.Split(url, ".")[0]
}

func (server *Server) createSite(w http.ResponseWriter, req *http.Request) {
	site, err := server.generateSiteName(MAX_GENERATE_SITE_NAME_ATTEMPTS)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	server.changeSite(site, req)
}

func (server *Server) generateSiteName(maxAttempts uint) (string, error) {
  for ; maxAttempts > 0; maxAttempts-- {
    site, err := server.makeSiteNameFrom(generateUniqueNumbers())
    if err != nil {
      log.Print(err)
      break
    }
    err = server.storage.CreateSite(site)
    if err == nil {
      return site, nil
    }
    log.Print(err)
    time.Sleep(DURATION_BETWEEN_GENERATE_SITE_NAME_ATTEMPTS)
  }
  return "", errors.New(fmt.Sprintf(`Can't create site.
Try again in a few seconds or contact %s for help`, BUG_REPORTS_EMAIL))
}

func (server *Server) makeSiteNameFrom(numbers []int) (string, error) {
  return server.hasher.Encode(numbers)
}

func generateUniqueNumbers() []int {
  utc := time.Now().UTC()
  second := int(utc.Unix()) // TODO: fix me at 2037-12-31
  millisecond := utc.Nanosecond() / 1000000
  return []int{second, millisecond}
}

func (server *Server) changeSite(site string, req *http.Request) error {
  rule, err := server.makeRule(site, req)
  if err != nil {
    return err
  }
  return server.storage.UpsertRule(rule)
}

func (server *Server) makeRule(site string, req *http.Request) (*Rule, error) {
  body, err := ioutil.ReadAll(req.Body)
  if err != nil {
    return nil, err
  }

  values, err := url.ParseQuery(req.URL.RawQuery)
  if err != nil {
    return nil, err
  }
  delay := 0
  path := server.GetConfigPath(req)
  delay, _ = strconv.Atoi(values.Get("delay"))
  return &Rule{site: site, responseStatus: http.StatusOK, headers: EMPTY_HEADERS,
    path: path, method: values.Get("method"),
    responseBody: string(body), delay: time.Duration(delay) * time.Second}, nil
}

func (server *Server) respondFromRule(w http.ResponseWriter, req *http.Request) {
	rule, found, err := server.storage.FindRuleMatching(server.GetKey(req), req)
	if err != nil {
		log.Print(err)
		http.Error(w, "Internal error. For real.", 500)
		return
	}

	if found {
		ApplyRule(rule, w)
	} else {
		http.Error(w, "No rule. For real.", 404)
	}
}

func (server *Server) isChangeSite(r *http.Request) bool {
	if r.Method != "POST" {
		return false
	}
	if server.isInSingleSiteMode() {
		return strings.HasPrefix(r.URL.Path, server.config.singleDomainUrlPath)

	}
	return strings.HasPrefix(getSubdomain(r.Host), "admin-")
}

func (server *Server) GetKey(req *http.Request) string {
	if server.isInSingleSiteMode() {
		return ""
	}
	if server.isChangeSite(req) {
		return strings.TrimPrefix(getSubdomain(req.Host), "admin-")
	}
	return getSubdomain(req.Host)
}

func (server *Server) GetConfigPath(r *http.Request) string {
	if server.isInSingleSiteMode() {
		return "/" + strings.TrimPrefix(r.URL.Path, server.config.singleDomainUrlPath)
	}
	return r.URL.Path
}

func ApplyRule(rule *Rule, w http.ResponseWriter) {
	log.Printf("sleeping for %v", rule.delay)
	time.Sleep(rule.delay)

	addHeaders(rule.headers, w)
	w.WriteHeader(rule.responseStatus)
	io.WriteString(w, rule.responseBody)
}

func addHeaders(headers map[string]string, w http.ResponseWriter) {
	responseHeader := w.Header()
	for key, value := range headers {
		responseHeader.Add(key, value)
	}
}

func (server *Server) createDefaultRules() {
	server.createDelayRules()
	server.createStatusRules()
}

func (server *Server) createDelayRules() {
	for i := 0; i <= MAX_DELAY; i++ {
		delaySite := strconv.Itoa(i)
		delay := time.Duration(i) * time.Second

		server.storage.UpsertRule(&Rule{site: delaySite, headers: EMPTY_HEADERS, delay: delay,
			responseStatus: http.StatusOK, responseBody: DEFAULT_RESPONSE,
		})
	}
}

func (server *Server) createStatusRules() {
	for i := MIN_STATUS; i <= MAX_STATUS; i++ {
		statusSite := strconv.Itoa(i)
		headers := server.headersForStatus(i)
		server.storage.UpsertRule(&Rule{site: statusSite, responseStatus: i,
			headers: headers, responseBody: DEFAULT_RESPONSE})
	}
}

func (server *Server) headersForStatus(status int) map[string]string {
	_, isRedirect := REDIRECT_STATUSES[status]
	if isRedirect {
		// TODO: check that protocol-independent location is legal HTTP
		// TODO: header should respect current port
		host := fmt.Sprintf("//0.goslow.link")
		return map[string]string{"Location": host}
	}
	return EMPTY_HEADERS
}

func (server *Server) ListenAndServe() error {
	log.Printf("listening on %s", server.config.address)
	return http.ListenAndServe(server.config.address, server)
}
<|endoftext|>


<commit_before>package engine

import (
	"crypto/sha1"
	"fmt"
	"path/filepath"
	"time"

	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/db/lock"
	"github.com/concourse/concourse/atc/exec"
	"github.com/concourse/concourse/atc/resource"
	"github.com/concourse/concourse/atc/worker"
)

type coreStepFactory struct {
	pool                  worker.Pool
	streamer              worker.Streamer
	lockFactory           lock.LockFactory
	teamFactory           db.TeamFactory
	buildFactory          db.BuildFactory
	resourceCacheFactory  db.ResourceCacheFactory
	resourceConfigFactory db.ResourceConfigFactory
	defaultLimits         atc.ContainerLimits
	strategy              worker.PlacementStrategy
	noInputStrategy       worker.PlacementStrategy
	checkStrategy worker.PlacementStrategy
	defaultCheckTimeout   time.Duration
	defaultGetTimeout     time.Duration
	defaultPutTimeout     time.Duration
	defaultTaskTimeout    time.Duration
}

func NewCoreStepFactory(
	pool worker.Pool,
	streamer worker.Streamer,
	lockFactory lock.LockFactory,
	teamFactory db.TeamFactory,
	buildFactory db.BuildFactory,
	resourceCacheFactory db.ResourceCacheFactory,
	resourceConfigFactory db.ResourceConfigFactory,
	defaultLimits atc.ContainerLimits,
	strategy worker.PlacementStrategy,
	noInputStrategy worker.PlacementStrategy,
	checkStrategy worker.PlacementStrategy,
	defaultCheckTimeout time.Duration,
	defaultGetTimeout time.Duration,
	defaultPutTimeout time.Duration,
	defaultTaskTimeout time.Duration,
) CoreStepFactory {
	return &coreStepFactory{
		pool:                  pool,
		streamer:              streamer,
		lockFactory:           lockFactory,
		teamFactory:           teamFactory,
		buildFactory:          buildFactory,
		resourceCacheFactory:  resourceCacheFactory,
		resourceConfigFactory: resourceConfigFactory,
		defaultLimits:         defaultLimits,
		strategy:              strategy,
		noInputStrategy:       noInputStrategy,
		checkStrategy: checkStrategy,
		defaultCheckTimeout:   defaultCheckTimeout,
		defaultGetTimeout:     defaultGetTimeout,
		defaultPutTimeout:     defaultPutTimeout,
		defaultTaskTimeout:    defaultTaskTimeout,
	}
}

func (factory *coreStepFactory) GetStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	containerMetadata db.ContainerMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	containerMetadata.WorkingDirectory = resource.ResourcesDir("get")

	getStep := exec.NewGetStep(
		plan.ID,
		*plan.Get,
		stepMetadata,
		containerMetadata,
		factory.lockFactory,
		factory.resourceCacheFactory,
		factory.noInputStrategy,
		delegateFactory,
		factory.pool,
		factory.defaultGetTimeout,
	)

	getStep = exec.LogError(getStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		getStep = exec.RetryError(getStep, delegateFactory)
	}
	return getStep
}

func (factory *coreStepFactory) PutStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	containerMetadata db.ContainerMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	containerMetadata.WorkingDirectory = resource.ResourcesDir("put")

	putStep := exec.NewPutStep(
		plan.ID,
		*plan.Put,
		stepMetadata,
		containerMetadata,
		factory.strategy,
		factory.pool,
		delegateFactory,
		factory.defaultPutTimeout,
	)

	putStep = exec.LogError(putStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		putStep = exec.RetryError(putStep, delegateFactory)
	}
	return putStep
}

func (factory *coreStepFactory) CheckStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	containerMetadata db.ContainerMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	containerMetadata.WorkingDirectory = resource.ResourcesDir("check")
	// TODO (runtime/#4957): Placement Strategy should be abstracted out from step factory or step level concern
	checkStep := exec.NewCheckStep(
		plan.ID,
		*plan.Check,
		stepMetadata,
		factory.resourceConfigFactory,
		containerMetadata,
		factory.noInputStrategy,
		factory.pool,
		delegateFactory,
		factory.defaultCheckTimeout,
	)

	checkStep = exec.LogError(checkStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		checkStep = exec.RetryError(checkStep, delegateFactory)
	}
	return checkStep
}

func (factory *coreStepFactory) RunStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	containerMetadata db.ContainerMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	containerMetadata.WorkingDirectory = "/tmp/build/run"

	runStep := exec.NewRunStep(
		plan.ID,
		*plan.Run,
		delegateFactory,
	)

	runStep = exec.LogError(runStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		runStep = exec.RetryError(runStep, delegateFactory)
	}
	return runStep
}

func (factory *coreStepFactory) TaskStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	containerMetadata db.ContainerMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	sum := sha1.Sum([]byte(plan.Task.Name))
	containerMetadata.WorkingDirectory = filepath.Join("/tmp", "build", fmt.Sprintf("%x", sum[:4]))

	taskStep := exec.NewTaskStep(
		plan.ID,
		*plan.Task,
		factory.defaultLimits,
		stepMetadata,
		containerMetadata,
		factory.strategy,
		factory.pool,
		factory.streamer,
		delegateFactory,
		factory.defaultTaskTimeout,
	)

	taskStep = exec.LogError(taskStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		taskStep = exec.RetryError(taskStep, delegateFactory)
	}
	return taskStep
}

func (factory *coreStepFactory) SetPipelineStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	spStep := exec.NewSetPipelineStep(
		plan.ID,
		*plan.SetPipeline,
		stepMetadata,
		delegateFactory,
		factory.teamFactory,
		factory.buildFactory,
		factory.streamer,
	)

	spStep = exec.LogError(spStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		spStep = exec.RetryError(spStep, delegateFactory)
	}
	return spStep
}

func (factory *coreStepFactory) LoadVarStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	loadVarStep := exec.NewLoadVarStep(
		plan.ID,
		*plan.LoadVar,
		stepMetadata,
		delegateFactory,
		factory.streamer,
	)

	loadVarStep = exec.LogError(loadVarStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		loadVarStep = exec.RetryError(loadVarStep, delegateFactory)
	}
	return loadVarStep
}

func (factory *coreStepFactory) ArtifactInputStep(
	plan atc.Plan,
	build db.Build,
) exec.Step {
	return exec.NewArtifactInputStep(plan, build, factory.pool)
}

func (factory *coreStepFactory) ArtifactOutputStep(
	plan atc.Plan,
	build db.Build,
) exec.Step {
	return exec.NewArtifactOutputStep(plan, build, factory.pool)
}
<commit_msg>fixed an error<commit_after>package engine

import (
	"crypto/sha1"
	"fmt"
	"path/filepath"
	"time"

	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/db/lock"
	"github.com/concourse/concourse/atc/exec"
	"github.com/concourse/concourse/atc/resource"
	"github.com/concourse/concourse/atc/worker"
)

type coreStepFactory struct {
	pool                  worker.Pool
	streamer              worker.Streamer
	lockFactory           lock.LockFactory
	teamFactory           db.TeamFactory
	buildFactory          db.BuildFactory
	resourceCacheFactory  db.ResourceCacheFactory
	resourceConfigFactory db.ResourceConfigFactory
	defaultLimits         atc.ContainerLimits
	strategy              worker.PlacementStrategy
	noInputStrategy       worker.PlacementStrategy
	checkStrategy worker.PlacementStrategy
	defaultCheckTimeout   time.Duration
	defaultGetTimeout     time.Duration
	defaultPutTimeout     time.Duration
	defaultTaskTimeout    time.Duration
}

func NewCoreStepFactory(
	pool worker.Pool,
	streamer worker.Streamer,
	lockFactory lock.LockFactory,
	teamFactory db.TeamFactory,
	buildFactory db.BuildFactory,
	resourceCacheFactory db.ResourceCacheFactory,
	resourceConfigFactory db.ResourceConfigFactory,
	defaultLimits atc.ContainerLimits,
	strategy worker.PlacementStrategy,
	noInputStrategy worker.PlacementStrategy,
	checkStrategy worker.PlacementStrategy,
	defaultCheckTimeout time.Duration,
	defaultGetTimeout time.Duration,
	defaultPutTimeout time.Duration,
	defaultTaskTimeout time.Duration,
) CoreStepFactory {
	return &coreStepFactory{
		pool:                  pool,
		streamer:              streamer,
		lockFactory:           lockFactory,
		teamFactory:           teamFactory,
		buildFactory:          buildFactory,
		resourceCacheFactory:  resourceCacheFactory,
		resourceConfigFactory: resourceConfigFactory,
		defaultLimits:         defaultLimits,
		strategy:              strategy,
		noInputStrategy:       noInputStrategy,
		checkStrategy: checkStrategy,
		defaultCheckTimeout:   defaultCheckTimeout,
		defaultGetTimeout:     defaultGetTimeout,
		defaultPutTimeout:     defaultPutTimeout,
		defaultTaskTimeout:    defaultTaskTimeout,
	}
}

func (factory *coreStepFactory) GetStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	containerMetadata db.ContainerMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	containerMetadata.WorkingDirectory = resource.ResourcesDir("get")

	getStep := exec.NewGetStep(
		plan.ID,
		*plan.Get,
		stepMetadata,
		containerMetadata,
		factory.lockFactory,
		factory.resourceCacheFactory,
		factory.noInputStrategy,
		delegateFactory,
		factory.pool,
		factory.defaultGetTimeout,
	)

	getStep = exec.LogError(getStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		getStep = exec.RetryError(getStep, delegateFactory)
	}
	return getStep
}

func (factory *coreStepFactory) PutStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	containerMetadata db.ContainerMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	containerMetadata.WorkingDirectory = resource.ResourcesDir("put")

	putStep := exec.NewPutStep(
		plan.ID,
		*plan.Put,
		stepMetadata,
		containerMetadata,
		factory.strategy,
		factory.pool,
		delegateFactory,
		factory.defaultPutTimeout,
	)

	putStep = exec.LogError(putStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		putStep = exec.RetryError(putStep, delegateFactory)
	}
	return putStep
}

func (factory *coreStepFactory) CheckStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	containerMetadata db.ContainerMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	containerMetadata.WorkingDirectory = resource.ResourcesDir("check")
	// TODO (runtime/#4957): Placement Strategy should be abstracted out from step factory or step level concern
	checkStep := exec.NewCheckStep(
		plan.ID,
		*plan.Check,
		stepMetadata,
		factory.resourceConfigFactory,
		containerMetadata,
		factory.noInputStrategy,
		factory.checkStrategy,
		factory.pool,
		delegateFactory,
		factory.defaultCheckTimeout,
	)

	checkStep = exec.LogError(checkStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		checkStep = exec.RetryError(checkStep, delegateFactory)
	}
	return checkStep
}

func (factory *coreStepFactory) RunStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	containerMetadata db.ContainerMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	containerMetadata.WorkingDirectory = "/tmp/build/run"

	runStep := exec.NewRunStep(
		plan.ID,
		*plan.Run,
		delegateFactory,
	)

	runStep = exec.LogError(runStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		runStep = exec.RetryError(runStep, delegateFactory)
	}
	return runStep
}

func (factory *coreStepFactory) TaskStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	containerMetadata db.ContainerMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	sum := sha1.Sum([]byte(plan.Task.Name))
	containerMetadata.WorkingDirectory = filepath.Join("/tmp", "build", fmt.Sprintf("%x", sum[:4]))

	taskStep := exec.NewTaskStep(
		plan.ID,
		*plan.Task,
		factory.defaultLimits,
		stepMetadata,
		containerMetadata,
		factory.strategy,
		factory.pool,
		factory.streamer,
		delegateFactory,
		factory.defaultTaskTimeout,
	)

	taskStep = exec.LogError(taskStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		taskStep = exec.RetryError(taskStep, delegateFactory)
	}
	return taskStep
}

func (factory *coreStepFactory) SetPipelineStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	spStep := exec.NewSetPipelineStep(
		plan.ID,
		*plan.SetPipeline,
		stepMetadata,
		delegateFactory,
		factory.teamFactory,
		factory.buildFactory,
		factory.streamer,
	)

	spStep = exec.LogError(spStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		spStep = exec.RetryError(spStep, delegateFactory)
	}
	return spStep
}

func (factory *coreStepFactory) LoadVarStep(
	plan atc.Plan,
	stepMetadata exec.StepMetadata,
	delegateFactory DelegateFactory,
) exec.Step {
	loadVarStep := exec.NewLoadVarStep(
		plan.ID,
		*plan.LoadVar,
		stepMetadata,
		delegateFactory,
		factory.streamer,
	)

	loadVarStep = exec.LogError(loadVarStep, delegateFactory)
	if atc.EnableBuildRerunWhenWorkerDisappears {
		loadVarStep = exec.RetryError(loadVarStep, delegateFactory)
	}
	return loadVarStep
}

func (factory *coreStepFactory) ArtifactInputStep(
	plan atc.Plan,
	build db.Build,
) exec.Step {
	return exec.NewArtifactInputStep(plan, build, factory.pool)
}

func (factory *coreStepFactory) ArtifactOutputStep(
	plan atc.Plan,
	build db.Build,
) exec.Step {
	return exec.NewArtifactOutputStep(plan, build, factory.pool)
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package engine

import (
	"fmt"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
)

type PGPPullEngineArg struct {
	UserAsserts []string
}

type PGPPullEngine struct {
	listTrackingEngine *ListTrackingEngine
	userAsserts        []string
	gpgClient          *libkb.GpgCLI
	libkb.Contextified
}

func NewPGPPullEngine(arg *PGPPullEngineArg, g *libkb.GlobalContext) *PGPPullEngine {
	return &PGPPullEngine{
		listTrackingEngine: NewListTrackingEngine(&ListTrackingEngineArg{}, g),
		userAsserts:        arg.UserAsserts,
		Contextified:       libkb.NewContextified(g),
	}
}

func (e *PGPPullEngine) Name() string {
	return "PGPPull"
}

func (e *PGPPullEngine) Prereqs() Prereqs {
	return Prereqs{}
}

func (e *PGPPullEngine) RequiredUIs() []libkb.UIKind {
	return []libkb.UIKind{
		libkb.LogUIKind,
	}
}

func (e *PGPPullEngine) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{e.listTrackingEngine}
}

func proofSetFromUserSummary(summary keybase1.UserSummary) *libkb.ProofSet {
	proofs := []libkb.Proof{
		{Key: "keybase", Value: summary.Username},
		{Key: "uid", Value: summary.Uid.String()},
	}
	for _, socialProof := range summary.Proofs.Social {
		proofs = append(proofs, libkb.Proof{
			Key:   socialProof.ProofType,
			Value: socialProof.ProofName,
		})
	}
	for _, webProof := range summary.Proofs.Web {
		for _, protocol := range webProof.Protocols {
			proofs = append(proofs, libkb.Proof{
				Key:   protocol,
				Value: webProof.Hostname,
			})
		}
	}
	return libkb.NewProofSet(proofs)
}

func (e *PGPPullEngine) getTrackedUserSummaries(ctx *Context) ([]keybase1.UserSummary, error) {
	err := RunEngine(e.listTrackingEngine, ctx)
	if err != nil {
		return nil, err
	}
	allTrackedSummaries := e.listTrackingEngine.TableResult()

	// Without any userAsserts specified, just return everything.
	if e.userAsserts == nil || len(e.userAsserts) == 0 {
		return allTrackedSummaries, nil
	}

	// With userAsserts specified, return only those summaries. If an assert
	// doesn't match any tracked users, that's an error. If an assert matches
	// more than one tracked user, that is also an error. If multiple
	// assertions match the same user, that's fine.

	// First parse all the assertion expressions.
	parsedAsserts := make(map[string]libkb.AssertionExpression)
	for _, assertString := range e.userAsserts {
		assertExpr, err := libkb.AssertionParseAndOnly(assertString)
		if err != nil {
			return nil, err
		}
		parsedAsserts[assertString] = assertExpr
	}

	// Then loop over all the tracked users, keeping track of which expressions
	// have matched before.
	matchedSummaries := make(map[string]keybase1.UserSummary)
	assertionsUsed := make(map[string]bool)
	for _, summary := range allTrackedSummaries {
		proofSet := proofSetFromUserSummary(summary)
		for assertStr, parsedAssert := range parsedAsserts {
			if parsedAssert.MatchSet(*proofSet) {
				if assertionsUsed[assertStr] {
					return nil, fmt.Errorf("Assertion \"%s\" matched more than one tracked user.", assertStr)
				}
				assertionsUsed[assertStr] = true
				matchedSummaries[summary.Username] = summary
			}
		}
	}

	// Make sure every assertion found a match.
	for _, assertString := range e.userAsserts {
		if !assertionsUsed[assertString] {
			return nil, fmt.Errorf("Assertion \"%s\" did not match any tracked users.", assertString)
		}
	}

	matchedList := []keybase1.UserSummary{}
	for _, summary := range matchedSummaries {
		matchedList = append(matchedList, summary)
	}
	return matchedList, nil
}

func (e *PGPPullEngine) runLoggedOut(ctx *Context) error {
	if len(e.userAsserts) == 0 {
		return libkb.PGPPullLoggedOutError{}
	}
	for _, assertString := range e.userAsserts {
		if err := e.processUserWhenLoggedOut(ctx, assertString); err != nil {
			return err
		}
	}
	return nil
}

func (e *PGPPullEngine) processUserWhenLoggedOut(ctx *Context, u string) error {
	iarg := NewIdentifyTrackArg(u, false, true, keybase1.TrackOptions{
		LocalOnly: true,
	})
	ieng := NewIdentify(iarg, e.G())
	if err := RunEngine(ieng, ctx); err != nil {
		e.G().Log.Info("identify run err: %s", err)
		return err
	}

	// prompt if the identify is correct
	outcome := ieng.Outcome().Export()
	outcome.ForPGPPull = true
	result, err := ctx.IdentifyUI.Confirm(outcome)
	if err != nil {
		return err
	}

	if !result.IdentityConfirmed {
		e.G().Log.Warning("Not confirmed; skipping key import")
		return nil
	}

	if err = e.exportKeysToGPG(ctx, ieng.User(), nil); err != nil {
		return err
	}

	return nil
}

func (e *PGPPullEngine) Run(ctx *Context) error {

	e.gpgClient = libkb.NewGpgCLI(e.G(), ctx.LogUI)
	err := e.gpgClient.Configure()
	if err != nil {
		return err
	}

	if ok, _, _ := IsLoggedIn(e, ctx); !ok {
		return e.runLoggedOut(ctx)
	}

	summaries, err := e.getTrackedUserSummaries(ctx)
	if err != nil {
		return err
	}

	return e.runLoggedIn(ctx, summaries)
}

func (e *PGPPullEngine) runLoggedIn(ctx *Context, summaries []keybase1.UserSummary) error {

	// Loop over the list of all users we track.
	for _, userSummary := range summaries {
		// Compute the set of tracked pgp fingerprints. LoadUser will fetch key
		// data from the server, and we will compare it against this.
		trackedFingerprints := make(map[string]bool)
		for _, pubKey := range userSummary.Proofs.PublicKeys {
			if pubKey.PGPFingerprint != "" {
				trackedFingerprints[pubKey.PGPFingerprint] = true
			}
		}

		// Get user data from the server.
		user, err := libkb.LoadUser(libkb.NewLoadUserByNameArg(e.G(), userSummary.Username))
		if err != nil {
			ctx.LogUI.Errorf("Failed to load user %s: %s", userSummary.Username, err)
			continue
		}

		if err = e.exportKeysToGPG(ctx, user, trackedFingerprints); err != nil {
			return err
		}
	}
	return nil
}

func (e *PGPPullEngine) exportKeysToGPG(ctx *Context, user *libkb.User, tfp map[string]bool) error {
	for _, bundle := range user.GetActivePGPKeys(false) {
		// Check each key against the tracked set.
		if tfp != nil && !tfp[bundle.GetFingerprint().String()] {
			ctx.LogUI.Warning("Keybase says that %s owns key %s, but you have not tracked this fingerprint before.", user.GetName(), bundle.GetFingerprint())
			continue
		}

		if err := e.gpgClient.ExportKey(*bundle, false /* export public key only */); err != nil {
			return err
		}

		ctx.LogUI.Info("Imported key for %s.", user.GetName())
	}
	return nil
}
<commit_msg>Add rate limit to user loading loops<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package engine

import (
	"fmt"
	"time"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
)

type PGPPullEngineArg struct {
	UserAsserts []string
}

type PGPPullEngine struct {
	listTrackingEngine *ListTrackingEngine
	userAsserts        []string
	gpgClient          *libkb.GpgCLI
	libkb.Contextified
}

func NewPGPPullEngine(arg *PGPPullEngineArg, g *libkb.GlobalContext) *PGPPullEngine {
	return &PGPPullEngine{
		listTrackingEngine: NewListTrackingEngine(&ListTrackingEngineArg{}, g),
		userAsserts:        arg.UserAsserts,
		Contextified:       libkb.NewContextified(g),
	}
}

func (e *PGPPullEngine) Name() string {
	return "PGPPull"
}

func (e *PGPPullEngine) Prereqs() Prereqs {
	return Prereqs{}
}

func (e *PGPPullEngine) RequiredUIs() []libkb.UIKind {
	return []libkb.UIKind{
		libkb.LogUIKind,
	}
}

func (e *PGPPullEngine) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{e.listTrackingEngine}
}

func proofSetFromUserSummary(summary keybase1.UserSummary) *libkb.ProofSet {
	proofs := []libkb.Proof{
		{Key: "keybase", Value: summary.Username},
		{Key: "uid", Value: summary.Uid.String()},
	}
	for _, socialProof := range summary.Proofs.Social {
		proofs = append(proofs, libkb.Proof{
			Key:   socialProof.ProofType,
			Value: socialProof.ProofName,
		})
	}
	for _, webProof := range summary.Proofs.Web {
		for _, protocol := range webProof.Protocols {
			proofs = append(proofs, libkb.Proof{
				Key:   protocol,
				Value: webProof.Hostname,
			})
		}
	}
	return libkb.NewProofSet(proofs)
}

func (e *PGPPullEngine) getTrackedUserSummaries(ctx *Context) ([]keybase1.UserSummary, error) {
	err := RunEngine(e.listTrackingEngine, ctx)
	if err != nil {
		return nil, err
	}
	allTrackedSummaries := e.listTrackingEngine.TableResult()

	// Without any userAsserts specified, just return everything.
	if e.userAsserts == nil || len(e.userAsserts) == 0 {
		return allTrackedSummaries, nil
	}

	// With userAsserts specified, return only those summaries. If an assert
	// doesn't match any tracked users, that's an error. If an assert matches
	// more than one tracked user, that is also an error. If multiple
	// assertions match the same user, that's fine.

	// First parse all the assertion expressions.
	parsedAsserts := make(map[string]libkb.AssertionExpression)
	for _, assertString := range e.userAsserts {
		assertExpr, err := libkb.AssertionParseAndOnly(assertString)
		if err != nil {
			return nil, err
		}
		parsedAsserts[assertString] = assertExpr
	}

	// Then loop over all the tracked users, keeping track of which expressions
	// have matched before.
	matchedSummaries := make(map[string]keybase1.UserSummary)
	assertionsUsed := make(map[string]bool)
	for _, summary := range allTrackedSummaries {
		proofSet := proofSetFromUserSummary(summary)
		for assertStr, parsedAssert := range parsedAsserts {
			if parsedAssert.MatchSet(*proofSet) {
				if assertionsUsed[assertStr] {
					return nil, fmt.Errorf("Assertion \"%s\" matched more than one tracked user.", assertStr)
				}
				assertionsUsed[assertStr] = true
				matchedSummaries[summary.Username] = summary
			}
		}
	}

	// Make sure every assertion found a match.
	for _, assertString := range e.userAsserts {
		if !assertionsUsed[assertString] {
			return nil, fmt.Errorf("Assertion \"%s\" did not match any tracked users.", assertString)
		}
	}

	matchedList := []keybase1.UserSummary{}
	for _, summary := range matchedSummaries {
		matchedList = append(matchedList, summary)
	}
	return matchedList, nil
}

func (e *PGPPullEngine) runLoggedOut(ctx *Context) error {
	if len(e.userAsserts) == 0 {
		return libkb.PGPPullLoggedOutError{}
	}
	t := time.Now()
	for i, assertString := range e.userAsserts {
		t = e.rateLimit(t, i)
		if err := e.processUserWhenLoggedOut(ctx, assertString); err != nil {
			return err
		}
	}
	return nil
}

func (e *PGPPullEngine) processUserWhenLoggedOut(ctx *Context, u string) error {
	iarg := NewIdentifyTrackArg(u, false, true, keybase1.TrackOptions{
		LocalOnly: true,
	})
	ieng := NewIdentify(iarg, e.G())
	if err := RunEngine(ieng, ctx); err != nil {
		e.G().Log.Info("identify run err: %s", err)
		return err
	}

	// prompt if the identify is correct
	outcome := ieng.Outcome().Export()
	outcome.ForPGPPull = true
	result, err := ctx.IdentifyUI.Confirm(outcome)
	if err != nil {
		return err
	}

	if !result.IdentityConfirmed {
		e.G().Log.Warning("Not confirmed; skipping key import")
		return nil
	}

	if err = e.exportKeysToGPG(ctx, ieng.User(), nil); err != nil {
		return err
	}

	return nil
}

func (e *PGPPullEngine) Run(ctx *Context) error {

	e.gpgClient = libkb.NewGpgCLI(e.G(), ctx.LogUI)
	err := e.gpgClient.Configure()
	if err != nil {
		return err
	}

	if ok, _, _ := IsLoggedIn(e, ctx); !ok {
		return e.runLoggedOut(ctx)
	}

	summaries, err := e.getTrackedUserSummaries(ctx)
	if err != nil {
		return err
	}

	return e.runLoggedIn(ctx, summaries)
}

func (e *PGPPullEngine) runLoggedIn(ctx *Context, summaries []keybase1.UserSummary) error {

	// Loop over the list of all users we track.
	t := time.Now()
	for i, userSummary := range summaries {
		t = e.rateLimit(t, i)
		// Compute the set of tracked pgp fingerprints. LoadUser will fetch key
		// data from the server, and we will compare it against this.
		trackedFingerprints := make(map[string]bool)
		for _, pubKey := range userSummary.Proofs.PublicKeys {
			if pubKey.PGPFingerprint != "" {
				trackedFingerprints[pubKey.PGPFingerprint] = true
			}
		}

		// Get user data from the server.
		user, err := libkb.LoadUser(libkb.NewLoadUserByNameArg(e.G(), userSummary.Username))
		if err != nil {
			ctx.LogUI.Errorf("Failed to load user %s: %s", userSummary.Username, err)
			continue
		}

		if err = e.exportKeysToGPG(ctx, user, trackedFingerprints); err != nil {
			return err
		}
	}
	return nil
}

func (e *PGPPullEngine) exportKeysToGPG(ctx *Context, user *libkb.User, tfp map[string]bool) error {
	for _, bundle := range user.GetActivePGPKeys(false) {
		// Check each key against the tracked set.
		if tfp != nil && !tfp[bundle.GetFingerprint().String()] {
			ctx.LogUI.Warning("Keybase says that %s owns key %s, but you have not tracked this fingerprint before.", user.GetName(), bundle.GetFingerprint())
			continue
		}

		if err := e.gpgClient.ExportKey(*bundle, false /* export public key only */); err != nil {
			return err
		}

		ctx.LogUI.Info("Imported key for %s.", user.GetName())
	}
	return nil
}

func (e *PGPPullEngine) rateLimit(start time.Time, index int) time.Time {
	// server currently limiting to 32 req/s, but there can be 4 requests for each loaduser call.
	const loadUserPerSec = 8
	if index == 0 {
		return start
	}
	if index%loadUserPerSec != 0 {
		return start
	}
	d := time.Second - time.Since(start)
	if d > 0 {
		e.G().Log.Debug("sleeping for %s to slow down api requests", d)
		time.Sleep(d)
	}
	return time.Now()
}
<|endoftext|>


<commit_before>package types

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

// Errors/Exceptions
type MalError struct {
	Obj MalType
}

func (e MalError) Error() string {
	return fmt.Sprintf("%#v", e.Obj)
}

// General types
type MalType interface {
}

type EnvType interface {
	Find(key Symbol) EnvType
	Set(key Symbol, value MalType) MalType
	Get(key Symbol) (MalType, error)
}

// Scalars
func Nil_Q(obj MalType) bool {
	if obj == nil {
		return true
	} else {
		return false
	}
}

func True_Q(obj MalType) bool {
	switch tobj := obj.(type) {
	case bool:
		return tobj == true
	default:
		return false
	}
}

func False_Q(obj MalType) bool {
	switch tobj := obj.(type) {
	case bool:
		return tobj == false
	default:
		return false
	}
}

// Symbols
type Symbol struct {
	Val string
}

func Symbol_Q(obj MalType) bool {
	if obj == nil {
		return false
	}
	return reflect.TypeOf(obj).Name() == "Symbol"
}

// Keywords
func NewKeyword(s string) (MalType, error) {
	return "\u029e" + s, nil
}

func Keyword_Q(obj MalType) bool {
	if obj == nil {
		return false
	}
	switch s := obj.(type) {
	case string:
		return strings.HasPrefix(s, "\u029e")
	default:
		return false
	}
}

// Strings
func String_Q(obj MalType) bool {
	if obj == nil {
		return false
	}
	return reflect.TypeOf(obj).Name() == "string"
}

// Functions
type Func struct {
	Fn   func([]MalType) (MalType, error)
	Meta MalType
}

func Func_Q(obj MalType) bool {
	if obj == nil {
		return false
	}
	return reflect.TypeOf(obj).Name() == "Func"
}

type MalFunc struct {
	Eval    func(MalType, EnvType) (MalType, error)
	Exp     MalType
	Env     EnvType
	Params  MalType
	IsMacro bool
	GenEnv  func(EnvType, MalType, MalType) (EnvType, error)
	Meta    MalType
}

func MalFunc_Q(obj MalType) bool {
	if obj == nil {
		return false
	}
	return reflect.TypeOf(obj).Name() == "MalFunc"
}

func (f MalFunc) SetMacro() MalType {
	f.IsMacro = true
	return f
}

func (f MalFunc) GetMacro() bool {
	return f.IsMacro
}

// Take either a MalFunc or regular function and apply it to the
// arguments
func Apply(f_mt MalType, a []MalType) (MalType, error) {
	switch f := f_mt.(type) {
	case MalFunc:
		env, e := f.GenEnv(f.Env, f.Params, List{a, nil})
		if e != nil {
			return nil, e
		}
		return f.Eval(f.Exp, env)
	case Func:
		return f.Fn(a)
	case func([]MalType) (MalType, error):
		return f(a)
	default:
		return nil, errors.New("Invalid function to Apply")
	}
}

// Lists
type List struct {
	Val  []MalType
	Meta MalType
}

func NewList(a ...MalType) MalType {
	return List{a, nil}
}

func List_Q(obj MalType) bool {
	if obj == nil {
		return false
	}
	return reflect.TypeOf(obj).Name() == "List"
}

// Vectors
type Vector struct {
	Val  []MalType
	Meta MalType
}

func Vector_Q(obj MalType) bool {
	if obj == nil {
		return false
	}
	return reflect.TypeOf(obj).Name() == "Vector"
}

func GetSlice(seq MalType) ([]MalType, error) {
	switch obj := seq.(type) {
	case List:
		return obj.Val, nil
	case Vector:
		return obj.Val, nil
	default:
		return nil, errors.New("GetSlice called on non-sequence")
	}
}

// Hash Maps
type HashMap struct {
	Val  map[string]MalType
	Meta MalType
}

func NewHashMap(seq MalType) (MalType, error) {
	lst, e := GetSlice(seq)
	if e != nil {
		return nil, e
	}
	if len(lst)%2 == 1 {
		return nil, errors.New("Odd number of arguments to NewHashMap")
	}
	m := map[string]MalType{}
	for i := 0; i < len(lst); i += 2 {
		str, ok := lst[i].(string)
		if !ok {
			return nil, errors.New("expected hash-map key string")
		}
		m[str] = lst[i+1]
	}
	return HashMap{m, nil}, nil
}

func HashMap_Q(obj MalType) bool {
	if obj == nil {
		return false
	}
	return reflect.TypeOf(obj).Name() == "HashMap"
}

// Atoms
type Atom struct {
	Val  MalType
	Meta MalType
}

func (a *Atom) Set(val MalType) MalType {
	a.Val = val
	return a
}

func Atom_Q(obj MalType) bool {
	switch obj.(type) {
	case *Atom:
		return true
	default:
		return false
	}
}

// General functions

func _obj_type(obj MalType) string {
	if obj == nil {
		return "nil"
	}
	return reflect.TypeOf(obj).Name()
}

func Sequential_Q(seq MalType) bool {
	if seq == nil {
		return false
	}
	return (reflect.TypeOf(seq).Name() == "List") ||
		(reflect.TypeOf(seq).Name() == "Vector")
}

func Equal_Q(a MalType, b MalType) bool {
	ota := reflect.TypeOf(a)
	otb := reflect.TypeOf(b)
	if !((ota == otb) || (Sequential_Q(a) && Sequential_Q(b))) {
		return false
	}
	//av := reflect.ValueOf(a); bv := reflect.ValueOf(b)
	//fmt.Printf("here2: %#v\n", reflect.TypeOf(a).Name())
	//switch reflect.TypeOf(a).Name() {
	switch a.(type) {
	case Symbol:
		return a.(Symbol).Val == b.(Symbol).Val
	case List:
		as, _ := GetSlice(a)
		bs, _ := GetSlice(b)
		if len(as) != len(bs) {
			return false
		}
		for i := 0; i < len(as); i += 1 {
			if !Equal_Q(as[i], bs[i]) {
				return false
			}
		}
		return true
	case Vector:
		as, _ := GetSlice(a)
		bs, _ := GetSlice(b)
		if len(as) != len(bs) {
			return false
		}
		for i := 0; i < len(as); i += 1 {
			if !Equal_Q(as[i], bs[i]) {
				return false
			}
		}
		return true
	case HashMap:
		am := a.(HashMap).Val
		bm := b.(HashMap).Val
		if len(am) != len(bm) {
			return false
		}
		for k, v := range am {
			if !Equal_Q(v, bm[k]) {
				return false
			}
		}
		return true
	default:
		return a == b
	}
}
<commit_msg>Go: Use type assertions where possible instead of reflection<commit_after>package types

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

// Errors/Exceptions
type MalError struct {
	Obj MalType
}

func (e MalError) Error() string {
	return fmt.Sprintf("%#v", e.Obj)
}

// General types
type MalType interface {
}

type EnvType interface {
	Find(key Symbol) EnvType
	Set(key Symbol, value MalType) MalType
	Get(key Symbol) (MalType, error)
}

// Scalars
func Nil_Q(obj MalType) bool {
	return obj == nil
}

func True_Q(obj MalType) bool {
	b, ok := obj.(bool)
	return ok && b == true
}

func False_Q(obj MalType) bool {
	b, ok := obj.(bool)
	return ok && b == false
}

// Symbols
type Symbol struct {
	Val string
}

func Symbol_Q(obj MalType) bool {
	_, ok := obj.(Symbol)
	return ok
}

// Keywords
func NewKeyword(s string) (MalType, error) {
	return "\u029e" + s, nil
}

func Keyword_Q(obj MalType) bool {
	s, ok := obj.(string)
	return ok && strings.HasPrefix(s, "\u029e")
}

// Strings
func String_Q(obj MalType) bool {
	_, ok := obj.(string)
	return ok
}

// Functions
type Func struct {
	Fn   func([]MalType) (MalType, error)
	Meta MalType
}

func Func_Q(obj MalType) bool {
	_, ok := obj.(Func)
	return ok
}

type MalFunc struct {
	Eval    func(MalType, EnvType) (MalType, error)
	Exp     MalType
	Env     EnvType
	Params  MalType
	IsMacro bool
	GenEnv  func(EnvType, MalType, MalType) (EnvType, error)
	Meta    MalType
}

func MalFunc_Q(obj MalType) bool {
	_, ok := obj.(MalFunc)
	return ok
}

func (f MalFunc) SetMacro() MalType {
	f.IsMacro = true
	return f
}

func (f MalFunc) GetMacro() bool {
	return f.IsMacro
}

// Take either a MalFunc or regular function and apply it to the
// arguments
func Apply(f_mt MalType, a []MalType) (MalType, error) {
	switch f := f_mt.(type) {
	case MalFunc:
		env, e := f.GenEnv(f.Env, f.Params, List{a, nil})
		if e != nil {
			return nil, e
		}
		return f.Eval(f.Exp, env)
	case Func:
		return f.Fn(a)
	case func([]MalType) (MalType, error):
		return f(a)
	default:
		return nil, errors.New("Invalid function to Apply")
	}
}

// Lists
type List struct {
	Val  []MalType
	Meta MalType
}

func NewList(a ...MalType) MalType {
	return List{a, nil}
}

func List_Q(obj MalType) bool {
	_, ok := obj.(List)
	return ok
}

// Vectors
type Vector struct {
	Val  []MalType
	Meta MalType
}

func Vector_Q(obj MalType) bool {
	_, ok := obj.(Vector)
	return ok
}

func GetSlice(seq MalType) ([]MalType, error) {
	switch obj := seq.(type) {
	case List:
		return obj.Val, nil
	case Vector:
		return obj.Val, nil
	default:
		return nil, errors.New("GetSlice called on non-sequence")
	}
}

// Hash Maps
type HashMap struct {
	Val  map[string]MalType
	Meta MalType
}

func NewHashMap(seq MalType) (MalType, error) {
	lst, e := GetSlice(seq)
	if e != nil {
		return nil, e
	}
	if len(lst)%2 == 1 {
		return nil, errors.New("Odd number of arguments to NewHashMap")
	}
	m := map[string]MalType{}
	for i := 0; i < len(lst); i += 2 {
		str, ok := lst[i].(string)
		if !ok {
			return nil, errors.New("expected hash-map key string")
		}
		m[str] = lst[i+1]
	}
	return HashMap{m, nil}, nil
}

func HashMap_Q(obj MalType) bool {
	_, ok := obj.(HashMap)
	return ok
}

// Atoms
type Atom struct {
	Val  MalType
	Meta MalType
}

func (a *Atom) Set(val MalType) MalType {
	a.Val = val
	return a
}

func Atom_Q(obj MalType) bool {
	_, ok := obj.(*Atom)
	return ok
}

// General functions

func _obj_type(obj MalType) string {
	if obj == nil {
		return "nil"
	}
	return reflect.TypeOf(obj).Name()
}

func Sequential_Q(seq MalType) bool {
	if seq == nil {
		return false
	}
	return (reflect.TypeOf(seq).Name() == "List") ||
		(reflect.TypeOf(seq).Name() == "Vector")
}

func Equal_Q(a MalType, b MalType) bool {
	ota := reflect.TypeOf(a)
	otb := reflect.TypeOf(b)
	if !((ota == otb) || (Sequential_Q(a) && Sequential_Q(b))) {
		return false
	}
	//av := reflect.ValueOf(a); bv := reflect.ValueOf(b)
	//fmt.Printf("here2: %#v\n", reflect.TypeOf(a).Name())
	//switch reflect.TypeOf(a).Name() {
	switch a.(type) {
	case Symbol:
		return a.(Symbol).Val == b.(Symbol).Val
	case List:
		as, _ := GetSlice(a)
		bs, _ := GetSlice(b)
		if len(as) != len(bs) {
			return false
		}
		for i := 0; i < len(as); i += 1 {
			if !Equal_Q(as[i], bs[i]) {
				return false
			}
		}
		return true
	case Vector:
		as, _ := GetSlice(a)
		bs, _ := GetSlice(b)
		if len(as) != len(bs) {
			return false
		}
		for i := 0; i < len(as); i += 1 {
			if !Equal_Q(as[i], bs[i]) {
				return false
			}
		}
		return true
	case HashMap:
		am := a.(HashMap).Val
		bm := b.(HashMap).Val
		if len(am) != len(bm) {
			return false
		}
		for k, v := range am {
			if !Equal_Q(v, bm[k]) {
				return false
			}
		}
		return true
	default:
		return a == b
	}
}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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.

// +build windows

// Package storage provides storage management functionality.
package storage

import (
	"errors"
	"fmt"

	"github.com/scjalliance/comshim"
	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
	"github.com/google/winops/powershell"
)

var (
	// ErrUnmarshal indicates an error attempting to unmarshal a response from a PowerShell cmdlet.
	ErrUnmarshal = errors.New("unable to unmarshal powershell output")

	fnPSCmd = powershell.Command
)

// ExtendedStatus is a placeholder for MSFT_StorageExtendedStatus
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-storageextendedstatus
type ExtendedStatus struct{}

// Service represents a connection to the host Storage service (in WMI).
type Service struct {
	wmiIntf *ole.IDispatch
	wmiSvc  *ole.IDispatch
}

// Connect connects to the WMI provider for managing storage objects.
// You must call Close() to release the provider when finished.
//
// Example: storage.Connect()
func Connect() (Service, error) {
	comshim.Add(1)
	svc := Service{}

	unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
	if err != nil {
		comshim.Done()
		return svc, fmt.Errorf("CreateObject: %w", err)
	}
	defer unknown.Release()
	svc.wmiIntf, err = unknown.QueryInterface(ole.IID_IDispatch)
	if err != nil {
		comshim.Done()
		return svc, fmt.Errorf("QueryInterface: %w", err)
	}
	serviceRaw, err := oleutil.CallMethod(svc.wmiIntf, "ConnectServer", nil, `\\.\ROOT\Microsoft\Windows\Storage`)
	if err != nil {
		svc.Close()
		return svc, fmt.Errorf("ConnectServer: %w", err)
	}
	svc.wmiSvc = serviceRaw.ToIDispatch()

	return svc, nil
}

// Close frees all resources associated with a volume.
func (svc *Service) Close() {
	svc.wmiIntf.Release()
	svc.wmiSvc.Release()
	comshim.Done()
}
<commit_msg>Remove references to the powershell package.<commit_after>// Copyright 2021 Google LLC
//
// 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 storage provides storage management functionality.
package storage

import (
	"fmt"

	"github.com/scjalliance/comshim"
	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

// ExtendedStatus is a placeholder for MSFT_StorageExtendedStatus
//
// Ref: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/msft-storageextendedstatus
type ExtendedStatus struct{}

// Service represents a connection to the host Storage service (in WMI).
type Service struct {
	wmiIntf *ole.IDispatch
	wmiSvc  *ole.IDispatch
}

// Connect connects to the WMI provider for managing storage objects.
// You must call Close() to release the provider when finished.
//
// Example: storage.Connect()
func Connect() (Service, error) {
	comshim.Add(1)
	svc := Service{}

	unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
	if err != nil {
		comshim.Done()
		return svc, fmt.Errorf("CreateObject: %w", err)
	}
	defer unknown.Release()
	svc.wmiIntf, err = unknown.QueryInterface(ole.IID_IDispatch)
	if err != nil {
		comshim.Done()
		return svc, fmt.Errorf("QueryInterface: %w", err)
	}
	serviceRaw, err := oleutil.CallMethod(svc.wmiIntf, "ConnectServer", nil, `\\.\ROOT\Microsoft\Windows\Storage`)
	if err != nil {
		svc.Close()
		return svc, fmt.Errorf("ConnectServer: %w", err)
	}
	svc.wmiSvc = serviceRaw.ToIDispatch()

	return svc, nil
}

// Close frees all resources associated with a volume.
func (svc *Service) Close() {
	svc.wmiIntf.Release()
	svc.wmiSvc.Release()
	comshim.Done()
}
<|endoftext|>


<commit_before>package syscalls

import (
	"bytes"
	"github.com/lunixbochs/struc"
	"syscall"
)

const (
	AF_LOCAL   = 1
	AF_INET    = 2
	AF_INET6   = 10
	AF_PACKET  = 17
	AF_NETLINK = 16
)

type RawSockaddrUnix struct {
	Family uint16
	Path   [108]byte
}

func decodeSockaddr(u U, p []byte) syscall.Sockaddr {
	family := u.ByteOrder().Uint16(p)
	buf := bytes.NewReader(p)
	switch family {
	case AF_LOCAL:
		var addr RawSockaddrUnix
		struc.Unpack(buf, &addr)
		paths := bytes.SplitN([]byte(addr.Path[:]), []byte{0}, 2)
		return &syscall.SockaddrUnix{Name: string(paths[0])}
	case AF_INET:
		var addr syscall.RawSockaddrInet4
		struc.Unpack(buf, &addr)
		// TODO: unfinished
		return &syscall.SockaddrInet4{}
	}
	return nil
}
<commit_msg>finish decodeSockaddr<commit_after>package syscalls

import (
	"bytes"
	"github.com/lunixbochs/struc"
	"syscall"
)

const (
	AF_LOCAL   = 1
	AF_INET    = 2
	AF_INET6   = 10
	AF_PACKET  = 17
	AF_NETLINK = 16
)

type RawSockaddrUnix struct {
	Family uint16
	Path   [108]byte
}

func decodeSockaddr(u U, p []byte) syscall.Sockaddr {
	family := u.ByteOrder().Uint16(p)
	buf := bytes.NewReader(p)
	switch family {
	case AF_LOCAL:
		var a RawSockaddrUnix
		struc.Unpack(buf, &a)
		paths := bytes.SplitN([]byte(a.Path[:]), []byte{0}, 2)
		return &syscall.SockaddrUnix{Name: string(paths[0])}
	case AF_INET:
		var a syscall.RawSockaddrInet4
		struc.Unpack(buf, &a)
		return &syscall.SockaddrInet4{Port: int(a.Port), Addr: a.Addr}
	case AF_INET6:
		var a syscall.RawSockaddrInet6
		struc.Unpack(buf, &a)
		return &syscall.SockaddrInet6{Port: int(a.Port), Addr: a.Addr}
		// TODO: only on Linux?
		/*
			case AF_PACKET:
				var a syscall.RawSockaddrLinkLayer
				struc.Unpack(buf, &a)
				return &syscall.SockaddrLinkLayer{
					Protocol: a.Protocol, Ifindex: a.Ifindex, Hatype: a.Hatype,
					Pkttype: a.Pkttype, Halen: a.Halen,
				}
			case AF_NETLINK:
				var a syscall.RawSockaddrNetlink
				struc.Unpack(buf, &a)
				return &syscall.SockaddrNetlink{Pad: a.Pad, Pid: a.Pid, Groups: a.Groups}
		*/
	}
	return nil
}
<|endoftext|>


<commit_before>package rest

import (
	s "../service"
	rj "github.com/fkmhrk-go/rawjson"
	"github.com/gorilla/mux"
	"net/http"
)

func getTradings(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		return trading.GetListByUser(token)
	})
}

func createTrading(trading s.TradingService) handler {
	return makeJsonHandler(func(token, tType string,
		json rj.RawJsonObject) s.Result {
		// read input
		date, _ := json.String("date")
		companyId, _ := json.String("company_id")
		titleType, _ := json.Int("title_type")
		subject, _ := json.String("subject")
		workFrom, _ := json.Long("work_from")
		workTo, _ := json.Long("work_to")
		quotationDate, _ := json.Long("quotation_date")
		billDate, _ := json.Long("bill_date")
		taxRate, _ := json.Float("tax_rate")
		product, _ := json.String("product")

		return trading.Create(token, date, companyId,
			subject, product, titleType, workFrom, workTo,
			quotationDate, billDate, float32(taxRate))
	})
}

func updateTrading(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		// read path param
		vars := mux.Vars(req)
		tradingId := vars["tradingId"]

		// to json
		json, _ := rj.ObjectFromString(readBody(req))

		// read input
		companyId, _ := json.String("company_id")
		subject, _ := json.String("subject")
		titleType, _ := json.Int("title_type")
		workFrom, _ := json.Long("work_from")
		workTo, _ := json.Long("work_to")
		quotationDate, _ := json.Long("quotation_date")
		billDate, _ := json.Long("bill_date")
		taxRate, _ := json.Float("tax_rate")
		product, _ := json.String("product")

		return trading.Update(token, tradingId, companyId,
			subject, product, titleType, workFrom, workTo,
			quotationDate, billDate, float32(taxRate))
	})
}

func getTradingItems(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		// read path param
		vars := mux.Vars(req)
		tradingId := vars["tradingId"]

		return trading.GetItemListByTradingId(token, tradingId)
	})
}

func createTradingItem(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		// read path param
		vars := mux.Vars(req)
		tradingId := vars["tradingId"]

		// to json
		json, _ := rj.ObjectFromString(readBody(req))

		// get values
		sortOrder, _ := json.Int("sort_order")
		subject, _ := json.String("subject")
		unitPrice, _ := json.Int("unit_price")
		amount, _ := json.Int("amount")
		degree, _ := json.String("degree")
		taxType, _ := json.Int("tax_type")
		memo, _ := json.String("memo")

		return trading.CreateItem(token, tradingId, subject, degree, memo, sortOrder, unitPrice, amount, taxType)
	})
}

func updateTradingItem(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		// read path param
		vars := mux.Vars(req)
		tradingId := vars["tradingId"]
		id := vars["itemId"]

		// to json
		json, _ := rj.ObjectFromString(readBody(req))

		// get values
		sortOrder, _ := json.Int("sort_order")
		subject, _ := json.String("subject")
		unitPrice, _ := json.Int("unit_price")
		amount, _ := json.Int("amount")
		degree, _ := json.String("degree")
		taxType, _ := json.Int("tax_type")
		memo, _ := json.String("memo")

		return trading.UpdateItem(token, id, tradingId, subject, degree, memo, sortOrder, unitPrice, amount, taxType)
	})
}

func deleteTradingItem(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		// read path param
		vars := mux.Vars(req)
		tradingId := vars["tradingId"]
		id := vars["itemId"]

		return trading.DeleteItem(token, id, tradingId)
	})
}
<commit_msg>add parameter<commit_after>package rest

import (
	s "../service"
	rj "github.com/fkmhrk-go/rawjson"
	"github.com/gorilla/mux"
	"net/http"
)

func getTradings(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		return trading.GetListByUser(token)
	})
}

func createTrading(trading s.TradingService) handler {
	return makeJsonHandler(func(token, tType string,
		json rj.RawJsonObject) s.Result {
		// read input
		date, _ := json.String("date")
		companyId, _ := json.String("company_id")
		titleType, _ := json.Int("title_type")
		subject, _ := json.String("subject")
		workFrom, _ := json.Long("work_from")
		workTo, _ := json.Long("work_to")
		total, _ := json.Long("total")
		quotationDate, _ := json.Long("quotation_date")
		billDate, _ := json.Long("bill_date")
		taxRate, _ := json.Float("tax_rate")
		product, _ := json.String("product")

		return trading.Create(token, date, companyId,
			subject, product, titleType, workFrom, workTo,
			total, quotationDate, billDate, float32(taxRate))
	})
}

func updateTrading(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		// read path param
		vars := mux.Vars(req)
		tradingId := vars["tradingId"]

		// to json
		json, _ := rj.ObjectFromString(readBody(req))

		// read input
		companyId, _ := json.String("company_id")
		subject, _ := json.String("subject")
		titleType, _ := json.Int("title_type")
		workFrom, _ := json.Long("work_from")
		workTo, _ := json.Long("work_to")
		quotationDate, _ := json.Long("quotation_date")
		billDate, _ := json.Long("bill_date")
		taxRate, _ := json.Float("tax_rate")
		product, _ := json.String("product")

		return trading.Update(token, tradingId, companyId,
			subject, product, titleType, workFrom, workTo,
			quotationDate, billDate, float32(taxRate))
	})
}

func getTradingItems(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		// read path param
		vars := mux.Vars(req)
		tradingId := vars["tradingId"]

		return trading.GetItemListByTradingId(token, tradingId)
	})
}

func createTradingItem(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		// read path param
		vars := mux.Vars(req)
		tradingId := vars["tradingId"]

		// to json
		json, _ := rj.ObjectFromString(readBody(req))

		// get values
		sortOrder, _ := json.Int("sort_order")
		subject, _ := json.String("subject")
		unitPrice, _ := json.Int("unit_price")
		amount, _ := json.Int("amount")
		degree, _ := json.String("degree")
		taxType, _ := json.Int("tax_type")
		memo, _ := json.String("memo")

		return trading.CreateItem(token, tradingId, subject, degree, memo, sortOrder, unitPrice, amount, taxType)
	})
}

func updateTradingItem(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		// read path param
		vars := mux.Vars(req)
		tradingId := vars["tradingId"]
		id := vars["itemId"]

		// to json
		json, _ := rj.ObjectFromString(readBody(req))

		// get values
		sortOrder, _ := json.Int("sort_order")
		subject, _ := json.String("subject")
		unitPrice, _ := json.Int("unit_price")
		amount, _ := json.Int("amount")
		degree, _ := json.String("degree")
		taxType, _ := json.Int("tax_type")
		memo, _ := json.String("memo")

		return trading.UpdateItem(token, id, tradingId, subject, degree, memo, sortOrder, unitPrice, amount, taxType)
	})
}

func deleteTradingItem(trading s.TradingService) handler {
	return makeHandler(func(token, tType string,
		req *http.Request) s.Result {
		// read path param
		vars := mux.Vars(req)
		tradingId := vars["tradingId"]
		id := vars["itemId"]

		return trading.DeleteItem(token, id, tradingId)
	})
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"sync"
	"time"

	"github.com/hamaxx/gracevisor/deps/lumberjack"
)

var logLinePool = sync.Pool{}

type LogLine struct {
	line       bytes.Buffer
	time       time.Time
	instanceId uint32
}

func (ll *LogLine) String() string {
	return fmt.Sprintf("[%d/%s] %s", ll.instanceId, ll.time, ll.line.String())
}

func (ll *LogLine) WriteTo(w io.Writer) error {
	//TODO: no garbage
	if _, err := w.Write([]byte(ll.String())); err != nil {
		return err
	}
	if _, err := w.Write([]byte("\n")); err != nil {
		return err
	}
	return nil
}

type AppLogger struct {
	app *App

	stdoutWriter io.WriteCloser
	stderrWriter io.WriteCloser
}

func NewAppLogger(app *App) *AppLogger {
	stdoutWriter := &lumberjack.Logger{
		Filename:   app.config.Logger.StdoutLogFile,
		MaxSize:    app.config.Logger.MaxLogSize,
		MaxAge:     app.config.Logger.MaxLogAge,
		MaxBackups: app.config.Logger.MaxLogsKept,
	}

	stderrWriter := &lumberjack.Logger{
		Filename:   app.config.Logger.StderrLogFile,
		MaxSize:    app.config.Logger.MaxLogSize,
		MaxAge:     app.config.Logger.MaxLogAge,
		MaxBackups: app.config.Logger.MaxLogsKept,
	}

	return &AppLogger{
		app:          app,
		stdoutWriter: stdoutWriter,
		stderrWriter: stderrWriter,
	}
}

func (al *AppLogger) logStdout(logLine *LogLine) {
	if err := logLine.WriteTo(al.stdoutWriter); err != nil {
		log.Print(al.app.config.Name, ": Stdout write error:", err)
	}
	logLinePool.Put(logLine)
}

func (al *AppLogger) logStderr(logLine *LogLine) {
	if err := logLine.WriteTo(al.stderrWriter); err != nil {
		log.Print(al.app.config.Name, ": Stderr write error:", err)
	}

	logLinePool.Put(logLine)
}

type InstanceLogger struct {
	instance *Instance
}

func NewInstanceLogger(instance *Instance, outPipe, errPipe io.ReadCloser) (*InstanceLogger, error) {
	il := &InstanceLogger{
		instance: instance,
	}

	il.lineReader(outPipe, instance.app.appLogger.logStdout)
	il.lineReader(errPipe, instance.app.appLogger.logStderr)

	return il, nil
}

func (il *InstanceLogger) lineReader(pipe io.ReadCloser, writer func(*LogLine)) {
	rd := bufio.NewReader(pipe)
	go func() {
		for {
			line, err := rd.ReadBytes('\n')
			if err == io.EOF {
				return
			} else if err != nil {
				log.Print(il.instance.app.config.Name, ": Read Error:", err)
				return
			}
			if len(line) > 0 && line[len(line)-1] == '\n' {
				line = line[0 : len(line)-1]
			}
			if len(line) > 0 && line[len(line)-1] == '\r' {
				line = line[0 : len(line)-1]
			}
			ll, err := il.newLogLine(line)
			if err != nil {
				log.Print(il.instance.app.config.Name, ": Log write error:", err)
				continue
			}
			writer(ll)
		}
	}()
}

func (il *InstanceLogger) newLogLine(line []byte) (*LogLine, error) {
	var logLine *LogLine

	if v := logLinePool.Get(); v != nil {
		logLine = v.(*LogLine)
		logLine.line.Reset()
	} else {
		logLine = &LogLine{}
	}

	if _, err := logLine.line.Write(line); err != nil {
		return nil, err
	}
	logLine.time = time.Now()
	logLine.instanceId = il.instance.id

	return logLine, nil
}
<commit_msg>Use single logger if StdoutLogFile and StderrLogFile are the same.<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"sync"
	"time"

	"github.com/hamaxx/gracevisor/deps/lumberjack"
)

var logLinePool = sync.Pool{}

type LogLine struct {
	line       bytes.Buffer
	time       time.Time
	instanceId uint32
}

func (ll *LogLine) String() string {
	return fmt.Sprintf("[%d/%s] %s", ll.instanceId, ll.time, ll.line.String())
}

func (ll *LogLine) WriteTo(w io.Writer) error {
	//TODO: no garbage
	if _, err := w.Write([]byte(ll.String())); err != nil {
		return err
	}
	if _, err := w.Write([]byte("\n")); err != nil {
		return err
	}
	return nil
}

type AppLogger struct {
	app *App

	stdoutWriter io.WriteCloser
	stderrWriter io.WriteCloser
}

func NewAppLogger(app *App) *AppLogger {
	stdoutWriter := &lumberjack.Logger{
		Filename:   app.config.Logger.StdoutLogFile,
		MaxSize:    app.config.Logger.MaxLogSize,
		MaxAge:     app.config.Logger.MaxLogAge,
		MaxBackups: app.config.Logger.MaxLogsKept,
	}

	var stderrWriter io.WriteCloser
	if app.config.Logger.StdoutLogFile == app.config.Logger.StderrLogFile {
		stderrWriter = stdoutWriter
	} else {
		stderrWriter = &lumberjack.Logger{
			Filename:   app.config.Logger.StderrLogFile,
			MaxSize:    app.config.Logger.MaxLogSize,
			MaxAge:     app.config.Logger.MaxLogAge,
			MaxBackups: app.config.Logger.MaxLogsKept,
		}
	}

	return &AppLogger{
		app:          app,
		stdoutWriter: stdoutWriter,
		stderrWriter: stderrWriter,
	}
}

func (al *AppLogger) logStdout(logLine *LogLine) {
	if err := logLine.WriteTo(al.stdoutWriter); err != nil {
		log.Print(al.app.config.Name, ": Stdout write error:", err)
	}
	logLinePool.Put(logLine)
}

func (al *AppLogger) logStderr(logLine *LogLine) {
	if err := logLine.WriteTo(al.stderrWriter); err != nil {
		log.Print(al.app.config.Name, ": Stderr write error:", err)
	}

	logLinePool.Put(logLine)
}

type InstanceLogger struct {
	instance *Instance
}

func NewInstanceLogger(instance *Instance, outPipe, errPipe io.ReadCloser) (*InstanceLogger, error) {
	il := &InstanceLogger{
		instance: instance,
	}

	il.lineReader(outPipe, instance.app.appLogger.logStdout)
	il.lineReader(errPipe, instance.app.appLogger.logStderr)

	return il, nil
}

func (il *InstanceLogger) lineReader(pipe io.ReadCloser, writer func(*LogLine)) {
	rd := bufio.NewReader(pipe)
	go func() {
		for {
			line, err := rd.ReadBytes('\n')
			if err == io.EOF {
				return
			} else if err != nil {
				log.Print(il.instance.app.config.Name, ": Read Error:", err)
				return
			}
			if len(line) > 0 && line[len(line)-1] == '\n' {
				line = line[0 : len(line)-1]
			}
			if len(line) > 0 && line[len(line)-1] == '\r' {
				line = line[0 : len(line)-1]
			}
			ll, err := il.newLogLine(line)
			if err != nil {
				log.Print(il.instance.app.config.Name, ": Log write error:", err)
				continue
			}
			writer(ll)
		}
	}()
}

func (il *InstanceLogger) newLogLine(line []byte) (*LogLine, error) {
	var logLine *LogLine

	if v := logLinePool.Get(); v != nil {
		logLine = v.(*LogLine)
		logLine.line.Reset()
	} else {
		logLine = &LogLine{}
	}

	if _, err := logLine.line.Write(line); err != nil {
		return nil, err
	}
	logLine.time = time.Now()
	logLine.instanceId = il.instance.id

	return logLine, nil
}
<|endoftext|>


<commit_before>package graphdriver

import (
	"fmt"
	"github.com/dotcloud/docker/archive"
)

type InitFunc func(root string) (Driver, error)

type Driver interface {
	Create(id, parent string) error
	Remove(id string) error

	Get(id string) (dir string, err error)

	Diff(id string) (archive.Archive, error)
	DiffSize(id string) (bytes int64, err error)
	Changes(id string) ([]archive.Change, error)

	Cleanup() error
}

var (
	// All registred drivers
	drivers map[string]InitFunc
	// Slice of drivers that should be used in an order
	priority = []string{
		"aufs",
		"devicemapper",
		"dummy",
	}
)

func init() {
	drivers = make(map[string]InitFunc)
}

func Register(name string, initFunc InitFunc) error {
	if _, exists := drivers[name]; exists {
		return fmt.Errorf("Name already registered %s", name)
	}
	drivers[name] = initFunc

	return nil
}

func New(root string) (Driver, error) {
	var driver Driver
	var lastError error
	// Check for priority drivers first
	for _, name := range priority {
		if initFunc, exists := drivers[name]; exists {
			driver, lastError = initFunc(root)
			if lastError != nil {
				continue
			}
			return driver, nil
		}
	}

	// Check all registered drivers if no priority driver is found
	for _, initFunc := range drivers {
		driver, lastError = initFunc(root)
		if lastError != nil {
			continue
		}
		return driver, nil
	}
	return nil, lastError
}
<commit_msg>Set DOCKER_DRIVER to override the choice of driver (aufs, devicemapper or dummy)<commit_after>package graphdriver

import (
	"fmt"
	"github.com/dotcloud/docker/archive"
	"os"
)

type InitFunc func(root string) (Driver, error)

type Driver interface {
	Create(id, parent string) error
	Remove(id string) error

	Get(id string) (dir string, err error)

	Diff(id string) (archive.Archive, error)
	DiffSize(id string) (bytes int64, err error)
	Changes(id string) ([]archive.Change, error)

	Cleanup() error
}

var (
	// All registred drivers
	drivers map[string]InitFunc
	// Slice of drivers that should be used in an order
	priority = []string{
		"aufs",
		"devicemapper",
		"dummy",
	}
)

func init() {
	drivers = make(map[string]InitFunc)
}

func Register(name string, initFunc InitFunc) error {
	if _, exists := drivers[name]; exists {
		return fmt.Errorf("Name already registered %s", name)
	}
	drivers[name] = initFunc

	return nil
}

func getDriver(name, home string) (Driver, error) {
	if initFunc, exists := drivers[name]; exists {
		return initFunc(home)
	}
	return nil, fmt.Errorf("No such driver: %s", name)
}

func New(root string) (Driver, error) {
	var driver Driver
	var lastError error
	// Use environment variable DOCKER_DRIVER to force a choice of driver
	if name := os.Getenv("DOCKER_DRIVER"); name != "" {
		return getDriver(name, root)
	}
	// Check for priority drivers first
	for _, name := range priority {
		driver, lastError = getDriver(name, root)
		if lastError != nil {
			continue
		}
		return driver, nil
	}

	// Check all registered drivers if no priority driver is found
	for _, initFunc := range drivers {
		driver, lastError = initFunc(root)
		if lastError != nil {
			continue
		}
		return driver, nil
	}
	return nil, lastError
}
<|endoftext|>