<commit_before>// Copyright 2014-2015 Apptimist, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"runtime"
	"time"

	"github.com/apptimistco/asn/debug"
	"github.com/apptimistco/asn/debug/mutex"
	"golang.org/x/net/websocket"
)

const (
	ldl = 100 * time.Millisecond
)

type nopCloserWriter struct {
	io.Writer
}

func (nopCloserWriter) Close() error { return nil }

// NopCloserWriter returns a WriteCloser with a no-op Close method wrapping
// the provided writer.
func NopCloserWriter(w io.Writer) io.WriteCloser {
	return nopCloserWriter{w}
}

type SrvListener struct {
	ln    Listener
	stop  chan struct{}
	done  chan error
	ws    bool
	clean string
}

type Server struct {
	mutex.Mutex
	cmd       *Command
	repos     Repos
	listeners []*SrvListener
	sessions  []*Ses

	listening struct {
		stop chan struct{}
		done chan struct{}
	}
}

func (cmd *Command) Server(args ...string) {
	srv := &Server{
		cmd:       cmd,
		listeners: make([]*SrvListener, 0),
		sessions:  make([]*Ses, 0),
	}
	err := cmd.Cfg.Check(ServerMode)
	defer func() { cmd.Done <- err }()
	if err != nil {
		runtime.Goexit()
	}
	srv.Mutex.Set(cmd.Cfg.Name)
	if err = srv.repos.Set(cmd.Cfg.Dir); err != nil {
		runtime.Goexit()
	}
	defer func() { srv.repos.Reset() }()
	for _, k := range []*UserKeys{
		srv.cmd.Cfg.Keys.Admin,
		srv.cmd.Cfg.Keys.Server,
	} {
		user := srv.repos.users.User(k.Pub.Encr)
		if user == nil {
			user, err = srv.repos.NewUser(k.Pub.Encr)
			if err != nil {
				runtime.Goexit()
			}
			user.cache.Auth().Set(k.Pub.Auth)
			user.cache.Author().Set(k.Pub.Encr)
		}
		user = nil
	}
	if len(args) > 0 {
		// local server command line exec
		var ses Ses
		// FIXME ses.asn.Init()
		// FIXME defer ses.Reset()
		ses.Set(srv)
		ses.Set(&srv.cmd.Cfg)
		ses.Set(&srv.repos)
		ses.Set(srv.ForEachLogin)
		admin := srv.cmd.Cfg.Keys.Admin.Pub.Encr
		ses.Keys.Client.Login = *admin
		ses.asnsrv = true
		ses.user = ses.asn.repos.users.User(admin)
		v := ses.Exec(NewReqString("exec"), cmd.Stdin, args...)
		err, _ = v.(error)
		AckOut(cmd.Stdout, v)
		v = nil
		runtime.Goexit()
	}
	if err = srv.Listen(); err != nil {
		runtime.Goexit()
	}
	cmd.Stdin.Close()
	cmd.Stdout.Close()
	cmd.Stdout = NopCloserWriter(ioutil.Discard)
	cmd.Stderr.Close()
	cmd.Stderr = NopCloserWriter(ioutil.Discard)
	for {
		sig := <-srv.cmd.Sig
		srv.Diag("caught", sig)
		switch {
		case IsINT(sig):
			debug.Trace.WriteTo(debug.Log)
			srv.Close()
			srv.Hangup()
			runtime.Goexit()
		case IsTERM(sig):
			srv.Close()
		case IsUSR1(sig):
			debug.Trace.WriteTo(debug.Log)
		}
	}
}

func (srv *Server) AddListener(l *SrvListener) {
	srv.Lock()
	defer srv.Unlock()
	for _, p := range srv.listeners {
		if p == nil {
			p = l
			return
		}
	}
	srv.listeners = append(srv.listeners, l)
}

func (srv *Server) Close() {
	for i, le := range srv.listeners {
		if le.ws {
			le.ln.Close()
		} else {
			le.stop <- struct{}{}
			<-le.done
		}
		close(le.stop)
		close(le.done)
		le.ln = nil
		srv.listeners[i] = nil
	}
	srv.listeners = nil
}

func (srv *Server) ForEachLogin(f func(*Ses)) {
	srv.Lock()
	defer srv.Unlock()
	for _, ses := range srv.sessions {
		if ses != nil && ses.asn.state == established {
			f(ses)
		}
	}
}

func (srv *Server) handler(conn net.Conn) {
	var ses Ses
	svc := srv.cmd.Cfg.Keys
	ses.asn.Init()
	ses.Set(&srv.cmd.Cfg)
	ses.Set(&srv.repos)
	ses.Set(srv.ForEachLogin)
	srv.add(&ses)
	ses.asn.Set(conn)
	defer func() {
		r := recover()
		ses.Lock()
		ses.Unlock()
		if r != nil {
			err := r.(error)
			ses.asn.Diag(debug.Depth(3), err)
		}
		for i := 0; ses.asn.tx.going; i += 1 {
			if i == 0 {
				close(ses.asn.tx.ch)
			}
			time.Sleep(100 * time.Millisecond)
			if i == 3 {
				panic("can't close connection")
			}
		}
		user := srv.repos.users.User(&ses.Keys.Client.Login)
		if user != nil && user.logins > 0 {
			user.logins -= 1
		}
		srv.rm(&ses)
		srv.Log("disconnected", &ses.Keys.Client.Ephemeral)
		ses.Reset()
	}()
	conn.Read(ses.Keys.Client.Ephemeral[:])
	ses.asn.Set(NewBox(2, srv.cmd.Cfg.Keys.Nonce,
		&ses.Keys.Client.Ephemeral, svc.Server.Pub.Encr,
		svc.Server.Sec.Encr))
	srv.Log("connected", &ses.Keys.Client.Ephemeral)
	for {
		pdu, opened := <-ses.asn.rx.ch
		if !opened {
			runtime.Goexit()
		}
		err := pdu.Open()
		if err != nil {
			pdu.Free()
			panic(err)
		}
		var v Version
		v.ReadFrom(pdu)
		// FIXME to adjust version ... ses.asn.Set(v)
		var id Id
		id.ReadFrom(pdu)
		id.Internal(v)
		ses.asn.time.out = time.Now()
		switch id {
		case AckReqId:
			err = ses.asn.AckerRx(pdu)
		case ExecReqId:
			err = ses.RxExec(pdu)
		case LoginReqId:
			if err = ses.RxLogin(pdu); err != nil {
				panic(err)
			}
			ses.asn.Log("login, ephemeral:",
				&ses.Keys.Client.Login,
				&ses.Keys.Client.Ephemeral)
		case BlobId:
			if bytes.Equal(ses.Keys.Client.Login.Bytes(),
				svc.Admin.Pub.Encr.Bytes()) ||
				bytes.Equal(ses.Keys.Client.Login.Bytes(),
					svc.Server.Pub.Encr.Bytes()) {
				_, err = ses.asn.repos.Store(&ses, v, nil, pdu)
			} else {
				err = os.ErrPermission
			}
			if err != nil {
				ses.asn.Diag(err)
			}
		default:
			if id >= Nids {
				panic(ErrIncompatible)
			} else {
				panic(ErrUnsupported)
			}
		}
		pdu.Free()
		pdu = nil
		if err != nil {
			panic(err)
		}
	}
}

func (srv *Server) Hangup() {
	srv.Lock()
	for _, ses := range srv.sessions {
		if ses != nil && ses.asn.tx.going {
			close(ses.asn.tx.ch)
		}
	}
	srv.Unlock()
	for {
		active := 0
		srv.Lock()
		for _, ses := range srv.sessions {
			if ses != nil {
				active += 1
			}
		}
		srv.Unlock()
		if active == 0 {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
	srv.sessions = nil
}

func (srv *Server) Listen() error {
	for _, lurl := range srv.cmd.Cfg.Listen {
		l := &SrvListener{
			stop: make(chan struct{}, 1),
			done: make(chan error, 1),
		}
		switch lurl.Scheme {
		case "tcp":
			addr, err := net.ResolveTCPAddr(lurl.Scheme, lurl.Host)
			if err != nil {
				return err
			}
			l.ln, err = net.ListenTCP(lurl.Scheme, addr)
			if err != nil {
				return err
			}
			srv.AddListener(l)
			srv.Diag("listening on", addr)
			go l.listen(srv)
		case "unix":
			path := UrlPathSearch(lurl.Path)
			os.Remove(path)
			addr, err := net.ResolveUnixAddr(lurl.Scheme, path)
			if err != nil {
				return err
			}
			l.ln, err = net.ListenUnix(lurl.Scheme, addr)
			if err != nil {
				return err
			}
			srv.AddListener(l)
			l.clean = path
			srv.Diag("listening on", addr)
			go l.listen(srv)
		case "ws":
			l.ws = true
			addr, err := net.ResolveTCPAddr("tcp", lurl.Host)
			if err != nil {
				return err
			}
			if l.ln, err = net.ListenTCP("tcp", addr); err != nil {
				return err
			}
			srv.AddListener(l)
			f := func(ws *websocket.Conn) {
				srv.handler(ws)
			}
			/*
				FIXME should use a custom handler
				h := func (w http.ResponseWriter, req *http.Request) {
					s := websocket.Server{Handler: websocket.Handler(webHandler)}
					s.ServeHTTP(w, req)
				});
				s := &http.Server{
					Addr:           ":8080",
					Handler:        h,
					ReadTimeout:    10 * time.Second,
					WriteTimeout:   10 * time.Second,
					MaxHeaderBytes: 1 << 20,
				}
				return s.Serve(l)
			*/
			mux := http.NewServeMux()
			mux.Handle(lurl.Path, websocket.Handler(f))
			srv.Diag("listening on", addr)
			go http.Serve(l.ln, mux)
		default:
			err := &Error{lurl.Scheme, "unsupported"}
			srv.Diag(err)
			return err
		}
	}
	return nil
}

func (srv *Server) add(ses *Ses) {
	srv.Lock()
	defer srv.Unlock()
	for i := range srv.sessions {
		if srv.sessions[i] == nil {
			srv.sessions[i] = ses
			return
		}
	}
	srv.sessions = append(srv.sessions, ses)
}

func (srv *Server) rm(ses *Ses) {
	srv.Lock()
	defer srv.Unlock()
	for i := range srv.sessions {
		if srv.sessions[i] == ses {
			srv.sessions[i] = nil
			break
		}
	}
}

func (l *SrvListener) listen(srv *Server) {
	for {
		select {
		case <-l.stop:
			err := l.ln.Close()
			if len(l.clean) > 0 {
				os.Remove(l.clean)
			}
			l.done <- err
			return
		default:
			l.ln.SetDeadline(time.Now().Add(ldl))
			conn, err := l.ln.Accept()
			if err == nil {
				go srv.handler(conn)
			} else if opErr, ok := err.(*net.OpError); !ok ||
				!opErr.Timeout() {
				srv.Diag("accept", err)
			}
		}
	}
}
<commit_msg>Set 10sec Deadline and check for errors from Ephemeral Read<commit_after>// Copyright 2014-2015 Apptimist, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"runtime"
	"time"

	"github.com/apptimistco/asn/debug"
	"github.com/apptimistco/asn/debug/mutex"
	"golang.org/x/net/websocket"
)

const (
	ldl = 100 * time.Millisecond
)

type nopCloserWriter struct {
	io.Writer
}

func (nopCloserWriter) Close() error { return nil }

// NopCloserWriter returns a WriteCloser with a no-op Close method wrapping
// the provided writer.
func NopCloserWriter(w io.Writer) io.WriteCloser {
	return nopCloserWriter{w}
}

type SrvListener struct {
	ln    Listener
	stop  chan struct{}
	done  chan error
	ws    bool
	clean string
}

type Server struct {
	mutex.Mutex
	cmd       *Command
	repos     Repos
	listeners []*SrvListener
	sessions  []*Ses

	listening struct {
		stop chan struct{}
		done chan struct{}
	}
}

func (cmd *Command) Server(args ...string) {
	srv := &Server{
		cmd:       cmd,
		listeners: make([]*SrvListener, 0),
		sessions:  make([]*Ses, 0),
	}
	err := cmd.Cfg.Check(ServerMode)
	defer func() { cmd.Done <- err }()
	if err != nil {
		runtime.Goexit()
	}
	srv.Mutex.Set(cmd.Cfg.Name)
	if err = srv.repos.Set(cmd.Cfg.Dir); err != nil {
		runtime.Goexit()
	}
	defer func() { srv.repos.Reset() }()
	for _, k := range []*UserKeys{
		srv.cmd.Cfg.Keys.Admin,
		srv.cmd.Cfg.Keys.Server,
	} {
		user := srv.repos.users.User(k.Pub.Encr)
		if user == nil {
			user, err = srv.repos.NewUser(k.Pub.Encr)
			if err != nil {
				runtime.Goexit()
			}
			user.cache.Auth().Set(k.Pub.Auth)
			user.cache.Author().Set(k.Pub.Encr)
		}
		user = nil
	}
	if len(args) > 0 {
		// local server command line exec
		var ses Ses
		// FIXME ses.asn.Init()
		// FIXME defer ses.Reset()
		ses.Set(srv)
		ses.Set(&srv.cmd.Cfg)
		ses.Set(&srv.repos)
		ses.Set(srv.ForEachLogin)
		admin := srv.cmd.Cfg.Keys.Admin.Pub.Encr
		ses.Keys.Client.Login = *admin
		ses.asnsrv = true
		ses.user = ses.asn.repos.users.User(admin)
		v := ses.Exec(NewReqString("exec"), cmd.Stdin, args...)
		err, _ = v.(error)
		AckOut(cmd.Stdout, v)
		v = nil
		runtime.Goexit()
	}
	if err = srv.Listen(); err != nil {
		runtime.Goexit()
	}
	cmd.Stdin.Close()
	cmd.Stdout.Close()
	cmd.Stdout = NopCloserWriter(ioutil.Discard)
	cmd.Stderr.Close()
	cmd.Stderr = NopCloserWriter(ioutil.Discard)
	for {
		sig := <-srv.cmd.Sig
		srv.Diag("caught", sig)
		switch {
		case IsINT(sig):
			debug.Trace.WriteTo(debug.Log)
			srv.Close()
			srv.Hangup()
			runtime.Goexit()
		case IsTERM(sig):
			srv.Close()
		case IsUSR1(sig):
			debug.Trace.WriteTo(debug.Log)
		}
	}
}

func (srv *Server) AddListener(l *SrvListener) {
	srv.Lock()
	defer srv.Unlock()
	for _, p := range srv.listeners {
		if p == nil {
			p = l
			return
		}
	}
	srv.listeners = append(srv.listeners, l)
}

func (srv *Server) Close() {
	for i, le := range srv.listeners {
		if le.ws {
			le.ln.Close()
		} else {
			le.stop <- struct{}{}
			<-le.done
		}
		close(le.stop)
		close(le.done)
		le.ln = nil
		srv.listeners[i] = nil
	}
	srv.listeners = nil
}

func (srv *Server) ForEachLogin(f func(*Ses)) {
	srv.Lock()
	defer srv.Unlock()
	for _, ses := range srv.sessions {
		if ses != nil && ses.asn.state == established {
			f(ses)
		}
	}
}

func (srv *Server) handler(conn net.Conn) {
	var ses Ses
	svc := srv.cmd.Cfg.Keys
	ses.asn.Init()
	ses.Set(&srv.cmd.Cfg)
	ses.Set(&srv.repos)
	ses.Set(srv.ForEachLogin)
	srv.add(&ses)
	ses.asn.Set(conn)
	defer func() {
		r := recover()
		ses.Lock()
		ses.Unlock()
		if r != nil {
			err := r.(error)
			ses.asn.Diag(debug.Depth(3), err)
		}
		for i := 0; ses.asn.tx.going; i += 1 {
			if i == 0 {
				close(ses.asn.tx.ch)
			}
			time.Sleep(100 * time.Millisecond)
			if i == 3 {
				panic("can't close connection")
			}
		}
		user := srv.repos.users.User(&ses.Keys.Client.Login)
		if user != nil && user.logins > 0 {
			user.logins -= 1
		}
		srv.rm(&ses)
		srv.Log("disconnected", &ses.Keys.Client.Ephemeral)
		ses.Reset()
	}()
	conn.SetReadDeadline(time.Now().Add(10 * time.Second))
	n, err := conn.Read(ses.Keys.Client.Ephemeral[:])
	conn.SetReadDeadline(time.Time{})
	if err != nil {
		srv.Log(err)
		panic(err)
	}
	if n != PubEncrSz {
		panic(Error{"Oops!", "incomplete ephemeral key"})
	}
	ses.asn.Set(NewBox(2, srv.cmd.Cfg.Keys.Nonce,
		&ses.Keys.Client.Ephemeral, svc.Server.Pub.Encr,
		svc.Server.Sec.Encr))
	srv.Log("connected", &ses.Keys.Client.Ephemeral)
	for {
		pdu, opened := <-ses.asn.rx.ch
		if !opened {
			runtime.Goexit()
		}
		err := pdu.Open()
		if err != nil {
			pdu.Free()
			panic(err)
		}
		var v Version
		v.ReadFrom(pdu)
		// FIXME to adjust version ... ses.asn.Set(v)
		var id Id
		id.ReadFrom(pdu)
		id.Internal(v)
		ses.asn.time.out = time.Now()
		switch id {
		case AckReqId:
			err = ses.asn.AckerRx(pdu)
		case ExecReqId:
			err = ses.RxExec(pdu)
		case LoginReqId:
			if err = ses.RxLogin(pdu); err != nil {
				panic(err)
			}
			ses.asn.Log("login, ephemeral:",
				&ses.Keys.Client.Login,
				&ses.Keys.Client.Ephemeral)
		case BlobId:
			if bytes.Equal(ses.Keys.Client.Login.Bytes(),
				svc.Admin.Pub.Encr.Bytes()) ||
				bytes.Equal(ses.Keys.Client.Login.Bytes(),
					svc.Server.Pub.Encr.Bytes()) {
				_, err = ses.asn.repos.Store(&ses, v, nil, pdu)
			} else {
				err = os.ErrPermission
			}
			if err != nil {
				ses.asn.Diag(err)
			}
		default:
			if id >= Nids {
				panic(ErrIncompatible)
			} else {
				panic(ErrUnsupported)
			}
		}
		pdu.Free()
		pdu = nil
		if err != nil {
			panic(err)
		}
	}
}

func (srv *Server) Hangup() {
	srv.Lock()
	for _, ses := range srv.sessions {
		if ses != nil && ses.asn.tx.going {
			close(ses.asn.tx.ch)
		}
	}
	srv.Unlock()
	for {
		active := 0
		srv.Lock()
		for _, ses := range srv.sessions {
			if ses != nil {
				active += 1
			}
		}
		srv.Unlock()
		if active == 0 {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
	srv.sessions = nil
}

func (srv *Server) Listen() error {
	for _, lurl := range srv.cmd.Cfg.Listen {
		l := &SrvListener{
			stop: make(chan struct{}, 1),
			done: make(chan error, 1),
		}
		switch lurl.Scheme {
		case "tcp":
			addr, err := net.ResolveTCPAddr(lurl.Scheme, lurl.Host)
			if err != nil {
				return err
			}
			l.ln, err = net.ListenTCP(lurl.Scheme, addr)
			if err != nil {
				return err
			}
			srv.AddListener(l)
			srv.Diag("listening on", addr)
			go l.listen(srv)
		case "unix":
			path := UrlPathSearch(lurl.Path)
			os.Remove(path)
			addr, err := net.ResolveUnixAddr(lurl.Scheme, path)
			if err != nil {
				return err
			}
			l.ln, err = net.ListenUnix(lurl.Scheme, addr)
			if err != nil {
				return err
			}
			srv.AddListener(l)
			l.clean = path
			srv.Diag("listening on", addr)
			go l.listen(srv)
		case "ws":
			l.ws = true
			addr, err := net.ResolveTCPAddr("tcp", lurl.Host)
			if err != nil {
				return err
			}
			if l.ln, err = net.ListenTCP("tcp", addr); err != nil {
				return err
			}
			srv.AddListener(l)
			f := func(ws *websocket.Conn) {
				srv.handler(ws)
			}
			/*
				FIXME should use a custom handler
				h := func (w http.ResponseWriter, req *http.Request) {
					s := websocket.Server{Handler: websocket.Handler(webHandler)}
					s.ServeHTTP(w, req)
				});
				s := &http.Server{
					Addr:           ":8080",
					Handler:        h,
					ReadTimeout:    10 * time.Second,
					WriteTimeout:   10 * time.Second,
					MaxHeaderBytes: 1 << 20,
				}
				return s.Serve(l)
			*/
			mux := http.NewServeMux()
			mux.Handle(lurl.Path, websocket.Handler(f))
			srv.Diag("listening on", addr)
			go http.Serve(l.ln, mux)
		default:
			err := &Error{lurl.Scheme, "unsupported"}
			srv.Diag(err)
			return err
		}
	}
	return nil
}

func (srv *Server) add(ses *Ses) {
	srv.Lock()
	defer srv.Unlock()
	for i := range srv.sessions {
		if srv.sessions[i] == nil {
			srv.sessions[i] = ses
			return
		}
	}
	srv.sessions = append(srv.sessions, ses)
}

func (srv *Server) rm(ses *Ses) {
	srv.Lock()
	defer srv.Unlock()
	for i := range srv.sessions {
		if srv.sessions[i] == ses {
			srv.sessions[i] = nil
			break
		}
	}
}

func (l *SrvListener) listen(srv *Server) {
	for {
		select {
		case <-l.stop:
			err := l.ln.Close()
			if len(l.clean) > 0 {
				os.Remove(l.clean)
			}
			l.done <- err
			return
		default:
			l.ln.SetDeadline(time.Now().Add(ldl))
			conn, err := l.ln.Accept()
			if err == nil {
				go srv.handler(conn)
			} else if opErr, ok := err.(*net.OpError); !ok ||
				!opErr.Timeout() {
				srv.Diag("accept", err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package sse // import astuart.co/go-sse

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net/http"
)

//SSE name constants
const (
	eName = "event"
	dName = "data"
)

var (
	//ErrNilChan will be returned by Notify if it is passed a nil channel
	ErrNilChan = fmt.Errorf("nil channel given")
)

//Client is the default client used for requests.
var Client = &http.Client{}

func liveReq(verb, uri string, body io.Reader) (*http.Request, error) {
	req, err := GetReq(verb, uri, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Accept", "text/event-stream")

	return req, nil
}

//Event is a go representation of an http server-sent event
type Event struct {
	URI  string
	Type string
	Data io.Reader
}

//GetReq is a function to return a single request. It will be used by notify to
//get a request and can be replaces if additional configuration is desired on
//the request. The "Accept" header will necessarily be overwritten.
var GetReq = func(verb, uri string, body io.Reader) (*http.Request, error) {
	return http.NewRequest(verb, uri, body)
}

//Notify takes the uri of an SSE stream and channel, and will send an Event
//down the channel when recieved, until the stream is closed. It will then
//close the stream. This is blocking, and so you will likely want to call this
//in a new goroutine (via `go Notify(..)`)
func Notify(uri string, evCh chan<- *Event) error {
	if evCh == nil {
		return ErrNilChan
	}

	req, err := liveReq("GET", uri, nil)
	if err != nil {
		return fmt.Errorf("error getting sse request: %v", err)
	}

	res, err := Client.Do(req)
	if err != nil {
		return fmt.Errorf("error performing request for %s: %v", uri, err)
	}

	br := bufio.NewReader(res.Body)
	defer res.Body.Close()

	delim := []byte{':', ' '}

	var currEvent *Event

	for {
		bs, err := br.ReadBytes('\n')

		if err != nil && err != io.EOF {
			return err
		}

		if len(bs) < 2 {
			continue
		}

		spl := bytes.Split(bs, delim)

		if len(spl) < 2 {
			continue
		}

		switch string(spl[0]) {
		case eName:
			currEvent = &Event{URI: uri}
			currEvent.Type = string(bytes.TrimSpace(spl[1]))
		case dName:
			currEvent.Data = bytes.NewBuffer(bytes.TrimSpace(spl[1]))
			evCh <- currEvent
		}
		if err == io.EOF {
			break
		}
	}

	return nil
}
<commit_msg>fix import directive<commit_after>package sse // import "astuart.co/go-sse"

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"net/http"
)

//SSE name constants
const (
	eName = "event"
	dName = "data"
)

var (
	//ErrNilChan will be returned by Notify if it is passed a nil channel
	ErrNilChan = fmt.Errorf("nil channel given")
)

//Client is the default client used for requests.
var Client = &http.Client{}

func liveReq(verb, uri string, body io.Reader) (*http.Request, error) {
	req, err := GetReq(verb, uri, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Accept", "text/event-stream")

	return req, nil
}

//Event is a go representation of an http server-sent event
type Event struct {
	URI  string
	Type string
	Data io.Reader
}

//GetReq is a function to return a single request. It will be used by notify to
//get a request and can be replaces if additional configuration is desired on
//the request. The "Accept" header will necessarily be overwritten.
var GetReq = func(verb, uri string, body io.Reader) (*http.Request, error) {
	return http.NewRequest(verb, uri, body)
}

//Notify takes the uri of an SSE stream and channel, and will send an Event
//down the channel when recieved, until the stream is closed. It will then
//close the stream. This is blocking, and so you will likely want to call this
//in a new goroutine (via `go Notify(..)`)
func Notify(uri string, evCh chan<- *Event) error {
	if evCh == nil {
		return ErrNilChan
	}

	req, err := liveReq("GET", uri, nil)
	if err != nil {
		return fmt.Errorf("error getting sse request: %v", err)
	}

	res, err := Client.Do(req)
	if err != nil {
		return fmt.Errorf("error performing request for %s: %v", uri, err)
	}

	br := bufio.NewReader(res.Body)
	defer res.Body.Close()

	delim := []byte{':', ' '}

	var currEvent *Event

	for {
		bs, err := br.ReadBytes('\n')

		if err != nil && err != io.EOF {
			return err
		}

		if len(bs) < 2 {
			continue
		}

		spl := bytes.Split(bs, delim)

		if len(spl) < 2 {
			continue
		}

		switch string(spl[0]) {
		case eName:
			currEvent = &Event{URI: uri}
			currEvent.Type = string(bytes.TrimSpace(spl[1]))
		case dName:
			currEvent.Data = bytes.NewBuffer(bytes.TrimSpace(spl[1]))
			evCh <- currEvent
		}
		if err == io.EOF {
			break
		}
	}

	return nil
}
<|endoftext|>


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

// Garbage collection benchmark: parse Go packages repeatedly.

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"log"
	"net/http"
	_ "net/http/pprof"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

var serve = flag.String("serve", "", "serve http on this address at end")

func isGoFile(dir os.FileInfo) bool {
	return !dir.IsDir() &&
		!strings.HasPrefix(dir.Name(), ".") && // ignore .files
		path.Ext(dir.Name()) == ".go"
}

func isPkgFile(dir os.FileInfo) bool {
	return isGoFile(dir) &&
		!strings.HasSuffix(dir.Name(), "_test.go") // ignore test files
}

func pkgName(filename string) string {
	file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly)
	if err != nil || file == nil {
		return ""
	}
	return file.Name.Name
}

func parseDir(dirpath string) map[string]*ast.Package {
	// the package name is the directory name within its parent
	// (use dirname instead of path because dirname is clean; i.e. has no trailing '/')
	_, pkgname := path.Split(dirpath)

	// filter function to select the desired .go files
	filter := func(d os.FileInfo) bool {
		if isPkgFile(d) {
			// Some directories contain main packages: Only accept
			// files that belong to the expected package so that
			// parser.ParsePackage doesn't return "multiple packages
			// found" errors.
			// Additionally, accept the special package name
			// fakePkgName if we are looking at cmd documentation.
			name := pkgName(dirpath + "/" + d.Name())
			return name == pkgname
		}
		return false
	}

	// get package AST
	pkgs, err := parser.ParseDir(token.NewFileSet(), dirpath, filter, parser.ParseComments)
	if err != nil {
		println("parse", dirpath, err.Error())
		panic("fail")
	}
	return pkgs
}

func main() {
	st := new(runtime.MemStats)
	packages = append(packages, packages...)
	packages = append(packages, packages...)
	n := flag.Int("n", 4, "iterations")
	p := flag.Int("p", len(packages), "# of packages to keep in memory")
	flag.BoolVar(&st.DebugGC, "d", st.DebugGC, "print GC debugging info (pause times)")
	flag.Parse()

	var lastParsed []map[string]*ast.Package
	var t0 time.Time
	var numGC uint32
	var pauseTotalNs uint64
	pkgroot := runtime.GOROOT() + "/src/pkg/"
	for pass := 0; pass < 2; pass++ {
		// Once the heap is grown to full size, reset counters.
		// This hides the start-up pauses, which are much smaller
		// than the normal pauses and would otherwise make
		// the average look much better than it actually is.
		runtime.ReadMemStats(st)
		numGC = st.NumGC
		pauseTotalNs = st.PauseTotalNs
		t0 = time.Now()

		for i := 0; i < *n; i++ {
			parsed := make([]map[string]*ast.Package, *p)
			for j := range parsed {
				parsed[j] = parseDir(pkgroot + packages[j%len(packages)])
			}
			if i+1 == *n && *serve != "" {
				lastParsed = parsed
			}
		}
		runtime.GC()
		runtime.GC()
	}
	t1 := time.Now()

	runtime.ReadMemStats(st)
	st.NumGC -= numGC
	st.PauseTotalNs -= pauseTotalNs
	fmt.Printf("Alloc=%d/%d Heap=%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n",
		st.Alloc, st.TotalAlloc,
		st.Sys,
		st.Mallocs, float64(st.PauseTotalNs)/1e9,
		st.NumGC, float64(st.PauseTotalNs)/1e9/float64(st.NumGC))

	/*
		fmt.Printf("%10s %10s %10s\n", "size", "#alloc", "#free")
		for _, s := range st.BySize {
			fmt.Printf("%10d %10d %10d\n", s.Size, s.Mallocs, s.Frees)
		}
	*/
	// Standard gotest benchmark output, collected by build dashboard.
	gcstats("BenchmarkParser", *n, t1.Sub(t0))

	if *serve != "" {
		log.Fatal(http.ListenAndServe(*serve, nil))
		println(lastParsed)
	}
}

// find . -type d -not -path "./exp" -not -path "./exp/*" -printf "\t\"%p\",\n" | sort | sed "s/\.\///" | grep -v testdata
var packages = []string{
	"archive",
	"archive/tar",
	"archive/zip",
	"bufio",
	"builtin",
	"bytes",
	"compress",
	"compress/bzip2",
	"compress/flate",
	"compress/gzip",
	"compress/lzw",
	"compress/zlib",
	"container",
	"container/heap",
	"container/list",
	"container/ring",
	"crypto",
	"crypto/aes",
	"crypto/cipher",
	"crypto/des",
	"crypto/dsa",
	"crypto/ecdsa",
	"crypto/elliptic",
	"crypto/hmac",
	"crypto/md5",
	"crypto/rand",
	"crypto/rc4",
	"crypto/rsa",
	"crypto/sha1",
	"crypto/sha256",
	"crypto/sha512",
	"crypto/subtle",
	"crypto/tls",
	"crypto/x509",
	"crypto/x509/pkix",
	"database",
	"database/sql",
	"database/sql/driver",
	"debug",
	"debug/dwarf",
	"debug/elf",
	"debug/gosym",
	"debug/macho",
	"debug/pe",
	"encoding",
	"encoding/ascii85",
	"encoding/asn1",
	"encoding/base32",
	"encoding/base64",
	"encoding/binary",
	"encoding/csv",
	"encoding/gob",
	"encoding/hex",
	"encoding/json",
	"encoding/pem",
	"encoding/xml",
	"errors",
	"expvar",
	"flag",
	"fmt",
	"go",
	"go/ast",
	"go/build",
	"go/doc",
	"go/format",
	"go/parser",
	"go/printer",
	"go/scanner",
	"go/token",
	"go/types",
	"hash",
	"hash/adler32",
	"hash/crc32",
	"hash/crc64",
	"hash/fnv",
	"html",
	"html/template",
	"image",
	"image/color",
	"image/draw",
	"image/gif",
	"image/jpeg",
	"image/png",
	"index",
	"index/suffixarray",
	"io",
	"io/ioutil",
	"log",
	"log/syslog",
	"math",
	"math/big",
	"math/cmplx",
	"math/rand",
	"mime",
	"mime/multipart",
	"net",
	"net/http",
	"net/http/cgi",
	"net/http/cookiejar",
	"net/http/fcgi",
	"net/http/httptest",
	"net/http/httputil",
	"net/http/pprof",
	"net/mail",
	"net/rpc",
	"net/rpc/jsonrpc",
	"net/smtp",
	"net/textproto",
	"net/url",
	"os",
	"os/exec",
	"os/signal",
	"os/user",
	"path",
	"path/filepath",
	"reflect",
	"regexp",
	"regexp/syntax",
	"runtime",
	"runtime/cgo",
	"runtime/debug",
	"runtime/pprof",
	"runtime/race",
	"sort",
	"strconv",
	"strings",
	"sync",
	"sync/atomic",
	"syscall",
	"testing",
	"testing/iotest",
	"testing/quick",
	"text",
	"text/scanner",
	"text/tabwriter",
	"text/template",
	"text/template/parse",
	"time",
	"unicode",
	"unicode/utf16",
	"unicode/utf8",
	"unsafe",
}
<commit_msg>test/bench/garbage: fix parser benchmark<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Garbage collection benchmark: parse Go packages repeatedly.

package main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"log"
	"net/http"
	_ "net/http/pprof"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

var serve = flag.String("serve", "", "serve http on this address at end")

func isGoFile(dir os.FileInfo) bool {
	return !dir.IsDir() &&
		!strings.HasPrefix(dir.Name(), ".") && // ignore .files
		path.Ext(dir.Name()) == ".go"
}

func isPkgFile(dir os.FileInfo) bool {
	return isGoFile(dir) &&
		!strings.HasSuffix(dir.Name(), "_test.go") // ignore test files
}

func pkgName(filename string) string {
	file, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly)
	if err != nil || file == nil {
		return ""
	}
	return file.Name.Name
}

func parseDir(dirpath string) map[string]*ast.Package {
	// the package name is the directory name within its parent
	// (use dirname instead of path because dirname is clean; i.e. has no trailing '/')
	_, pkgname := path.Split(dirpath)

	// filter function to select the desired .go files
	filter := func(d os.FileInfo) bool {
		if isPkgFile(d) {
			// Some directories contain main packages: Only accept
			// files that belong to the expected package so that
			// parser.ParsePackage doesn't return "multiple packages
			// found" errors.
			// Additionally, accept the special package name
			// fakePkgName if we are looking at cmd documentation.
			name := pkgName(dirpath + "/" + d.Name())
			return name == pkgname
		}
		return false
	}

	// get package AST
	pkgs, err := parser.ParseDir(token.NewFileSet(), dirpath, filter, parser.ParseComments)
	if err != nil {
		println("parse", dirpath, err.Error())
		panic("fail")
	}
	return pkgs
}

func main() {
	st := new(runtime.MemStats)
	packages = append(packages, packages...)
	packages = append(packages, packages...)
	n := flag.Int("n", 4, "iterations")
	p := flag.Int("p", len(packages), "# of packages to keep in memory")
	flag.BoolVar(&st.DebugGC, "d", st.DebugGC, "print GC debugging info (pause times)")
	flag.Parse()

	var lastParsed []map[string]*ast.Package
	var t0 time.Time
	var numGC uint32
	var pauseTotalNs uint64
	pkgroot := runtime.GOROOT() + "/src/pkg/"
	for pass := 0; pass < 2; pass++ {
		// Once the heap is grown to full size, reset counters.
		// This hides the start-up pauses, which are much smaller
		// than the normal pauses and would otherwise make
		// the average look much better than it actually is.
		runtime.ReadMemStats(st)
		numGC = st.NumGC
		pauseTotalNs = st.PauseTotalNs
		t0 = time.Now()

		for i := 0; i < *n; i++ {
			parsed := make([]map[string]*ast.Package, *p)
			for j := range parsed {
				parsed[j] = parseDir(pkgroot + packages[j%len(packages)])
			}
			if i+1 == *n && *serve != "" {
				lastParsed = parsed
			}
		}
		runtime.GC()
		runtime.GC()
	}
	t1 := time.Now()

	runtime.ReadMemStats(st)
	st.NumGC -= numGC
	st.PauseTotalNs -= pauseTotalNs
	fmt.Printf("Alloc=%d/%d Heap=%d Mallocs=%d PauseTime=%.3f/%d = %.3f\n",
		st.Alloc, st.TotalAlloc,
		st.Sys,
		st.Mallocs, float64(st.PauseTotalNs)/1e9,
		st.NumGC, float64(st.PauseTotalNs)/1e9/float64(st.NumGC))

	/*
		fmt.Printf("%10s %10s %10s\n", "size", "#alloc", "#free")
		for _, s := range st.BySize {
			fmt.Printf("%10d %10d %10d\n", s.Size, s.Mallocs, s.Frees)
		}
	*/
	// Standard gotest benchmark output, collected by build dashboard.
	gcstats("BenchmarkParser", *n, t1.Sub(t0))

	if *serve != "" {
		log.Fatal(http.ListenAndServe(*serve, nil))
		println(lastParsed)
	}
}

// find . -type d -not -path "./exp" -not -path "./exp/*" -printf "\t\"%p\",\n" | sort | sed "s/\.\///" | grep -v testdata
var packages = []string{
	"archive",
	"archive/tar",
	"archive/zip",
	"bufio",
	"builtin",
	"bytes",
	"compress",
	"compress/bzip2",
	"compress/flate",
	"compress/gzip",
	"compress/lzw",
	"compress/zlib",
	"container",
	"container/heap",
	"container/list",
	"container/ring",
	"crypto",
	"crypto/aes",
	"crypto/cipher",
	"crypto/des",
	"crypto/dsa",
	"crypto/ecdsa",
	"crypto/elliptic",
	"crypto/hmac",
	"crypto/md5",
	"crypto/rand",
	"crypto/rc4",
	"crypto/rsa",
	"crypto/sha1",
	"crypto/sha256",
	"crypto/sha512",
	"crypto/subtle",
	"crypto/tls",
	"crypto/x509",
	"crypto/x509/pkix",
	"database",
	"database/sql",
	"database/sql/driver",
	"debug",
	"debug/dwarf",
	"debug/elf",
	"debug/gosym",
	"debug/macho",
	"debug/pe",
	"encoding",
	"encoding/ascii85",
	"encoding/asn1",
	"encoding/base32",
	"encoding/base64",
	"encoding/binary",
	"encoding/csv",
	"encoding/gob",
	"encoding/hex",
	"encoding/json",
	"encoding/pem",
	"encoding/xml",
	"errors",
	"expvar",
	"flag",
	"fmt",
	"go",
	"go/ast",
	"go/build",
	"go/doc",
	"go/format",
	"go/parser",
	"go/printer",
	"go/scanner",
	"go/token",
	"hash",
	"hash/adler32",
	"hash/crc32",
	"hash/crc64",
	"hash/fnv",
	"html",
	"html/template",
	"image",
	"image/color",
	"image/draw",
	"image/gif",
	"image/jpeg",
	"image/png",
	"index",
	"index/suffixarray",
	"io",
	"io/ioutil",
	"log",
	"log/syslog",
	"math",
	"math/big",
	"math/cmplx",
	"math/rand",
	"mime",
	"mime/multipart",
	"net",
	"net/http",
	"net/http/cgi",
	"net/http/cookiejar",
	"net/http/fcgi",
	"net/http/httptest",
	"net/http/httputil",
	"net/http/pprof",
	"net/mail",
	"net/rpc",
	"net/rpc/jsonrpc",
	"net/smtp",
	"net/textproto",
	"net/url",
	"os",
	"os/exec",
	"os/signal",
	"os/user",
	"path",
	"path/filepath",
	"reflect",
	"regexp",
	"regexp/syntax",
	"runtime",
	"runtime/cgo",
	"runtime/debug",
	"runtime/pprof",
	"runtime/race",
	"sort",
	"strconv",
	"strings",
	"sync",
	"sync/atomic",
	"syscall",
	"testing",
	"testing/iotest",
	"testing/quick",
	"text",
	"text/scanner",
	"text/tabwriter",
	"text/template",
	"text/template/parse",
	"time",
	"unicode",
	"unicode/utf16",
	"unicode/utf8",
	"unsafe",
}
<|endoftext|>


<commit_before>/*-
 * Copyright (c) 2012 Caoimhe Chaos <caoimhechaos@protonmail.com>,
 *                    Ancient Solutions. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions  of source code must retain  the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions  in   binary  form  must   reproduce  the  above
 *    copyright  notice, this  list  of conditions  and the  following
 *    disclaimer in the  documentation and/or other materials provided
 *    with the distribution.
 *
 * THIS  SOFTWARE IS  PROVIDED BY  ANCIENT SOLUTIONS  AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO,  THE IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS
 * FOR A  PARTICULAR PURPOSE  ARE DISCLAIMED.  IN  NO EVENT  SHALL THE
 * FOUNDATION  OR CONTRIBUTORS  BE  LIABLE FOR  ANY DIRECT,  INDIRECT,
 * INCIDENTAL,   SPECIAL,    EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
 * (INCLUDING, BUT NOT LIMITED  TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE,  DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT  LIABILITY,  OR  TORT  (INCLUDING NEGLIGENCE  OR  OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"net/rpc"
	"os"
	"strings"

	"ancient-solutions.com/net/geocolo"
	"github.com/caoimhechaos/go-urlconnection"
)

func main() {
	var endpoint, uri, buri, origin, candidates string
	var maxdistance float64
	var client *rpc.Client
	var mode string
	var conn net.Conn
	var detailed bool
	var err error

	flag.StringVar(&endpoint, "endpoint", "",
		"The service URL to connect to")
	flag.StringVar(&uri, "doozer-uri", os.Getenv("DOOZER_URI"),
		"Doozer URI to connect to")
	flag.StringVar(&buri, "doozer-boot-uri", os.Getenv("DOOZER_BOOT_URI"),
		"Doozer Boot URI to find named clusters")
	flag.StringVar(&origin, "origin", "",
		"Country which we're looking for close countries for")
	flag.StringVar(&candidates, "candidates", "",
		"Comma separated list of countries to consider")
	flag.StringVar(&mode, "mode", "country",
		"Method to contact (country or ip)")
	flag.Float64Var(&maxdistance, "max-distance", 0,
		"Maximum distance from the closest IP to consider")
	flag.BoolVar(&detailed, "detailed", false,
		"Whether to give a detailed response")
	flag.Parse()

	if uri != "" {
		if err = urlconnection.SetupDoozer(buri, uri); err != nil {
			log.Fatal("Error initializing Doozer connection to ",
				uri, ": ", err.Error())
		}
	}

	conn, err = urlconnection.Connect(endpoint)
	if err != nil {
		log.Fatal("Error connecting to ", endpoint, ": ", err.Error())
	}
	client = rpc.NewClient(conn)

	if mode == "country" {
		var req geocolo.GeoProximityRequest
		var res geocolo.GeoProximityResponse

		if len(candidates) > 0 {
			req.Candidates = strings.Split(candidates, ",")
		}

		req.Origin = &origin
		req.DetailedResponse = &detailed

		err = client.Call("GeoProximityService.GetProximity", req,
			&res)
		if err != nil {
			log.Fatal("Error sending proximity request: ",
				err.Error())
		}

		if res.Closest == nil {
			log.Fatal("Failed to fetch closest country")
		} else {
			fmt.Printf("Closest country: %s\n", *res.Closest)
		}

		for _, detail := range res.FullMap {
			if detail == nil {
				log.Print("Error: detail is nil?")
			} else if detail.Country == nil {
				log.Print("Error: country is nil?")
				if detail.Distance != nil {
					log.Printf("(distance was %f)",
						*detail.Distance)
				}
			} else if detail.Distance == nil {
				log.Print("Error: distance is nil?")
				if detail.Country != nil {
					log.Printf("(country was %s)", *detail.Country)
				}
			} else {
				fmt.Printf("Country %s: distance %f\n", *detail.Country,
					*detail.Distance)
			}
		}
	} else if mode == "ip" {
		var req geocolo.GeoProximityByIPRequest
		var res geocolo.GeoProximityByIPResponse

		req.Candidates = strings.Split(candidates, ",")
		req.DetailedResponse = &detailed
		req.Origin = &origin
		req.MaxDistance = &maxdistance

		err = client.Call("GeoProximityService.GetProximityByIP",
			req, &res)
		if err != nil {
			log.Fatal("Error sending proximity request: ",
				err.Error())
		}

		for _, addr := range res.Closest {
			fmt.Printf("Close IP: %s\n", addr)
		}

		for _, detail := range res.FullMap {
			fmt.Printf("IP: %s, distance: %f\n", *detail.Ip,
				*detail.Distance)
		}
	}
}
<commit_msg>Also update the geocolo client to use etcd.<commit_after>/*-
 * Copyright (c) 2012 Caoimhe Chaos <caoimhechaos@protonmail.com>,
 *                    Ancient Solutions. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions  of source code must retain  the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions  in   binary  form  must   reproduce  the  above
 *    copyright  notice, this  list  of conditions  and the  following
 *    disclaimer in the  documentation and/or other materials provided
 *    with the distribution.
 *
 * THIS  SOFTWARE IS  PROVIDED BY  ANCIENT SOLUTIONS  AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO,  THE IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS
 * FOR A  PARTICULAR PURPOSE  ARE DISCLAIMED.  IN  NO EVENT  SHALL THE
 * FOUNDATION  OR CONTRIBUTORS  BE  LIABLE FOR  ANY DIRECT,  INDIRECT,
 * INCIDENTAL,   SPECIAL,    EXEMPLARY,   OR   CONSEQUENTIAL   DAMAGES
 * (INCLUDING, BUT NOT LIMITED  TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE,  DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT  LIABILITY,  OR  TORT  (INCLUDING NEGLIGENCE  OR  OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"net/rpc"
	"os"
	"strings"

	"github.com/caoimhechaos/geocolo"
	"github.com/caoimhechaos/go-urlconnection"
)

func main() {
	var endpoint, uri, origin, candidates string
	var cert, key, ca string
	var maxdistance float64
	var client *rpc.Client
	var mode string
	var conn net.Conn
	var detailed bool
	var err error

	flag.StringVar(&endpoint, "endpoint", "",
		"The service URL to connect to")
	flag.StringVar(&uri, "etcd-uri", os.Getenv("ETCD_URI"),
		"etcd URI to connect to")
	flag.StringVar(&origin, "origin", "",
		"Country which we're looking for close countries for")
	flag.StringVar(&candidates, "candidates", "",
		"Comma separated list of countries to consider")
	flag.StringVar(&mode, "mode", "country",
		"Method to contact (country or ip)")
	flag.Float64Var(&maxdistance, "max-distance", 0,
		"Maximum distance from the closest IP to consider")
	flag.BoolVar(&detailed, "detailed", false,
		"Whether to give a detailed response")

	flag.StringVar(&cert, "cert", "",
		"Certificate for connecting (if empty, don't use encryption)")
	flag.StringVar(&key, "key", "",
		"Private key for connecting")
	flag.StringVar(&ca, "ca-cert", "",
		"CA certificate for verifying etcd and geocolo")
	flag.Parse()

	if uri != "" {
		if err = urlconnection.SetupEtcd([]string{uri},
			cert, key, ca); err != nil {
			log.Fatal("Error initializing etcd connection to ",
				uri, ": ", err.Error())
		}
	}

	conn, err = urlconnection.Connect(endpoint)
	if err != nil {
		log.Fatal("Error connecting to ", endpoint, ": ", err.Error())
	}
	client = rpc.NewClient(conn)

	if mode == "country" {
		var req geocolo.GeoProximityRequest
		var res geocolo.GeoProximityResponse

		if len(candidates) > 0 {
			req.Candidates = strings.Split(candidates, ",")
		}

		req.Origin = &origin
		req.DetailedResponse = &detailed

		err = client.Call("GeoProximityService.GetProximity", req,
			&res)
		if err != nil {
			log.Fatal("Error sending proximity request: ",
				err.Error())
		}

		if res.Closest == nil {
			log.Fatal("Failed to fetch closest country")
		} else {
			fmt.Printf("Closest country: %s\n", *res.Closest)
		}

		for _, detail := range res.FullMap {
			if detail == nil {
				log.Print("Error: detail is nil?")
			} else if detail.Country == nil {
				log.Print("Error: country is nil?")
				if detail.Distance != nil {
					log.Printf("(distance was %f)",
						*detail.Distance)
				}
			} else if detail.Distance == nil {
				log.Print("Error: distance is nil?")
				if detail.Country != nil {
					log.Printf("(country was %s)", *detail.Country)
				}
			} else {
				fmt.Printf("Country %s: distance %f\n", *detail.Country,
					*detail.Distance)
			}
		}
	} else if mode == "ip" {
		var req geocolo.GeoProximityByIPRequest
		var res geocolo.GeoProximityByIPResponse

		req.Candidates = strings.Split(candidates, ",")
		req.DetailedResponse = &detailed
		req.Origin = &origin
		req.MaxDistance = &maxdistance

		err = client.Call("GeoProximityService.GetProximityByIP",
			req, &res)
		if err != nil {
			log.Fatal("Error sending proximity request: ",
				err.Error())
		}

		for _, addr := range res.Closest {
			fmt.Printf("Close IP: %s\n", addr)
		}

		for _, detail := range res.FullMap {
			fmt.Printf("IP: %s, distance: %f\n", *detail.Ip,
				*detail.Distance)
		}
	}
}
<|endoftext|>


<commit_before>package cloudflare

import (
	"encoding/json"
	"time"

	"github.com/pkg/errors"
)

// ZoneCustomSSL represents custom SSL certificate metadata.
type ZoneCustomSSL struct {
	ID              string                       `json:"id"`
	Hosts           []string                     `json:"hosts"`
	Issuer          string                       `json:"issuer"`
	Signature       string                       `json:"signature"`
	Status          string                       `json:"status"`
	BundleMethod    string                       `json:"bundle_method"`
	GeoRestrictions ZoneCustomSSLGeoRestrictions `json:"geo_restrictions"`
	ZoneID          string                       `json:"zone_id"`
	UploadedOn      time.Time                    `json:"uploaded_on"`
	ModifiedOn      time.Time                    `json:"modified_on"`
	ExpiresOn       time.Time                    `json:"expires_on"`
	Priority        int                          `json:"priority"`
	KeylessServer   KeylessSSL                   `json:"keyless_server"`
}

type ZoneCustomSSLGeoRestrictions struct {
	Label string `json:"label"`
}

// zoneCustomSSLResponse represents the response from the zone SSL details endpoint.
type zoneCustomSSLResponse struct {
	Response
	Result ZoneCustomSSL `json:"result"`
}

// zoneCustomSSLsResponse represents the response from the zone SSL list endpoint.
type zoneCustomSSLsResponse struct {
	Response
	Result []ZoneCustomSSL `json:"result"`
}

// ZoneCustomSSLOptions represents the parameters to create or update an existing
// custom SSL configuration.
type ZoneCustomSSLOptions struct {
	Certificate     string                       `json:"certificate"`
	PrivateKey      string                       `json:"private_key"`
	BundleMethod    string                       `json:"bundle_method,omitempty"`
	GeoRestrictions ZoneCustomSSLGeoRestrictions `json:"geo_restrictions,omitempty"`
	Type            string                       `json:"type,omitempty"`
}

// ZoneCustomSSLPriority represents a certificate's ID and priority. It is a
// subset of ZoneCustomSSL used for patch requests.
type ZoneCustomSSLPriority struct {
	ID       string `json:"ID"`
	Priority int    `json:"priority"`
}

// CreateSSL allows you to add a custom SSL certificate to the given zone.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-create-ssl-configuration
func (api *API) CreateSSL(zoneID string, options ZoneCustomSSLOptions) (ZoneCustomSSL, error) {
	uri := "/zones/" + zoneID + "/custom_certificates"
	res, err := api.makeRequest("POST", uri, options)
	if err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError)
	}
	var r zoneCustomSSLResponse
	if err := json.Unmarshal(res, &r); err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// ListSSL lists the custom certificates for the given zone.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-list-ssl-configurations
func (api *API) ListSSL(zoneID string) ([]ZoneCustomSSL, error) {
	uri := "/zones/" + zoneID + "/custom_certificates"
	res, err := api.makeRequest("GET", uri, nil)
	if err != nil {
		return nil, errors.Wrap(err, errMakeRequestError)
	}
	var r zoneCustomSSLsResponse
	if err := json.Unmarshal(res, &r); err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// SSLDetails returns the configuration details for a custom SSL certificate.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-ssl-configuration-details
func (api *API) SSLDetails(zoneID, certificateID string) (ZoneCustomSSL, error) {
	uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID
	res, err := api.makeRequest("GET", uri, nil)
	if err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError)
	}
	var r zoneCustomSSLResponse
	if err := json.Unmarshal(res, &r); err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// UpdateSSL updates (replaces) a custom SSL certificate.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-update-ssl-configuration
func (api *API) UpdateSSL(zoneID, certificateID string, options ZoneCustomSSLOptions) (ZoneCustomSSL, error) {
	uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID
	res, err := api.makeRequest("PATCH", uri, options)
	if err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError)
	}
	var r zoneCustomSSLResponse
	if err := json.Unmarshal(res, &r); err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// ReprioritizeSSL allows you to change the priority (which is served for a given
// request) of custom SSL certificates associated with the given zone.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-re-prioritize-ssl-certificates
func (api *API) ReprioritizeSSL(zoneID string, p []ZoneCustomSSLPriority) ([]ZoneCustomSSL, error) {
	uri := "/zones/" + zoneID + "/custom_certificates/prioritize"
	params := struct {
		Certificates []ZoneCustomSSLPriority `json:"certificates"`
	}{
		Certificates: p,
	}
	res, err := api.makeRequest("PUT", uri, params)
	if err != nil {
		return nil, errors.Wrap(err, errMakeRequestError)
	}
	var r zoneCustomSSLsResponse
	if err := json.Unmarshal(res, &r); err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// DeleteSSL deletes a custom SSL certificate from the given zone.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-delete-an-ssl-certificate
func (api *API) DeleteSSL(zoneID, certificateID string) error {
	uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID
	if _, err := api.makeRequest("DELETE", uri, nil); err != nil {
		return errors.Wrap(err, errMakeRequestError)
	}
	return nil
}
<commit_msg>comment to fix linter<commit_after>package cloudflare

import (
	"encoding/json"
	"time"

	"github.com/pkg/errors"
)

// ZoneCustomSSL represents custom SSL certificate metadata.
type ZoneCustomSSL struct {
	ID              string                       `json:"id"`
	Hosts           []string                     `json:"hosts"`
	Issuer          string                       `json:"issuer"`
	Signature       string                       `json:"signature"`
	Status          string                       `json:"status"`
	BundleMethod    string                       `json:"bundle_method"`
	GeoRestrictions ZoneCustomSSLGeoRestrictions `json:"geo_restrictions"`
	ZoneID          string                       `json:"zone_id"`
	UploadedOn      time.Time                    `json:"uploaded_on"`
	ModifiedOn      time.Time                    `json:"modified_on"`
	ExpiresOn       time.Time                    `json:"expires_on"`
	Priority        int                          `json:"priority"`
	KeylessServer   KeylessSSL                   `json:"keyless_server"`
}

// ZoneCustomSSLGeoRestrictions represents the parameter to create or update
// geographic restrictions on a custom ssl certificate.
type ZoneCustomSSLGeoRestrictions struct {
	Label string `json:"label"`
}

// zoneCustomSSLResponse represents the response from the zone SSL details endpoint.
type zoneCustomSSLResponse struct {
	Response
	Result ZoneCustomSSL `json:"result"`
}

// zoneCustomSSLsResponse represents the response from the zone SSL list endpoint.
type zoneCustomSSLsResponse struct {
	Response
	Result []ZoneCustomSSL `json:"result"`
}

// ZoneCustomSSLOptions represents the parameters to create or update an existing
// custom SSL configuration.
type ZoneCustomSSLOptions struct {
	Certificate     string                       `json:"certificate"`
	PrivateKey      string                       `json:"private_key"`
	BundleMethod    string                       `json:"bundle_method,omitempty"`
	GeoRestrictions ZoneCustomSSLGeoRestrictions `json:"geo_restrictions,omitempty"`
	Type            string                       `json:"type,omitempty"`
}

// ZoneCustomSSLPriority represents a certificate's ID and priority. It is a
// subset of ZoneCustomSSL used for patch requests.
type ZoneCustomSSLPriority struct {
	ID       string `json:"ID"`
	Priority int    `json:"priority"`
}

// CreateSSL allows you to add a custom SSL certificate to the given zone.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-create-ssl-configuration
func (api *API) CreateSSL(zoneID string, options ZoneCustomSSLOptions) (ZoneCustomSSL, error) {
	uri := "/zones/" + zoneID + "/custom_certificates"
	res, err := api.makeRequest("POST", uri, options)
	if err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError)
	}
	var r zoneCustomSSLResponse
	if err := json.Unmarshal(res, &r); err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// ListSSL lists the custom certificates for the given zone.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-list-ssl-configurations
func (api *API) ListSSL(zoneID string) ([]ZoneCustomSSL, error) {
	uri := "/zones/" + zoneID + "/custom_certificates"
	res, err := api.makeRequest("GET", uri, nil)
	if err != nil {
		return nil, errors.Wrap(err, errMakeRequestError)
	}
	var r zoneCustomSSLsResponse
	if err := json.Unmarshal(res, &r); err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// SSLDetails returns the configuration details for a custom SSL certificate.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-ssl-configuration-details
func (api *API) SSLDetails(zoneID, certificateID string) (ZoneCustomSSL, error) {
	uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID
	res, err := api.makeRequest("GET", uri, nil)
	if err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError)
	}
	var r zoneCustomSSLResponse
	if err := json.Unmarshal(res, &r); err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// UpdateSSL updates (replaces) a custom SSL certificate.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-update-ssl-configuration
func (api *API) UpdateSSL(zoneID, certificateID string, options ZoneCustomSSLOptions) (ZoneCustomSSL, error) {
	uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID
	res, err := api.makeRequest("PATCH", uri, options)
	if err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError)
	}
	var r zoneCustomSSLResponse
	if err := json.Unmarshal(res, &r); err != nil {
		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// ReprioritizeSSL allows you to change the priority (which is served for a given
// request) of custom SSL certificates associated with the given zone.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-re-prioritize-ssl-certificates
func (api *API) ReprioritizeSSL(zoneID string, p []ZoneCustomSSLPriority) ([]ZoneCustomSSL, error) {
	uri := "/zones/" + zoneID + "/custom_certificates/prioritize"
	params := struct {
		Certificates []ZoneCustomSSLPriority `json:"certificates"`
	}{
		Certificates: p,
	}
	res, err := api.makeRequest("PUT", uri, params)
	if err != nil {
		return nil, errors.Wrap(err, errMakeRequestError)
	}
	var r zoneCustomSSLsResponse
	if err := json.Unmarshal(res, &r); err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// DeleteSSL deletes a custom SSL certificate from the given zone.
//
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-delete-an-ssl-certificate
func (api *API) DeleteSSL(zoneID, certificateID string) error {
	uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID
	if _, err := api.makeRequest("DELETE", uri, nil); err != nil {
		return errors.Wrap(err, errMakeRequestError)
	}
	return nil
}
<|endoftext|>


<commit_before>package gotomic

import (
	"fmt"
	"sort"
	"sync/atomic"
	"unsafe"
)

const (
	UNDECIDED = iota
	READ_CHECK
	SUCCESSFUL
	FAILED
)

var nextCommit uint64 = 0

/*
 Clonable types can be handled by the transaction layer.
*/
type Clonable interface {
	Clone() Clonable
}

/*
 Handle wraps any type of data that is supposed to be handled by the transaction layer.
*/
type Handle struct {
	/*
	 Will point to a version.
	*/
	unsafe.Pointer
}

/*
 NewHandle will wrap a Clonable value to enable its use in the transaction layer.
*/
func NewHandle(c Clonable) *Handle {
	return &Handle{unsafe.Pointer(&version{0, nil, c})}
}
func (self *Handle) getVersion() *version {
	return (*version)(atomic.LoadPointer(&self.Pointer))
}
func (self *Handle) replace(old, neu *version) bool {
	return atomic.CompareAndSwapPointer(&self.Pointer, unsafe.Pointer(old), unsafe.Pointer(neu))
}

type handles []*Handle

func (self handles) Len() int {
	return len(self)
}
func (self handles) Swap(i, j int) {
	self[i], self[j] = self[j], self[i]
}
func (self handles) Less(i, j int) bool {
	return uintptr(unsafe.Pointer(self[i])) < uintptr(unsafe.Pointer(self[j]))
}

type version struct {
	/*
	 The number of the transaction that created this version.
	*/
	commitNumber uint64
	/*
	 The transaction (or nil) having locked this version.
	*/
	lockedBy *Transaction
	/*
		The content in this version.
	*/
	content Clonable
}

func (self *version) clone() *version {
	newVersion := *self
	newVersion.content = self.content.Clone()
	return &newVersion
}

type snapshot struct {
	old *version
	neu *version
}

/*
 Transaction is based on "Concurrent Programming Without Locks" by Keir Fraser and Tim Harris <http://www.cl.cam.ac.uk/research/srg/netos/papers/2007-cpwl.pdf>

 It has a few tweaks that I don't believe break it (but I haven't even tried proving it):

 1) It has an ever increasing counter for the last transaction to commit.

 It uses this counter to fail fast when trying to read a value that another transaction has changed since it began.

 2) It copies the data not only on write opening, but also on read opening.

 These changes will make the transactions act more along the lines of "Sandboxing Transactional Memory" by Luke Dalessandro and Michael L. Scott <http://www.cs.rochester.edu/u/scott/papers/2012_TRANSACT_sandboxing.pdf> and will hopefully avoid the need to kill transactions exhibiting invalid behaviour due to inconsistent states.
*/
type Transaction struct {
	/*
	 Steadily incrementing number for each committed transaction.
	*/
	commitNumber uint64
	status       int32
	readHandles  map[*Handle]*snapshot
	writeHandles map[*Handle]*snapshot
}

func NewTransaction() *Transaction {
	return &Transaction{
		atomic.LoadUint64(&nextCommit),
		UNDECIDED,
		make(map[*Handle]*snapshot),
		make(map[*Handle]*snapshot),
	}
}
func (self *Transaction) getStatus() int32 {
	return atomic.LoadInt32(&self.status)
}
func (self *Transaction) objRead(h *Handle) (rval *version, err error) {
	version := h.getVersion()
	if version.commitNumber > self.commitNumber {
		return nil, fmt.Errorf("%v has changed", h.getVersion().content)
	}
	if version.lockedBy == nil {
		return version, nil
	}
	other := version.lockedBy
	if other.getStatus() == READ_CHECK {
		if self.getStatus() != READ_CHECK || self.commitNumber > other.commitNumber {
			other.Commit()
		} else {
			other.Abort()
		}
	}
	if other.getStatus() == SUCCESSFUL {
		if other.commitNumber > self.commitNumber {
			return nil, fmt.Errorf("%v has changed", other.writeHandles[h].neu.content)
		}
		return other.writeHandles[h].neu, nil
	}
	return version, nil
}
func (self *Transaction) sortedWrites() []*Handle {
	var rval handles
	for handle, _ := range self.writeHandles {
		rval = append(rval, handle)
	}
	sort.Sort(rval)
	return rval
}
func (self *Transaction) release() {
	stat := self.getStatus()
	if stat == SUCCESSFUL {
		self.commitNumber = atomic.AddUint64(&nextCommit, 1)
	}
	for _, handle := range self.sortedWrites() {
		current := handle.getVersion()
		if current.lockedBy == self {
			snapshot := self.writeHandles[handle]
			wanted := snapshot.old
			if stat == SUCCESSFUL {
				wanted = snapshot.neu
				wanted.commitNumber = self.commitNumber
			}
			handle.replace(current, wanted)
		}
	}
}
func (self *Transaction) acquire() bool {
	for _, handle := range self.sortedWrites() {
		for {
			snapshot, _ := self.writeHandles[handle]
			lockedVersion := snapshot.old.clone()
			lockedVersion.lockedBy = self
			if handle.replace(snapshot.old, lockedVersion) {
				break
			}
			current := handle.getVersion()
			if current.lockedBy == nil {
				return false
			}
			if current.lockedBy == self {
				break
			}
			current.lockedBy.Commit()
		}
	}
	return true
}
func (self *Transaction) readCheck() bool {
	for handle, snapshot := range self.readHandles {
		if handle.getVersion() != snapshot.old {
			return false
		}
	}
	return true
}

/*
 Commit the transaction. Will return whether the commit was successful or not.
*/
func (self *Transaction) Commit() bool {
	if !self.acquire() {
		self.Abort()
		return false
	}
	atomic.CompareAndSwapInt32(&self.status, UNDECIDED, READ_CHECK)
	if !self.readCheck() {
		self.Abort()
		return false
	}
	atomic.CompareAndSwapInt32(&self.status, READ_CHECK, SUCCESSFUL)
	self.release()
	return self.getStatus() == SUCCESSFUL
}

/*
 Abort the transaction.

 Unless the transaction is half-committed Abort isn't really necessary.
*/
func (self *Transaction) Abort() {
	for {
		current := self.getStatus()
		if current == FAILED {
			return
		}
		atomic.CompareAndSwapInt32(&self.status, current, FAILED)
	}
	self.release()
}

/*
 Read will return a version of the data in h that is guaranteed to not have been changed since this Transaction started.

 Any changes made to the return value will *not* be saved when the Transaction commits.

 If another Transaction changes the data in h before this Transaction commits the commit will fail.
*/
func (self *Transaction) Read(h *Handle) (rval Clonable, err error) {
	if self.getStatus() != UNDECIDED {
		return nil, fmt.Errorf("%v is not UNDECIDED", self)
	}
	if snapshot, ok := self.readHandles[h]; ok {
		return snapshot.neu.content, nil
	}
	if snapshot, ok := self.writeHandles[h]; ok {
		return snapshot.neu.content, nil
	}
	oldVersion, err := self.objRead(h)
	if err != nil {
		return nil, err
	}
	newVersion := oldVersion.clone()
	self.readHandles[h] = &snapshot{oldVersion, newVersion}
	return newVersion.content, nil
}

/*
 Write will return a version of the data in h that is guaranteed to not have been changed since this Transaction started.

 All changes made to the return value *will* be saved when the Transaction commits.

 If another Transaction changes the data in h before this Transaction commits the commit will fail.
*/
func (self *Transaction) Write(h *Handle) (rval Clonable, err error) {
	if self.getStatus() != UNDECIDED {
		return nil, fmt.Errorf("%v is not UNDECIDED", self)
	}
	if snapshot, ok := self.writeHandles[h]; ok {
		return snapshot.neu.content, nil
	}
	if snapshot, ok := self.readHandles[h]; ok {
		delete(self.readHandles, h)
		self.writeHandles[h] = snapshot
		return snapshot.neu.content, nil
	}
	oldVersion, err := self.objRead(h)
	if err != nil {
		return nil, err
	}
	newVersion := oldVersion.clone()
	self.writeHandles[h] = &snapshot{oldVersion, newVersion}
	return newVersion.content, nil
}
<commit_msg>comments<commit_after>package gotomic

import (
	"fmt"
	"sort"
	"sync/atomic"
	"unsafe"
)

const (
	UNDECIDED = iota
	READ_CHECK
	SUCCESSFUL
	FAILED
)

var nextCommit uint64 = 0

/*
 Clonable types can be handled by the transaction layer.
*/
type Clonable interface {
	Clone() Clonable
}

/*
 Handle wraps any type of data that is supposed to be handled by the transaction layer.
*/
type Handle struct {
	/*
	 Will point to a version.
	*/
	unsafe.Pointer
}

/*
 NewHandle will wrap a Clonable value to enable its use in the transaction layer.
*/
func NewHandle(c Clonable) *Handle {
	return &Handle{unsafe.Pointer(&version{0, nil, c})}
}
func (self *Handle) getVersion() *version {
	return (*version)(atomic.LoadPointer(&self.Pointer))
}
func (self *Handle) replace(old, neu *version) bool {
	return atomic.CompareAndSwapPointer(&self.Pointer, unsafe.Pointer(old), unsafe.Pointer(neu))
}

type handles []*Handle

func (self handles) Len() int {
	return len(self)
}
func (self handles) Swap(i, j int) {
	self[i], self[j] = self[j], self[i]
}
func (self handles) Less(i, j int) bool {
	return uintptr(unsafe.Pointer(self[i])) < uintptr(unsafe.Pointer(self[j]))
}

type version struct {
	/*
	 The number of the transaction that created this version.
	*/
	commitNumber uint64
	/*
	 The transaction (or nil) having locked this version.
	*/
	lockedBy *Transaction
	/*
		The content in this version.
	*/
	content Clonable
}

func (self *version) clone() *version {
	newVersion := *self
	newVersion.content = self.content.Clone()
	return &newVersion
}

type snapshot struct {
	old *version
	neu *version
}

/*
 Transaction is based on "Concurrent Programming Without Locks" by Keir Fraser and Tim Harris <http://www.cl.cam.ac.uk/research/srg/netos/papers/2007-cpwl.pdf>

 It has a few tweaks that I don't believe break it (but I haven't even tried proving it):

 1) It has an ever increasing counter for the last transaction to commit.

 It uses this counter to fail transactions fast when they try to read a value that another
 transaction has changed since the first transaction began.

 2) It copies the data not only on write opening, but also on read opening.

 These changes will make the transactions act more along the lines of "Sandboxing Transactional Memory" by Luke Dalessandro and Michael L. Scott <http://www.cs.rochester.edu/u/scott/papers/2012_TRANSACT_sandboxing.pdf> and will hopefully avoid the need to kill transactions exhibiting invalid behaviour due to inconsistent states.
*/
type Transaction struct {
	/*
	 Steadily incrementing number for each committed transaction.
	*/
	commitNumber uint64
	status       int32
	readHandles  map[*Handle]*snapshot
	writeHandles map[*Handle]*snapshot
}

func NewTransaction() *Transaction {
	return &Transaction{
		atomic.LoadUint64(&nextCommit),
		UNDECIDED,
		make(map[*Handle]*snapshot),
		make(map[*Handle]*snapshot),
	}
}
func (self *Transaction) getStatus() int32 {
	return atomic.LoadInt32(&self.status)
}
func (self *Transaction) objRead(h *Handle) (rval *version, err error) {
	version := h.getVersion()
	if version.commitNumber > self.commitNumber {
		return nil, fmt.Errorf("%v has changed", h.getVersion().content)
	}
	if version.lockedBy == nil {
		return version, nil
	}
	other := version.lockedBy
	if other.getStatus() == READ_CHECK {
		if self.getStatus() != READ_CHECK || self.commitNumber > other.commitNumber {
			other.Commit()
		} else {
			other.Abort()
		}
	}
	if other.getStatus() == SUCCESSFUL {
		if other.commitNumber > self.commitNumber {
			return nil, fmt.Errorf("%v has changed", other.writeHandles[h].neu.content)
		}
		return other.writeHandles[h].neu, nil
	}
	return version, nil
}
func (self *Transaction) sortedWrites() []*Handle {
	var rval handles
	for handle, _ := range self.writeHandles {
		rval = append(rval, handle)
	}
	sort.Sort(rval)
	return rval
}
func (self *Transaction) release() {
	stat := self.getStatus()
	if stat == SUCCESSFUL {
		self.commitNumber = atomic.AddUint64(&nextCommit, 1)
	}
	for _, handle := range self.sortedWrites() {
		current := handle.getVersion()
		if current.lockedBy == self {
			snapshot := self.writeHandles[handle]
			wanted := snapshot.old
			if stat == SUCCESSFUL {
				wanted = snapshot.neu
				wanted.commitNumber = self.commitNumber
			}
			handle.replace(current, wanted)
		}
	}
}
func (self *Transaction) acquire() bool {
	for _, handle := range self.sortedWrites() {
		for {
			snapshot, _ := self.writeHandles[handle]
			lockedVersion := snapshot.old.clone()
			lockedVersion.lockedBy = self
			if handle.replace(snapshot.old, lockedVersion) {
				break
			}
			current := handle.getVersion()
			if current.lockedBy == nil {
				return false
			}
			if current.lockedBy == self {
				break
			}
			current.lockedBy.Commit()
		}
	}
	return true
}
func (self *Transaction) readCheck() bool {
	for handle, snapshot := range self.readHandles {
		if handle.getVersion() != snapshot.old {
			return false
		}
	}
	return true
}

/*
 Commit the transaction. Will return whether the commit was successful or not.
*/
func (self *Transaction) Commit() bool {
	if !self.acquire() {
		self.Abort()
		return false
	}
	atomic.CompareAndSwapInt32(&self.status, UNDECIDED, READ_CHECK)
	if !self.readCheck() {
		self.Abort()
		return false
	}
	atomic.CompareAndSwapInt32(&self.status, READ_CHECK, SUCCESSFUL)
	self.release()
	return self.getStatus() == SUCCESSFUL
}

/*
 Abort the transaction.

 Unless the transaction is half-committed Abort isn't really necessary.
*/
func (self *Transaction) Abort() {
	for {
		current := self.getStatus()
		if current == FAILED {
			return
		}
		atomic.CompareAndSwapInt32(&self.status, current, FAILED)
	}
	self.release()
}

/*
 Read will return a version of the data in h that is guaranteed to not have been changed since this Transaction started.

 Any changes made to the return value will *not* be saved when the Transaction commits.

 If another Transaction changes the data in h before this Transaction commits the commit will fail.
*/
func (self *Transaction) Read(h *Handle) (rval Clonable, err error) {
	if self.getStatus() != UNDECIDED {
		return nil, fmt.Errorf("%v is not UNDECIDED", self)
	}
	if snapshot, ok := self.readHandles[h]; ok {
		return snapshot.neu.content, nil
	}
	if snapshot, ok := self.writeHandles[h]; ok {
		return snapshot.neu.content, nil
	}
	oldVersion, err := self.objRead(h)
	if err != nil {
		return nil, err
	}
	newVersion := oldVersion.clone()
	self.readHandles[h] = &snapshot{oldVersion, newVersion}
	return newVersion.content, nil
}

/*
 Write will return a version of the data in h that is guaranteed to not have been changed since this Transaction started.

 All changes made to the return value *will* be saved when the Transaction commits.

 If another Transaction changes the data in h before this Transaction commits the commit will fail.
*/
func (self *Transaction) Write(h *Handle) (rval Clonable, err error) {
	if self.getStatus() != UNDECIDED {
		return nil, fmt.Errorf("%v is not UNDECIDED", self)
	}
	if snapshot, ok := self.writeHandles[h]; ok {
		return snapshot.neu.content, nil
	}
	if snapshot, ok := self.readHandles[h]; ok {
		delete(self.readHandles, h)
		self.writeHandles[h] = snapshot
		return snapshot.neu.content, nil
	}
	oldVersion, err := self.objRead(h)
	if err != nil {
		return nil, err
	}
	newVersion := oldVersion.clone()
	self.writeHandles[h] = &snapshot{oldVersion, newVersion}
	return newVersion.content, nil
}
<|endoftext|>


<commit_before>package caddyplug

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/mholt/caddy"
)

const noPlugin = "no plugins found, use caddyplug to add plugins"

func init() {
	caddy.RegisterEventHook("pluginloader", hook)
}

func pluginsDir() string {
	return filepath.Join(os.Getenv("HOME"), "lib", "caddy")
}

func hook(event caddy.EventName, info interface{}) error {
	switch event {
	case caddy.StartupEvent:
		if runtime.GOOS != "linux" {
			log.Println("pluginloader is only supported on Linux")
			return nil
		}
		if stat, err := os.Stat(pluginsDir()); err != nil || !stat.IsDir() {
			fmt.Println(noPlugin)
			return nil
		}
		count := 0
		if httpPlugins := listPlugins("http"); len(httpPlugins) > 0 {
			fmt.Println("http plugins loaded:", strings.Join(httpPlugins, ", "))
			count += len(httpPlugins)
		}
		if dnsPlugins := listPlugins("dns"); len(dnsPlugins) > 0 {
			fmt.Println("dns plugins loaded:", strings.Join(dnsPlugins, ", "))
			count += len(dnsPlugins)
		}
		if count == 0 {
			fmt.Println(noPlugin)
		}
	}
	return nil
}

func listPlugins(pluginType string) []string {
	var plugins []string
	dir, err := os.Open(filepath.Join(pluginsDir(), pluginType))
	defer dir.Close()

	if err != nil {
		return plugins
	}
	names, err := dir.Readdirnames(-1)
	if err != nil {
		return plugins
	}
	for _, name := range names {
		if !strings.HasSuffix(name, ".so") {
			continue
		}
		plugins = append(plugins, strings.TrimSuffix(name, ".so"))
	}
	return plugins
}
<commit_msg>fix unseen error<commit_after>package caddyplug

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/mholt/caddy"
)

const noPlugin = "no plugins found, use caddyplug to add plugins"

func init() {
	caddy.RegisterEventHook("pluginloader", hook)
}

func pluginsDir() string {
	return filepath.Join(os.Getenv("HOME"), "lib", "caddy")
}

var hook caddy.EventHook = func(event caddy.EventName, info interface{}) error {
	switch event {
	case caddy.StartupEvent:
		if runtime.GOOS != "linux" {
			log.Println("pluginloader is only supported on Linux")
			return nil
		}
		if stat, err := os.Stat(pluginsDir()); err != nil || !stat.IsDir() {
			fmt.Println(noPlugin)
			return nil
		}
		count := 0
		if httpPlugins := listPlugins("http"); len(httpPlugins) > 0 {
			fmt.Println("http plugins loaded:", strings.Join(httpPlugins, ", "))
			count += len(httpPlugins)
		}
		if dnsPlugins := listPlugins("dns"); len(dnsPlugins) > 0 {
			fmt.Println("dns plugins loaded:", strings.Join(dnsPlugins, ", "))
			count += len(dnsPlugins)
		}
		if count == 0 {
			fmt.Println(noPlugin)
		}
	}
	return nil
}

func listPlugins(pluginType string) []string {
	var plugins []string
	dir, err := os.Open(filepath.Join(pluginsDir(), pluginType))
	defer dir.Close()

	if err != nil {
		return plugins
	}
	names, err := dir.Readdirnames(-1)
	if err != nil {
		return plugins
	}
	for _, name := range names {
		if !strings.HasSuffix(name, ".so") {
			continue
		}
		plugins = append(plugins, strings.TrimSuffix(name, ".so"))
	}
	return plugins
}
<|endoftext|>


<commit_before>package linode

import (
	"context"
	"fmt"
	"strconv"

	"github.com/chiefy/linodego"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceLinodeDomain() *schema.Resource {
	return &schema.Resource{
		Create: resourceLinodeDomainCreate,
		Read:   resourceLinodeDomainRead,
		Update: resourceLinodeDomainUpdate,
		Delete: resourceLinodeDomainDelete,
		Exists: resourceLinodeDomainExists,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Schema: map[string]*schema.Schema{
			"domain": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The domain this Domain represents. These must be unique in our system; you cannot have two Domains representing the same domain.",
				Required:    true,
			},
			"domain_type": &schema.Schema{
				Type:        schema.TypeString,
				Description: "If this Domain represents the authoritative source of information for the domain it describes, or if it is a read-only copy of a master (also called a slave).",
				Default:     "master",
				Optional:    true,
			},
			"group": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The group this Domain belongs to. This is for display purposes only.",
				Optional:    true,
			},
			"status": &schema.Schema{
				Type:        schema.TypeString,
				Description: "Used to control whether this Domain is currently being rendered.",
				Optional:    true,
				Default:     "active",
			},
			"description": &schema.Schema{
				Type:        schema.TypeString,
				Description: "A description for this Domain. This is for display purposes only.",
				Optional:    true,
			},
			"master_ips": &schema.Schema{
				Type: schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Description: "The IP addresses representing the master DNS for this Domain.",
				Optional:    true,
			},
			"axfr_ips": &schema.Schema{
				Type: schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Description: "The list of IPs that may perform a zone transfer for this Domain. This is potentially dangerous, and should be set to an empty list unless you intend to use it.",
				Optional:    true,
			},
			"ttl_sec": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "'Time to Live' - the amount of time in seconds that this Domain's records may be cached by resolvers or other domain servers. Valid values are 300, 3600, 7200, 14400, 28800, 57600, 86400, 172800, 345600, 604800, 1209600, and 2419200 - any other value will be rounded to the nearest valid value.",
				Optional:    true,
			},
			"retry_sec": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "The interval, in seconds, at which a failed refresh should be retried. Valid values are 300, 3600, 7200, 14400, 28800, 57600, 86400, 172800, 345600, 604800, 1209600, and 2419200 - any other value will be rounded to the nearest valid value.",
				Optional:    true,
			},
			"expire_sec": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "The amount of time in seconds that may pass before this Domain is no longer authoritative. Valid values are 300, 3600, 7200, 14400, 28800, 57600, 86400, 172800, 345600, 604800, 1209600, and 2419200 - any other value will be rounded to the nearest valid value.",
				Optional:    true,
			},
			"refresh_sec": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "The amount of time in seconds before this Domain should be refreshed. Valid values are 300, 3600, 7200, 14400, 28800, 57600, 86400, 172800, 345600, 604800, 1209600, and 2419200 - any other value will be rounded to the nearest valid value.",
				Optional:    true,
			},
			"soa_email": &schema.Schema{
				Type:        schema.TypeString,
				Description: "Start of Authority email address. This is required for master Domains.",
				Optional:    true,
			},
		},
	}
}

func syncResourceData(d *schema.ResourceData, domain *linodego.Domain) {
	d.Set("domain", domain.Domain)
	d.Set("domain_type", domain.Type)
	d.Set("group", domain.Group)
	d.Set("status", domain.Status)
	d.Set("description", domain.Description)
	d.Set("master_ips", domain.MasterIPs)
	d.Set("afxr_ips", domain.AXfrIPs)
	d.Set("ttl_sec", domain.TTLSec)
	d.Set("retry_sec", domain.RetrySec)
	d.Set("expire_sec", domain.ExpireSec)
	d.Set("refresh_sec", domain.RefreshSec)
	d.Set("soa_email", domain.SOAEmail)
}

func resourceLinodeDomainExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return false, fmt.Errorf("Failed to parse Linode Domain ID %s as int because %s", d.Id(), err)
	}

	_, err = client.GetDomain(context.Background(), int(id))
	if err != nil {
		if lerr, ok := err.(*linodego.Error); ok && lerr.Code == 404 {
			d.SetId("")
			return false, nil
		}

		return false, fmt.Errorf("Failed to get Linode Domain ID %s because %s", d.Id(), err)
	}
	return true, nil
}

func resourceLinodeDomainRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode Domain ID %s as int because %s", d.Id(), err)
	}

	domain, err := client.GetDomain(context.Background(), int(id))

	if err != nil {
		return fmt.Errorf("Failed to find the specified Linode Domain because %s", err)
	}

	syncResourceData(d, domain)

	return nil
}

func resourceLinodeDomainCreate(d *schema.ResourceData, meta interface{}) error {
	client, ok := meta.(linodego.Client)
	if !ok {
		return fmt.Errorf("Invalid Client when creating Linode Domain")
	}

	createOpts := linodego.DomainCreateOptions{
		Domain:      d.Get("domain").(string),
		Type:        linodego.DomainType(d.Get("domain_type").(string)),
		Group:       d.Get("group").(string),
		Description: d.Get("description").(string),
		SOAEmail:    d.Get("soa_email").(string),
		RetrySec:    d.Get("retry_sec").(int),
		ExpireSec:   d.Get("expire_sec").(int),
		RefreshSec:  d.Get("refresh_sec").(int),
		TTLSec:      d.Get("ttl_sec").(int),
	}

	if v, ok := d.GetOk("master_ips"); ok {
		var masterIPS []string
		for _, ip := range v.([]interface{}) {
			masterIPS = append(masterIPS, ip.(string))
		}

		createOpts.MasterIPs = masterIPS
	}

	if v, ok := d.GetOk("axfr_ips"); ok {
		var AXfrIPs []string
		for _, ip := range v.([]interface{}) {
			AXfrIPs = append(AXfrIPs, ip.(string))
		}

		createOpts.AXfrIPs = AXfrIPs
	}

	domain, err := client.CreateDomain(context.Background(), &createOpts)
	if err != nil {
		return fmt.Errorf("Failed to create a Linode Domain because %s", err)
	}
	d.SetId(fmt.Sprintf("%d", domain.ID))
	syncResourceData(d, domain)

	return resourceLinodeDomainRead(d, meta)
}

func resourceLinodeDomainUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)

	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode Domain id %s as an int because %s", d.Id(), err)
	}

	updateOpts := linodego.DomainUpdateOptions{
		Domain:      d.Get("domain").(string),
		Status:      linodego.DomainStatus(d.Get("status").(string)),
		Type:        linodego.DomainType(d.Get("domain_type").(string)),
		Group:       d.Get("group").(string),
		Description: d.Get("description").(string),
		SOAEmail:    d.Get("soa_email").(string),
		RetrySec:    d.Get("retry_sec").(int),
		ExpireSec:   d.Get("expire_sec").(int),
		RefreshSec:  d.Get("refresh_sec").(int),
		TTLSec:      d.Get("ttl_sec").(int),
	}

	if v, ok := d.GetOk("master_ips"); ok {
		var masterIPS []string
		for _, ip := range v.([]interface{}) {
			masterIPS = append(masterIPS, ip.(string))
		}

		updateOpts.MasterIPs = masterIPS
	}

	if v, ok := d.GetOk("axfr_ips"); ok {
		var AXfrIPs []string
		for _, ip := range v.([]interface{}) {
			AXfrIPs = append(AXfrIPs, ip.(string))
		}

		updateOpts.AXfrIPs = AXfrIPs
	}

	domain, err := client.UpdateDomain(context.Background(), int(id), updateOpts)
	syncResourceData(d, domain)

	return nil
}

func resourceLinodeDomainDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode Domain id %s as int", d.Id())
	}
	err = client.DeleteDomain(context.Background(), int(id))
	if err != nil {
		return fmt.Errorf("Failed to delete Linode Domain %d because %s", id, err)
	}
	d.SetId("")

	return nil
}
<commit_msg>fix: catch errors from UpdateDomain<commit_after>package linode

import (
	"context"
	"fmt"
	"strconv"

	"github.com/chiefy/linodego"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceLinodeDomain() *schema.Resource {
	return &schema.Resource{
		Create: resourceLinodeDomainCreate,
		Read:   resourceLinodeDomainRead,
		Update: resourceLinodeDomainUpdate,
		Delete: resourceLinodeDomainDelete,
		Exists: resourceLinodeDomainExists,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Schema: map[string]*schema.Schema{
			"domain": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The domain this Domain represents. These must be unique in our system; you cannot have two Domains representing the same domain.",
				Required:    true,
			},
			"domain_type": &schema.Schema{
				Type:        schema.TypeString,
				Description: "If this Domain represents the authoritative source of information for the domain it describes, or if it is a read-only copy of a master (also called a slave).",
				Default:     "master",
				Optional:    true,
			},
			"group": &schema.Schema{
				Type:        schema.TypeString,
				Description: "The group this Domain belongs to. This is for display purposes only.",
				Optional:    true,
			},
			"status": &schema.Schema{
				Type:        schema.TypeString,
				Description: "Used to control whether this Domain is currently being rendered.",
				Optional:    true,
				Default:     "active",
			},
			"description": &schema.Schema{
				Type:        schema.TypeString,
				Description: "A description for this Domain. This is for display purposes only.",
				Optional:    true,
			},
			"master_ips": &schema.Schema{
				Type: schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Description: "The IP addresses representing the master DNS for this Domain.",
				Optional:    true,
			},
			"axfr_ips": &schema.Schema{
				Type: schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Description: "The list of IPs that may perform a zone transfer for this Domain. This is potentially dangerous, and should be set to an empty list unless you intend to use it.",
				Optional:    true,
			},
			"ttl_sec": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "'Time to Live' - the amount of time in seconds that this Domain's records may be cached by resolvers or other domain servers. Valid values are 300, 3600, 7200, 14400, 28800, 57600, 86400, 172800, 345600, 604800, 1209600, and 2419200 - any other value will be rounded to the nearest valid value.",
				Optional:    true,
			},
			"retry_sec": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "The interval, in seconds, at which a failed refresh should be retried. Valid values are 300, 3600, 7200, 14400, 28800, 57600, 86400, 172800, 345600, 604800, 1209600, and 2419200 - any other value will be rounded to the nearest valid value.",
				Optional:    true,
			},
			"expire_sec": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "The amount of time in seconds that may pass before this Domain is no longer authoritative. Valid values are 300, 3600, 7200, 14400, 28800, 57600, 86400, 172800, 345600, 604800, 1209600, and 2419200 - any other value will be rounded to the nearest valid value.",
				Optional:    true,
			},
			"refresh_sec": &schema.Schema{
				Type:        schema.TypeInt,
				Description: "The amount of time in seconds before this Domain should be refreshed. Valid values are 300, 3600, 7200, 14400, 28800, 57600, 86400, 172800, 345600, 604800, 1209600, and 2419200 - any other value will be rounded to the nearest valid value.",
				Optional:    true,
			},
			"soa_email": &schema.Schema{
				Type:        schema.TypeString,
				Description: "Start of Authority email address. This is required for master Domains.",
				Optional:    true,
			},
		},
	}
}

func syncResourceData(d *schema.ResourceData, domain *linodego.Domain) {
	d.Set("domain", domain.Domain)
	d.Set("domain_type", domain.Type)
	d.Set("group", domain.Group)
	d.Set("status", domain.Status)
	d.Set("description", domain.Description)
	d.Set("master_ips", domain.MasterIPs)
	d.Set("afxr_ips", domain.AXfrIPs)
	d.Set("ttl_sec", domain.TTLSec)
	d.Set("retry_sec", domain.RetrySec)
	d.Set("expire_sec", domain.ExpireSec)
	d.Set("refresh_sec", domain.RefreshSec)
	d.Set("soa_email", domain.SOAEmail)
}

func resourceLinodeDomainExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return false, fmt.Errorf("Failed to parse Linode Domain ID %s as int because %s", d.Id(), err)
	}

	_, err = client.GetDomain(context.Background(), int(id))
	if err != nil {
		if lerr, ok := err.(*linodego.Error); ok && lerr.Code == 404 {
			d.SetId("")
			return false, nil
		}

		return false, fmt.Errorf("Failed to get Linode Domain ID %s because %s", d.Id(), err)
	}
	return true, nil
}

func resourceLinodeDomainRead(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode Domain ID %s as int because %s", d.Id(), err)
	}

	domain, err := client.GetDomain(context.Background(), int(id))

	if err != nil {
		return fmt.Errorf("Failed to find the specified Linode Domain because %s", err)
	}

	syncResourceData(d, domain)

	return nil
}

func resourceLinodeDomainCreate(d *schema.ResourceData, meta interface{}) error {
	client, ok := meta.(linodego.Client)
	if !ok {
		return fmt.Errorf("Invalid Client when creating Linode Domain")
	}

	createOpts := linodego.DomainCreateOptions{
		Domain:      d.Get("domain").(string),
		Type:        linodego.DomainType(d.Get("domain_type").(string)),
		Group:       d.Get("group").(string),
		Description: d.Get("description").(string),
		SOAEmail:    d.Get("soa_email").(string),
		RetrySec:    d.Get("retry_sec").(int),
		ExpireSec:   d.Get("expire_sec").(int),
		RefreshSec:  d.Get("refresh_sec").(int),
		TTLSec:      d.Get("ttl_sec").(int),
	}

	if v, ok := d.GetOk("master_ips"); ok {
		var masterIPS []string
		for _, ip := range v.([]interface{}) {
			masterIPS = append(masterIPS, ip.(string))
		}

		createOpts.MasterIPs = masterIPS
	}

	if v, ok := d.GetOk("axfr_ips"); ok {
		var AXfrIPs []string
		for _, ip := range v.([]interface{}) {
			AXfrIPs = append(AXfrIPs, ip.(string))
		}

		createOpts.AXfrIPs = AXfrIPs
	}

	domain, err := client.CreateDomain(context.Background(), &createOpts)
	if err != nil {
		return fmt.Errorf("Failed to create a Linode Domain because %s", err)
	}
	d.SetId(fmt.Sprintf("%d", domain.ID))
	syncResourceData(d, domain)

	return resourceLinodeDomainRead(d, meta)
}

func resourceLinodeDomainUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)

	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode Domain id %s as an int because %s", d.Id(), err)
	}

	updateOpts := linodego.DomainUpdateOptions{
		Domain:      d.Get("domain").(string),
		Status:      linodego.DomainStatus(d.Get("status").(string)),
		Type:        linodego.DomainType(d.Get("domain_type").(string)),
		Group:       d.Get("group").(string),
		Description: d.Get("description").(string),
		SOAEmail:    d.Get("soa_email").(string),
		RetrySec:    d.Get("retry_sec").(int),
		ExpireSec:   d.Get("expire_sec").(int),
		RefreshSec:  d.Get("refresh_sec").(int),
		TTLSec:      d.Get("ttl_sec").(int),
	}

	if v, ok := d.GetOk("master_ips"); ok {
		var masterIPS []string
		for _, ip := range v.([]interface{}) {
			masterIPS = append(masterIPS, ip.(string))
		}

		updateOpts.MasterIPs = masterIPS
	}

	if v, ok := d.GetOk("axfr_ips"); ok {
		var AXfrIPs []string
		for _, ip := range v.([]interface{}) {
			AXfrIPs = append(AXfrIPs, ip.(string))
		}

		updateOpts.AXfrIPs = AXfrIPs
	}

	domain, err := client.UpdateDomain(context.Background(), int(id), updateOpts)
	if err != nil {
		return fmt.Errorf("Failed to update Linode Domain %d because %s", id, err)
	}
	syncResourceData(d, domain)

	return nil
}

func resourceLinodeDomainDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(linodego.Client)
	id, err := strconv.ParseInt(d.Id(), 10, 64)
	if err != nil {
		return fmt.Errorf("Failed to parse Linode Domain id %s as int", d.Id())
	}
	err = client.DeleteDomain(context.Background(), int(id))
	if err != nil {
		return fmt.Errorf("Failed to delete Linode Domain %d because %s", id, err)
	}
	d.SetId("")

	return nil
}
<|endoftext|>


<commit_before>package multihash

import (
	"crypto/sha1"
	"crypto/sha512"
	"errors"
	"fmt"

	keccak "github.com/gxed/hashland/keccakpg"
	blake2b "github.com/minio/blake2b-simd"
	sha256 "github.com/minio/sha256-simd"
	murmur3 "github.com/spaolacci/murmur3"
	blake2s "golang.org/x/crypto/blake2s"
	sha3 "golang.org/x/crypto/sha3"
)

// ErrSumNotSupported is returned when the Sum function code is not implemented
var ErrSumNotSupported = errors.New("Function not implemented. Complain to lib maintainer.")

// Sum obtains the cryptographic sum of a given buffer. The length parameter
// indicates the length of the resulting digest and passing a negative value
// use default length values for the selected hash function.
func Sum(data []byte, code uint64, length int) (Multihash, error) {
	m := Multihash{}
	err := error(nil)
	if !ValidCode(code) {
		return m, fmt.Errorf("invalid multihash code %d", code)
	}

	if length < 0 {
		var ok bool
		length, ok = DefaultLengths[code]
		if !ok {
			return m, fmt.Errorf("no default length for code %d", code)
		}
	}

	if code == ID && length != len(data) {
		return m, fmt.Errorf("the length of the identity hash (%d) must be equal to the length of the data (%d)",
			length, len(data))
	}

	var d []byte
	switch {
	case isBlake2s(code):
		olen := code - BLAKE2S_MIN + 1
		switch olen {
		case 32:
			out := blake2s.Sum256(data)
			d = out[:]
		default:
			return nil, fmt.Errorf("unsupported length for blake2s: %d", olen)
		}
	case isBlake2b(code):
		olen := uint8(code - BLAKE2B_MIN + 1)
		d = sumBlake2b(olen, data)
	default:
		switch code {
		case ID:
			d = sumID(data)
		case SHA1:
			d = sumSHA1(data)
		case SHA2_256:
			d = sumSHA256(data)
		case SHA2_512:
			d = sumSHA512(data)
		case KECCAK_224:
			d = sumKeccak224(data)
		case KECCAK_256:
			d = sumKeccak256(data)
		case KECCAK_384:
			d = sumKeccak384(data)
		case KECCAK_512:
			d = sumKeccak512(data)
		case SHA3_224:
			d = sumSHA3_224(data)
		case SHA3_256:
			d = sumSHA3_256(data)
		case SHA3_384:
			d = sumSHA3_384(data)
		case SHA3_512:
			d = sumSHA3_512(data)
		case DBL_SHA2_256:
			d = sumSHA256(sumSHA256(data))
		case MURMUR3:
			d, err = sumMURMUR3(data)
		case SHAKE_128:
			d = sumSHAKE128(data)
		case SHAKE_256:
			d = sumSHAKE256(data)
		default:
			return m, ErrSumNotSupported
		}
	}
	if err != nil {
		return m, err
	}
	if length >= 0 {
		d = d[:length]
	}
	return Encode(d, code)
}

func isBlake2s(code uint64) bool {
	return code >= BLAKE2S_MIN && code <= BLAKE2S_MAX
}
func isBlake2b(code uint64) bool {
	return code >= BLAKE2B_MIN && code <= BLAKE2B_MAX
}

func sumBlake2b(size uint8, data []byte) []byte {
	hasher, err := blake2b.New(&blake2b.Config{Size: size})
	if err != nil {
		panic(err)
	}

	if _, err := hasher.Write(data); err != nil {
		panic(err)
	}

	return hasher.Sum(nil)[:]
}

func sumID(data []byte) []byte {
	return data
}

func sumSHA1(data []byte) []byte {
	a := sha1.Sum(data)
	return a[0:20]
}

func sumSHA256(data []byte) []byte {
	a := sha256.Sum256(data)
	return a[0:32]
}

func sumSHA512(data []byte) []byte {
	a := sha512.Sum512(data)
	return a[0:64]
}

func sumKeccak224(data []byte) []byte {
	h := keccak.New224()
	h.Write(data)
	return h.Sum(nil)
}

func sumKeccak256(data []byte) []byte {
	h := keccak.New256()
	h.Write(data)
	return h.Sum(nil)
}

func sumKeccak384(data []byte) []byte {
	h := keccak.New384()
	h.Write(data)
	return h.Sum(nil)
}

func sumKeccak512(data []byte) []byte {
	h := keccak.New512()
	h.Write(data)
	return h.Sum(nil)
}

func sumSHA3(data []byte) ([]byte, error) {
	h := sha3.New512()
	if _, err := h.Write(data); err != nil {
		return nil, err
	}
	return h.Sum(nil), nil
}

func sumSHA3_512(data []byte) []byte {
	a := sha3.Sum512(data)
	return a[:]
}

func sumMURMUR3(data []byte) ([]byte, error) {
	number := murmur3.Sum32(data)
	bytes := make([]byte, 4)
	for i := range bytes {
		bytes[i] = byte(number & 0xff)
		number >>= 8
	}
	return bytes, nil
}

func sumSHAKE128(data []byte) []byte {
	bytes := make([]byte, 32)
	sha3.ShakeSum128(bytes, data)
	return bytes
}

func sumSHAKE256(data []byte) []byte {
	bytes := make([]byte, 64)
	sha3.ShakeSum256(bytes, data)
	return bytes
}

func sumSHA3_384(data []byte) []byte {
	a := sha3.Sum384(data)
	return a[:]
}

func sumSHA3_256(data []byte) []byte {
	a := sha3.Sum256(data)
	return a[:]
}

func sumSHA3_224(data []byte) []byte {
	a := sha3.Sum224(data)
	return a[:]
}
<commit_msg>allow the default length (-1) when specifying the length of an ID hash<commit_after>package multihash

import (
	"crypto/sha1"
	"crypto/sha512"
	"errors"
	"fmt"

	keccak "github.com/gxed/hashland/keccakpg"
	blake2b "github.com/minio/blake2b-simd"
	sha256 "github.com/minio/sha256-simd"
	murmur3 "github.com/spaolacci/murmur3"
	blake2s "golang.org/x/crypto/blake2s"
	sha3 "golang.org/x/crypto/sha3"
)

// ErrSumNotSupported is returned when the Sum function code is not implemented
var ErrSumNotSupported = errors.New("Function not implemented. Complain to lib maintainer.")

// Sum obtains the cryptographic sum of a given buffer. The length parameter
// indicates the length of the resulting digest and passing a negative value
// use default length values for the selected hash function.
func Sum(data []byte, code uint64, length int) (Multihash, error) {
	m := Multihash{}
	err := error(nil)
	if !ValidCode(code) {
		return m, fmt.Errorf("invalid multihash code %d", code)
	}

	if length < 0 {
		var ok bool
		length, ok = DefaultLengths[code]
		if !ok {
			return m, fmt.Errorf("no default length for code %d", code)
		}
	}

	if code == ID && length >= 0 && length != len(data) {
		return m, fmt.Errorf("the length of the identity hash (%d) must be equal to the length of the data (%d)",
			length, len(data))
	}

	var d []byte
	switch {
	case isBlake2s(code):
		olen := code - BLAKE2S_MIN + 1
		switch olen {
		case 32:
			out := blake2s.Sum256(data)
			d = out[:]
		default:
			return nil, fmt.Errorf("unsupported length for blake2s: %d", olen)
		}
	case isBlake2b(code):
		olen := uint8(code - BLAKE2B_MIN + 1)
		d = sumBlake2b(olen, data)
	default:
		switch code {
		case ID:
			d = sumID(data)
		case SHA1:
			d = sumSHA1(data)
		case SHA2_256:
			d = sumSHA256(data)
		case SHA2_512:
			d = sumSHA512(data)
		case KECCAK_224:
			d = sumKeccak224(data)
		case KECCAK_256:
			d = sumKeccak256(data)
		case KECCAK_384:
			d = sumKeccak384(data)
		case KECCAK_512:
			d = sumKeccak512(data)
		case SHA3_224:
			d = sumSHA3_224(data)
		case SHA3_256:
			d = sumSHA3_256(data)
		case SHA3_384:
			d = sumSHA3_384(data)
		case SHA3_512:
			d = sumSHA3_512(data)
		case DBL_SHA2_256:
			d = sumSHA256(sumSHA256(data))
		case MURMUR3:
			d, err = sumMURMUR3(data)
		case SHAKE_128:
			d = sumSHAKE128(data)
		case SHAKE_256:
			d = sumSHAKE256(data)
		default:
			return m, ErrSumNotSupported
		}
	}
	if err != nil {
		return m, err
	}
	if length >= 0 {
		d = d[:length]
	}
	return Encode(d, code)
}

func isBlake2s(code uint64) bool {
	return code >= BLAKE2S_MIN && code <= BLAKE2S_MAX
}
func isBlake2b(code uint64) bool {
	return code >= BLAKE2B_MIN && code <= BLAKE2B_MAX
}

func sumBlake2b(size uint8, data []byte) []byte {
	hasher, err := blake2b.New(&blake2b.Config{Size: size})
	if err != nil {
		panic(err)
	}

	if _, err := hasher.Write(data); err != nil {
		panic(err)
	}

	return hasher.Sum(nil)[:]
}

func sumID(data []byte) []byte {
	return data
}

func sumSHA1(data []byte) []byte {
	a := sha1.Sum(data)
	return a[0:20]
}

func sumSHA256(data []byte) []byte {
	a := sha256.Sum256(data)
	return a[0:32]
}

func sumSHA512(data []byte) []byte {
	a := sha512.Sum512(data)
	return a[0:64]
}

func sumKeccak224(data []byte) []byte {
	h := keccak.New224()
	h.Write(data)
	return h.Sum(nil)
}

func sumKeccak256(data []byte) []byte {
	h := keccak.New256()
	h.Write(data)
	return h.Sum(nil)
}

func sumKeccak384(data []byte) []byte {
	h := keccak.New384()
	h.Write(data)
	return h.Sum(nil)
}

func sumKeccak512(data []byte) []byte {
	h := keccak.New512()
	h.Write(data)
	return h.Sum(nil)
}

func sumSHA3(data []byte) ([]byte, error) {
	h := sha3.New512()
	if _, err := h.Write(data); err != nil {
		return nil, err
	}
	return h.Sum(nil), nil
}

func sumSHA3_512(data []byte) []byte {
	a := sha3.Sum512(data)
	return a[:]
}

func sumMURMUR3(data []byte) ([]byte, error) {
	number := murmur3.Sum32(data)
	bytes := make([]byte, 4)
	for i := range bytes {
		bytes[i] = byte(number & 0xff)
		number >>= 8
	}
	return bytes, nil
}

func sumSHAKE128(data []byte) []byte {
	bytes := make([]byte, 32)
	sha3.ShakeSum128(bytes, data)
	return bytes
}

func sumSHAKE256(data []byte) []byte {
	bytes := make([]byte, 64)
	sha3.ShakeSum256(bytes, data)
	return bytes
}

func sumSHA3_384(data []byte) []byte {
	a := sha3.Sum384(data)
	return a[:]
}

func sumSHA3_256(data []byte) []byte {
	a := sha3.Sum256(data)
	return a[:]
}

func sumSHA3_224(data []byte) []byte {
	a := sha3.Sum224(data)
	return a[:]
}
<|endoftext|>