<commit_before>package kcp

import (
	"errors"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/v2ray/v2ray-core/common/alloc"
	"github.com/v2ray/v2ray-core/common/log"
)

var (
	errTimeout          = errors.New("i/o timeout")
	errBrokenPipe       = errors.New("broken pipe")
	errClosedListener   = errors.New("Listener closed.")
	errClosedConnection = errors.New("Connection closed.")
)

type State int32

const (
	StateActive       State = 0
	StateReadyToClose State = 1
	StatePeerClosed   State = 2
	StateTerminating  State = 3
	StateTerminated   State = 4
)

const (
	headerSize uint32 = 2
)

func nowMillisec() int64 {
	now := time.Now()
	return now.Unix()*1000 + int64(now.Nanosecond()/1000000)
}

type RountTripInfo struct {
	sync.RWMutex
	variation uint32
	srtt      uint32
	rto       uint32
	minRtt    uint32
}

func (this *RountTripInfo) Update(rtt uint32) {
	if rtt > 0x7FFFFFFF {
		return
	}
	this.Lock()
	defer this.Unlock()

	// https://tools.ietf.org/html/rfc6298
	if this.srtt == 0 {
		this.srtt = rtt
		this.variation = rtt / 2
	} else {
		delta := rtt - this.srtt
		if this.srtt > rtt {
			delta = this.srtt - rtt
		}
		this.variation = (3*this.variation + delta) / 4
		this.srtt = (7*this.srtt + rtt) / 8
		if this.srtt < this.minRtt {
			this.srtt = this.minRtt
		}
	}
	var rto uint32
	if this.minRtt < 4*this.variation {
		rto = this.srtt + 4*this.variation
	} else {
		rto = this.srtt + this.variation
	}

	if rto > 10000 {
		rto = 10000
	}
	this.rto = rto * 3 / 2
}

func (this *RountTripInfo) Timeout() uint32 {
	this.RLock()
	defer this.RUnlock()

	return this.rto
}

func (this *RountTripInfo) SmoothedTime() uint32 {
	this.RLock()
	defer this.RUnlock()

	return this.srtt
}

// Connection is a KCP connection over UDP.
type Connection struct {
	block         Authenticator
	local, remote net.Addr
	rd            time.Time
	wd            time.Time // write deadline
	writer        io.WriteCloser
	since         int64
	dataInputCond *sync.Cond

	conv             uint16
	state            State
	stateBeginTime   uint32
	lastIncomingTime uint32
	lastPayloadTime  uint32
	sendingUpdated   bool
	lastPingTime     uint32

	mss       uint32
	roundTrip *RountTripInfo
	interval  uint32

	receivingWorker *ReceivingWorker
	sendingWorker   *SendingWorker

	fastresend        uint32
	congestionControl bool
	output            *BufferedSegmentWriter
}

// NewConnection create a new KCP connection between local and remote.
func NewConnection(conv uint16, writerCloser io.WriteCloser, local *net.UDPAddr, remote *net.UDPAddr, block Authenticator) *Connection {
	log.Info("KCP|Connection: creating connection ", conv)

	conn := new(Connection)
	conn.local = local
	conn.remote = remote
	conn.block = block
	conn.writer = writerCloser
	conn.since = nowMillisec()
	conn.dataInputCond = sync.NewCond(new(sync.Mutex))

	authWriter := &AuthenticationWriter{
		Authenticator: block,
		Writer:        writerCloser,
	}
	conn.conv = conv
	conn.output = NewSegmentWriter(authWriter)

	conn.mss = authWriter.Mtu() - DataSegmentOverhead
	conn.roundTrip = &RountTripInfo{
		rto:    100,
		minRtt: effectiveConfig.Tti,
	}
	conn.interval = effectiveConfig.Tti
	conn.receivingWorker = NewReceivingWorker(conn)
	conn.fastresend = 2
	conn.congestionControl = effectiveConfig.Congestion
	conn.sendingWorker = NewSendingWorker(conn)

	go conn.updateTask()

	return conn
}

func (this *Connection) Elapsed() uint32 {
	return uint32(nowMillisec() - this.since)
}

// Read implements the Conn Read method.
func (this *Connection) Read(b []byte) (int, error) {
	if this == nil {
		return 0, io.EOF
	}

	for {
		if this.State() == StateReadyToClose || this.State() == StateTerminating || this.State() == StateTerminated {
			return 0, io.EOF
		}
		nBytes := this.receivingWorker.Read(b)
		if nBytes > 0 {
			return nBytes, nil
		}
		var timer *time.Timer
		if !this.rd.IsZero() {
			duration := this.rd.Sub(time.Now())
			if duration <= 0 {
				return 0, errTimeout
			}
			timer = time.AfterFunc(duration, this.dataInputCond.Signal)
		}
		this.dataInputCond.L.Lock()
		this.dataInputCond.Wait()
		this.dataInputCond.L.Unlock()
		if timer != nil {
			timer.Stop()
		}
		if !this.rd.IsZero() && this.rd.Before(time.Now()) {
			return 0, errTimeout
		}
	}
}

// Write implements the Conn Write method.
func (this *Connection) Write(b []byte) (int, error) {
	if this == nil || this.State() != StateActive {
		return 0, io.ErrClosedPipe
	}
	totalWritten := 0

	for {
		if this == nil || this.State() != StateActive {
			return totalWritten, io.ErrClosedPipe
		}

		nBytes := this.sendingWorker.Push(b[totalWritten:])
		if nBytes > 0 {
			totalWritten += nBytes
			if totalWritten == len(b) {
				return totalWritten, nil
			}
		}

		if !this.wd.IsZero() && this.wd.Before(time.Now()) {
			return totalWritten, errTimeout
		}

		// Sending windows is 1024 for the moment. This amount is not gonna sent in 1 sec.
		time.Sleep(time.Second)
	}
}

func (this *Connection) SetState(state State) {
	current := this.Elapsed()
	atomic.StoreInt32((*int32)(&this.state), int32(state))
	atomic.StoreUint32(&this.stateBeginTime, current)
	log.Info("KCP|Connection: Entering state ", state, " at ", current)

	switch state {
	case StateReadyToClose:
		this.receivingWorker.CloseRead()
	case StatePeerClosed:
		this.sendingWorker.CloseWrite()
	case StateTerminating:
		this.receivingWorker.CloseRead()
		this.sendingWorker.CloseWrite()
	case StateTerminated:
		this.receivingWorker.CloseRead()
		this.sendingWorker.CloseWrite()
	}
}

// Close closes the connection.
func (this *Connection) Close() error {
	if this == nil {
		return errClosedConnection
	}

	this.dataInputCond.Broadcast()

	state := this.State()
	if state == StateReadyToClose ||
		state == StateTerminating ||
		state == StateTerminated {
		return errClosedConnection
	}
	log.Info("KCP|Connection: Closing connection to ", this.remote)

	if state == StateActive {
		this.SetState(StateReadyToClose)
	}
	if state == StatePeerClosed {
		this.SetState(StateTerminating)
	}

	return nil
}

// LocalAddr returns the local network address. The Addr returned is shared by all invocations of LocalAddr, so do not modify it.
func (this *Connection) LocalAddr() net.Addr {
	if this == nil {
		return nil
	}
	return this.local
}

// RemoteAddr returns the remote network address. The Addr returned is shared by all invocations of RemoteAddr, so do not modify it.
func (this *Connection) RemoteAddr() net.Addr {
	if this == nil {
		return nil
	}
	return this.remote
}

// SetDeadline sets the deadline associated with the listener. A zero time value disables the deadline.
func (this *Connection) SetDeadline(t time.Time) error {
	if err := this.SetReadDeadline(t); err != nil {
		return err
	}
	if err := this.SetWriteDeadline(t); err != nil {
		return err
	}
	return nil
}

// SetReadDeadline implements the Conn SetReadDeadline method.
func (this *Connection) SetReadDeadline(t time.Time) error {
	if this == nil || this.State() != StateActive {
		return errClosedConnection
	}
	this.rd = t
	return nil
}

// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (this *Connection) SetWriteDeadline(t time.Time) error {
	if this == nil || this.State() != StateActive {
		return errClosedConnection
	}
	this.wd = t
	return nil
}

// kcp update, input loop
func (this *Connection) updateTask() {
	for this.State() != StateTerminated {
		this.flush()

		interval := time.Duration(effectiveConfig.Tti) * time.Millisecond
		if this.State() == StateTerminating {
			interval = time.Second
		}
		time.Sleep(interval)
	}
	this.Terminate()
}

func (this *Connection) FetchInputFrom(conn net.Conn) {
	go func() {
		for {
			payload := alloc.NewBuffer()
			nBytes, err := conn.Read(payload.Value)
			if err != nil {
				payload.Release()
				return
			}
			payload.Slice(0, nBytes)
			if this.block.Open(payload) {
				this.Input(payload.Value)
			} else {
				log.Info("KCP|Connection: Invalid response from ", conn.RemoteAddr())
			}
			payload.Release()
		}
	}()
}

func (this *Connection) Reusable() bool {
	return false
}

func (this *Connection) SetReusable(b bool) {}

func (this *Connection) Terminate() {
	if this == nil || this.writer == nil {
		return
	}
	log.Info("KCP|Connection: Terminating connection to ", this.RemoteAddr())

	this.writer.Close()
}

func (this *Connection) HandleOption(opt SegmentOption) {
	if (opt & SegmentOptionClose) == SegmentOptionClose {
		this.OnPeerClosed()
	}
}

func (this *Connection) OnPeerClosed() {
	state := this.State()
	if state == StateReadyToClose {
		this.SetState(StateTerminating)
	}
	if state == StateActive {
		this.SetState(StatePeerClosed)
	}
}

// Input when you received a low level packet (eg. UDP packet), call it
func (this *Connection) Input(data []byte) int {
	current := this.Elapsed()
	atomic.StoreUint32(&this.lastIncomingTime, current)

	var seg Segment
	for {
		seg, data = ReadSegment(data)
		if seg == nil {
			break
		}

		switch seg := seg.(type) {
		case *DataSegment:
			this.HandleOption(seg.Opt)
			this.receivingWorker.ProcessSegment(seg)
			atomic.StoreUint32(&this.lastPayloadTime, current)
			this.dataInputCond.Signal()
		case *AckSegment:
			this.HandleOption(seg.Opt)
			this.sendingWorker.ProcessSegment(current, seg)
			atomic.StoreUint32(&this.lastPayloadTime, current)
		case *CmdOnlySegment:
			this.HandleOption(seg.Opt)
			if seg.Cmd == SegmentCommandTerminated {
				state := this.State()
				if state == StateActive ||
					state == StateReadyToClose ||
					state == StatePeerClosed {
					this.SetState(StateTerminating)
				} else if state == StateTerminating {
					this.SetState(StateTerminated)
				}
			}
			this.sendingWorker.ProcessReceivingNext(seg.ReceivinNext)
			this.receivingWorker.ProcessSendingNext(seg.SendingNext)
		default:
		}
	}

	return 0
}

func (this *Connection) flush() {
	current := this.Elapsed()

	if this.State() == StateTerminated {
		return
	}
	if this.State() == StateActive && current-this.lastPayloadTime >= 30000 {
		this.Close()
	}

	if this.State() == StateTerminating {
		this.output.Write(&CmdOnlySegment{
			Conv: this.conv,
			Cmd:  SegmentCommandTerminated,
		})
		this.output.Flush()

		if current-this.stateBeginTime > 8000 {
			this.SetState(StateTerminated)
		}
		return
	}

	if this.State() == StateReadyToClose && current-this.stateBeginTime > 15000 {
		this.SetState(StateTerminating)
	}

	// flush acknowledges
	this.receivingWorker.Flush(current)
	this.sendingWorker.Flush(current)

	if this.sendingWorker.PingNecessary() || this.receivingWorker.PingNecessary() || current-this.lastPingTime >= 5000 {
		seg := NewCmdOnlySegment()
		seg.Conv = this.conv
		seg.Cmd = SegmentCommandPing
		seg.ReceivinNext = this.receivingWorker.nextNumber
		seg.SendingNext = this.sendingWorker.firstUnacknowledged
		if this.State() == StateReadyToClose {
			seg.Opt = SegmentOptionClose
		}
		this.output.Write(seg)
		this.lastPingTime = current
		this.sendingUpdated = false
		seg.Release()
	}

	// flash remain segments
	this.output.Flush()

}

func (this *Connection) State() State {
	return State(atomic.LoadInt32((*int32)(&this.state)))
}
<commit_msg>remove lastpayloadtime<commit_after>package kcp

import (
	"errors"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/v2ray/v2ray-core/common/alloc"
	"github.com/v2ray/v2ray-core/common/log"
)

var (
	errTimeout          = errors.New("i/o timeout")
	errBrokenPipe       = errors.New("broken pipe")
	errClosedListener   = errors.New("Listener closed.")
	errClosedConnection = errors.New("Connection closed.")
)

type State int32

const (
	StateActive       State = 0
	StateReadyToClose State = 1
	StatePeerClosed   State = 2
	StateTerminating  State = 3
	StateTerminated   State = 4
)

const (
	headerSize uint32 = 2
)

func nowMillisec() int64 {
	now := time.Now()
	return now.Unix()*1000 + int64(now.Nanosecond()/1000000)
}

type RountTripInfo struct {
	sync.RWMutex
	variation uint32
	srtt      uint32
	rto       uint32
	minRtt    uint32
}

func (this *RountTripInfo) Update(rtt uint32) {
	if rtt > 0x7FFFFFFF {
		return
	}
	this.Lock()
	defer this.Unlock()

	// https://tools.ietf.org/html/rfc6298
	if this.srtt == 0 {
		this.srtt = rtt
		this.variation = rtt / 2
	} else {
		delta := rtt - this.srtt
		if this.srtt > rtt {
			delta = this.srtt - rtt
		}
		this.variation = (3*this.variation + delta) / 4
		this.srtt = (7*this.srtt + rtt) / 8
		if this.srtt < this.minRtt {
			this.srtt = this.minRtt
		}
	}
	var rto uint32
	if this.minRtt < 4*this.variation {
		rto = this.srtt + 4*this.variation
	} else {
		rto = this.srtt + this.variation
	}

	if rto > 10000 {
		rto = 10000
	}
	this.rto = rto * 3 / 2
}

func (this *RountTripInfo) Timeout() uint32 {
	this.RLock()
	defer this.RUnlock()

	return this.rto
}

func (this *RountTripInfo) SmoothedTime() uint32 {
	this.RLock()
	defer this.RUnlock()

	return this.srtt
}

// Connection is a KCP connection over UDP.
type Connection struct {
	block         Authenticator
	local, remote net.Addr
	rd            time.Time
	wd            time.Time // write deadline
	writer        io.WriteCloser
	since         int64
	dataInputCond *sync.Cond

	conv             uint16
	state            State
	stateBeginTime   uint32
	lastIncomingTime uint32
	sendingUpdated   bool
	lastPingTime     uint32

	mss       uint32
	roundTrip *RountTripInfo
	interval  uint32

	receivingWorker *ReceivingWorker
	sendingWorker   *SendingWorker

	fastresend        uint32
	congestionControl bool
	output            *BufferedSegmentWriter
}

// NewConnection create a new KCP connection between local and remote.
func NewConnection(conv uint16, writerCloser io.WriteCloser, local *net.UDPAddr, remote *net.UDPAddr, block Authenticator) *Connection {
	log.Info("KCP|Connection: creating connection ", conv)

	conn := new(Connection)
	conn.local = local
	conn.remote = remote
	conn.block = block
	conn.writer = writerCloser
	conn.since = nowMillisec()
	conn.dataInputCond = sync.NewCond(new(sync.Mutex))

	authWriter := &AuthenticationWriter{
		Authenticator: block,
		Writer:        writerCloser,
	}
	conn.conv = conv
	conn.output = NewSegmentWriter(authWriter)

	conn.mss = authWriter.Mtu() - DataSegmentOverhead
	conn.roundTrip = &RountTripInfo{
		rto:    100,
		minRtt: effectiveConfig.Tti,
	}
	conn.interval = effectiveConfig.Tti
	conn.receivingWorker = NewReceivingWorker(conn)
	conn.fastresend = 2
	conn.congestionControl = effectiveConfig.Congestion
	conn.sendingWorker = NewSendingWorker(conn)

	go conn.updateTask()

	return conn
}

func (this *Connection) Elapsed() uint32 {
	return uint32(nowMillisec() - this.since)
}

// Read implements the Conn Read method.
func (this *Connection) Read(b []byte) (int, error) {
	if this == nil {
		return 0, io.EOF
	}

	for {
		if this.State() == StateReadyToClose || this.State() == StateTerminating || this.State() == StateTerminated {
			return 0, io.EOF
		}
		nBytes := this.receivingWorker.Read(b)
		if nBytes > 0 {
			return nBytes, nil
		}
		var timer *time.Timer
		if !this.rd.IsZero() {
			duration := this.rd.Sub(time.Now())
			if duration <= 0 {
				return 0, errTimeout
			}
			timer = time.AfterFunc(duration, this.dataInputCond.Signal)
		}
		this.dataInputCond.L.Lock()
		this.dataInputCond.Wait()
		this.dataInputCond.L.Unlock()
		if timer != nil {
			timer.Stop()
		}
		if !this.rd.IsZero() && this.rd.Before(time.Now()) {
			return 0, errTimeout
		}
	}
}

// Write implements the Conn Write method.
func (this *Connection) Write(b []byte) (int, error) {
	if this == nil || this.State() != StateActive {
		return 0, io.ErrClosedPipe
	}
	totalWritten := 0

	for {
		if this == nil || this.State() != StateActive {
			return totalWritten, io.ErrClosedPipe
		}

		nBytes := this.sendingWorker.Push(b[totalWritten:])
		if nBytes > 0 {
			totalWritten += nBytes
			if totalWritten == len(b) {
				return totalWritten, nil
			}
		}

		if !this.wd.IsZero() && this.wd.Before(time.Now()) {
			return totalWritten, errTimeout
		}

		// Sending windows is 1024 for the moment. This amount is not gonna sent in 1 sec.
		time.Sleep(time.Second)
	}
}

func (this *Connection) SetState(state State) {
	current := this.Elapsed()
	atomic.StoreInt32((*int32)(&this.state), int32(state))
	atomic.StoreUint32(&this.stateBeginTime, current)
	log.Info("KCP|Connection: Entering state ", state, " at ", current)

	switch state {
	case StateReadyToClose:
		this.receivingWorker.CloseRead()
	case StatePeerClosed:
		this.sendingWorker.CloseWrite()
	case StateTerminating:
		this.receivingWorker.CloseRead()
		this.sendingWorker.CloseWrite()
	case StateTerminated:
		this.receivingWorker.CloseRead()
		this.sendingWorker.CloseWrite()
	}
}

// Close closes the connection.
func (this *Connection) Close() error {
	if this == nil {
		return errClosedConnection
	}

	this.dataInputCond.Broadcast()

	state := this.State()
	if state == StateReadyToClose ||
		state == StateTerminating ||
		state == StateTerminated {
		return errClosedConnection
	}
	log.Info("KCP|Connection: Closing connection to ", this.remote)

	if state == StateActive {
		this.SetState(StateReadyToClose)
	}
	if state == StatePeerClosed {
		this.SetState(StateTerminating)
	}

	return nil
}

// LocalAddr returns the local network address. The Addr returned is shared by all invocations of LocalAddr, so do not modify it.
func (this *Connection) LocalAddr() net.Addr {
	if this == nil {
		return nil
	}
	return this.local
}

// RemoteAddr returns the remote network address. The Addr returned is shared by all invocations of RemoteAddr, so do not modify it.
func (this *Connection) RemoteAddr() net.Addr {
	if this == nil {
		return nil
	}
	return this.remote
}

// SetDeadline sets the deadline associated with the listener. A zero time value disables the deadline.
func (this *Connection) SetDeadline(t time.Time) error {
	if err := this.SetReadDeadline(t); err != nil {
		return err
	}
	if err := this.SetWriteDeadline(t); err != nil {
		return err
	}
	return nil
}

// SetReadDeadline implements the Conn SetReadDeadline method.
func (this *Connection) SetReadDeadline(t time.Time) error {
	if this == nil || this.State() != StateActive {
		return errClosedConnection
	}
	this.rd = t
	return nil
}

// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (this *Connection) SetWriteDeadline(t time.Time) error {
	if this == nil || this.State() != StateActive {
		return errClosedConnection
	}
	this.wd = t
	return nil
}

// kcp update, input loop
func (this *Connection) updateTask() {
	for this.State() != StateTerminated {
		this.flush()

		interval := time.Duration(effectiveConfig.Tti) * time.Millisecond
		if this.State() == StateTerminating {
			interval = time.Second
		}
		time.Sleep(interval)
	}
	this.Terminate()
}

func (this *Connection) FetchInputFrom(conn net.Conn) {
	go func() {
		for {
			payload := alloc.NewBuffer()
			nBytes, err := conn.Read(payload.Value)
			if err != nil {
				payload.Release()
				return
			}
			payload.Slice(0, nBytes)
			if this.block.Open(payload) {
				this.Input(payload.Value)
			} else {
				log.Info("KCP|Connection: Invalid response from ", conn.RemoteAddr())
			}
			payload.Release()
		}
	}()
}

func (this *Connection) Reusable() bool {
	return false
}

func (this *Connection) SetReusable(b bool) {}

func (this *Connection) Terminate() {
	if this == nil || this.writer == nil {
		return
	}
	log.Info("KCP|Connection: Terminating connection to ", this.RemoteAddr())

	this.writer.Close()
}

func (this *Connection) HandleOption(opt SegmentOption) {
	if (opt & SegmentOptionClose) == SegmentOptionClose {
		this.OnPeerClosed()
	}
}

func (this *Connection) OnPeerClosed() {
	state := this.State()
	if state == StateReadyToClose {
		this.SetState(StateTerminating)
	}
	if state == StateActive {
		this.SetState(StatePeerClosed)
	}
}

// Input when you received a low level packet (eg. UDP packet), call it
func (this *Connection) Input(data []byte) int {
	current := this.Elapsed()
	atomic.StoreUint32(&this.lastIncomingTime, current)

	var seg Segment
	for {
		seg, data = ReadSegment(data)
		if seg == nil {
			break
		}

		switch seg := seg.(type) {
		case *DataSegment:
			this.HandleOption(seg.Opt)
			this.receivingWorker.ProcessSegment(seg)
			this.dataInputCond.Signal()
		case *AckSegment:
			this.HandleOption(seg.Opt)
			this.sendingWorker.ProcessSegment(current, seg)
		case *CmdOnlySegment:
			this.HandleOption(seg.Opt)
			if seg.Cmd == SegmentCommandTerminated {
				state := this.State()
				if state == StateActive ||
					state == StateReadyToClose ||
					state == StatePeerClosed {
					this.SetState(StateTerminating)
				} else if state == StateTerminating {
					this.SetState(StateTerminated)
				}
			}
			this.sendingWorker.ProcessReceivingNext(seg.ReceivinNext)
			this.receivingWorker.ProcessSendingNext(seg.SendingNext)
		default:
		}
	}

	return 0
}

func (this *Connection) flush() {
	current := this.Elapsed()

	if this.State() == StateTerminated {
		return
	}
	if this.State() == StateActive && current-atomic.LoadUint32(&this.lastIncomingTime) >= 30000 {
		this.Close()
	}

	if this.State() == StateTerminating {
		this.output.Write(&CmdOnlySegment{
			Conv: this.conv,
			Cmd:  SegmentCommandTerminated,
		})
		this.output.Flush()

		if current-atomic.LoadUint32(&this.stateBeginTime) > 8000 {
			this.SetState(StateTerminated)
		}
		return
	}

	if this.State() == StateReadyToClose && current-atomic.LoadUint32(&this.stateBeginTime) > 15000 {
		this.SetState(StateTerminating)
	}

	// flush acknowledges
	this.receivingWorker.Flush(current)
	this.sendingWorker.Flush(current)

	if this.sendingWorker.PingNecessary() || this.receivingWorker.PingNecessary() || current-atomic.LoadUint32(&this.lastPingTime) >= 5000 {
		seg := NewCmdOnlySegment()
		seg.Conv = this.conv
		seg.Cmd = SegmentCommandPing
		seg.ReceivinNext = this.receivingWorker.nextNumber
		seg.SendingNext = this.sendingWorker.firstUnacknowledged
		if this.State() == StateReadyToClose {
			seg.Opt = SegmentOptionClose
		}
		this.output.Write(seg)
		this.lastPingTime = current
		this.sendingUpdated = false
		seg.Release()
	}

	// flash remain segments
	this.output.Flush()

}

func (this *Connection) State() State {
	return State(atomic.LoadInt32((*int32)(&this.state)))
}
<|endoftext|>


<commit_before>//go:generate go tool yacc -o lang.go lang.y
package lang

import (
	"bufio"
	"bytes"
	"io"
	"regexp"
	"strconv"
	"unicode"
	"unicode/utf8"
)

type Token uint32

const (
	EOF Token = 1<<32 - 1 - iota
	Error
)

type Result struct {
	Token Token
	Value []byte
	Line  int
}

type Definition struct {
	Token   Token
	Pattern string
	regexp  *regexp.Regexp
}

type Scanner struct {
	quote      string
	inQuotes   bool
	tokenizer  *bufio.Scanner
	defs       []Definition
	lineNumber int
	leftover   []byte
}

func NewScanner(defs []Definition) *Scanner {
	for i := range defs {
		defs[i].regexp = regexp.MustCompile("^" + defs[i].Pattern)
	}
	return &Scanner{
		quote:      "",
		inQuotes:   false,
		defs:       defs,
		lineNumber: 1,
		leftover:   []byte{},
	}
}

func (s *Scanner) SetInput(input io.Reader) {
	s.tokenizer = bufio.NewScanner(input)
	s.tokenizer.Split(s.ScanWords)
}

func (s *Scanner) matchBytesToToken(bytes []byte) *Result {
	for _, def := range s.defs {
		if result := def.regexp.Find(bytes); result != nil {
			if len(result) != len(bytes) { // stuff leftover!
				s.leftover = append(s.leftover, bytes[len(result):]...)
			}
			return &Result{Token: def.Token, Value: result, Line: s.lineNumber}
		}
	}
	return &Result{Token: Error, Line: s.lineNumber, Value: []byte("No match for '" + string(bytes) + "'")}
}

func (s *Scanner) Next() *Result {
	if len(s.leftover) > 0 {
		res := s.matchBytesToToken(s.leftover)
		if res.Token != Error {
			s.leftover = []byte{}
			return res
		}
	}
	if !s.tokenizer.Scan() {
		err := s.tokenizer.Err()
		var ev []byte
		if err != nil {
			ev = []byte(err.Error())
		}
		return &Result{Token: Error, Line: s.lineNumber, Value: ev}
	}
	bytes := s.tokenizer.Bytes()
	return s.matchBytesToToken(bytes)
}

func (s *Scanner) Tokenize() []*Result {
	var results []*Result
	r := s.Next()
	for r != nil && r.Token != Error {
		results = append(results, r)
		r = s.Next()
	}
	return results
}

// slightly altered version of https://golang.org/src/bufio/scan.go?s=12794:12872 to keep track of line numbers
func (s *Scanner) ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) {
	// Skip leading spaces.
	start := 0
	for width := 0; start < len(data); start += width {
		var r rune
		r, width = utf8.DecodeRune(data[start:])
		if !unicode.IsSpace(r) {
			break
		}
		s.lineNumber += bytes.Count(data[start:], []byte{'\n'})
		s.lineNumber += bytes.Count(data[start:], []byte{'\r'})
	}
	// Scan until space, marking end of word, unless we are in a string
	for width, i := 0, start; i < len(data); i += width {
		var r rune
		r, width = utf8.DecodeRune(data[i:])
		quote := strconv.QuoteRuneToASCII(r)
		if len(quote) == 3 && (quote[1] == '"' || quote[1] == '\'') {
			if s.quote == quote {
				s.quote = ""
				s.inQuotes = false
			} else {
				s.quote = quote
				s.inQuotes = true
			}
		}
		if unicode.IsSpace(r) && !s.inQuotes {
			s.lineNumber += bytes.Count(data[i:], []byte{'\n'})
			s.lineNumber += bytes.Count(data[i:], []byte{'\r'})
			return i + width, data[start:i], nil
		} else if unicode.IsSpace(r) {
		}
	}
	// If we're at EOF, we have a final, non-empty, non-terminated word. Return it.
	if atEOF && len(data) > start {
		return len(data), data[start:], nil
	}
	// Request more data.
	return start, nil, nil
}
<commit_msg>handle leftover edge cases in lexer<commit_after>//go:generate go tool yacc -o lang.go lang.y
package lang

import (
	"bufio"
	"bytes"
	"io"
	"regexp"
	"strconv"
	"unicode"
	"unicode/utf8"
)

type Token uint32

const (
	EOF Token = 1<<32 - 1 - iota
	Error
)

type Result struct {
	Token Token
	Value []byte
	Line  int
}

type Definition struct {
	Token   Token
	Pattern string
	regexp  *regexp.Regexp
}

type Scanner struct {
	quote      string
	inQuotes   bool
	tokenizer  *bufio.Scanner
	defs       []Definition
	lineNumber int
	leftover   []byte
}

func NewScanner(defs []Definition) *Scanner {
	for i := range defs {
		defs[i].regexp = regexp.MustCompile("^" + defs[i].Pattern)
	}
	return &Scanner{
		quote:      "",
		inQuotes:   false,
		defs:       defs,
		lineNumber: 1,
		leftover:   []byte{},
	}
}

func (s *Scanner) SetInput(input io.Reader) {
	s.tokenizer = bufio.NewScanner(input)
	s.tokenizer.Split(s.ScanWords)
}

func (s *Scanner) matchBytesToToken(bytes []byte) *Result {
	for _, def := range s.defs {
		if result := def.regexp.Find(bytes); result != nil {
			if len(result) != len(bytes) { // stuff leftover!
				if len(s.leftover) == 0 {
					s.leftover = append(s.leftover, bytes[len(result):]...)
				}
			}
			return &Result{Token: def.Token, Value: result, Line: s.lineNumber}
		}
	}
	return &Result{Token: Error, Line: s.lineNumber, Value: []byte("No match for '" + string(bytes) + "'")}
}

func (s *Scanner) Next() *Result {
	if len(s.leftover) > 0 {
		res := s.matchBytesToToken(s.leftover)
		if res.Token != Error {
			s.leftover = s.leftover[len(res.Value):]
			return res
		}
	}
	if !s.tokenizer.Scan() {
		err := s.tokenizer.Err()
		var ev []byte
		if err != nil {
			ev = []byte(err.Error())
		}
		return &Result{Token: Error, Line: s.lineNumber, Value: ev}
	}
	bytes := s.tokenizer.Bytes()
	return s.matchBytesToToken(bytes)
}

func (s *Scanner) Tokenize() []*Result {
	var results []*Result
	r := s.Next()
	for r != nil && r.Token != Error {
		results = append(results, r)
		r = s.Next()
	}
	return results
}

// slightly altered version of https://golang.org/src/bufio/scan.go?s=12794:12872 to keep track of line numbers
func (s *Scanner) ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) {
	// Skip leading spaces.
	start := 0
	for width := 0; start < len(data); start += width {
		var r rune
		r, width = utf8.DecodeRune(data[start:])
		if !unicode.IsSpace(r) {
			break
		}
		s.lineNumber += bytes.Count(data[start:], []byte{'\n'})
		s.lineNumber += bytes.Count(data[start:], []byte{'\r'})
	}
	// Scan until space, marking end of word, unless we are in a string
	for width, i := 0, start; i < len(data); i += width {
		var r rune
		r, width = utf8.DecodeRune(data[i:])
		quote := strconv.QuoteRuneToASCII(r)
		if len(quote) == 3 && (quote[1] == '"' || quote[1] == '\'') {
			if s.quote == quote {
				s.quote = ""
				s.inQuotes = false
			} else {
				s.quote = quote
				s.inQuotes = true
			}
		}
		if unicode.IsSpace(r) && !s.inQuotes {
			s.lineNumber += bytes.Count(data[i:], []byte{'\n'})
			s.lineNumber += bytes.Count(data[i:], []byte{'\r'})
			return i + width, data[start:i], nil
		} else if unicode.IsSpace(r) {
		}
	}
	// If we're at EOF, we have a final, non-empty, non-terminated word. Return it.
	if atEOF && len(data) > start {
		return len(data), data[start:], nil
	}
	// Request more data.
	return start, nil, nil
}
<|endoftext|>


<commit_before>package queue

import (
	"github.com/nathan-osman/go-cannon/util"

	"log"
	"time"
)

// Mail queue managing the sending of messages to hosts.
type Queue struct {
	storage    *Storage
	hosts      map[string]*Host
	newMessage *util.NonBlockingChan
	stop       chan bool
}

// Deliver the specified message to the appropriate host queue.
func (q *Queue) deliverMessage(id string) {
	if m, err := q.storage.GetMessage(id); err == nil {
		log.Printf("delivering message to %s queue", m.Host)
		if _, ok := q.hosts[m.Host]; !ok {
			q.hosts[m.Host] = NewHost(m.Host, q.storage)
		}
		q.hosts[m.Host].Deliver(id)
	} else {
		log.Print(err)
	}
}

// Check for inactive host queues and shut them down.
func (q *Queue) checkForInactiveQueues() {
	for h := range q.hosts {
		if q.hosts[h].Idle() > time.Minute {
			q.hosts[h].Stop()
			delete(q.hosts, h)
		}
	}
}

// Receive new messages and deliver them to the specified host queue.
func (q *Queue) run() {
	defer close(q.stop)
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()
loop:
	for {
		select {
		case i := <-q.newMessage.Recv:
			q.deliverMessage(i.(string))
		case <-ticker.C:
			q.checkForInactiveQueues()
		case <-q.stop:
			break loop
		}
	}
	log.Println("shutting down host queues")
	for h := range q.hosts {
		q.hosts[h].Stop()
	}
}

// Create a new message queue. Any undelivered messages on disk will be added
// to the appropriate queue.
func NewQueue(directory string) (*Queue, error) {
	if s, messages, err := NewStorage(directory); err == nil {
		q := &Queue{
			storage:    s,
			hosts:      make(map[string]*Host),
			newMessage: util.NewNonBlockingChan(),
			stop:       make(chan bool),
		}
		for _, m := range messages {
			q.newMessage.Send <- m
		}
		go q.run()
		return q, nil
	} else {
		return nil, err
	}
}

// Deliver the specified message to the appropriate host queue.
func (q *Queue) Deliver(m *Message) error {
	if id, err := q.storage.NewMessage(m); err == nil {
		q.newMessage.Send <- id
		return nil
	} else {
		return err
	}
}

// Stop all active host queues.
func (q *Queue) Stop() {
	q.stop <- true
	<-q.stop
}
<commit_msg>Made message delivery to the host queue slightly more efficient.<commit_after>package queue

import (
	"github.com/nathan-osman/go-cannon/util"

	"log"
	"time"
)

// Mail queue managing the sending of messages to hosts.
type Queue struct {
	storage    *Storage
	hosts      map[string]*Host
	newMessage *util.NonBlockingChan
	stop       chan bool
}

// Deliver the specified message to the appropriate host queue.
func (q *Queue) deliverMessage(m *Message) {
	if id, err := q.storage.NewMessage(m); err == nil {
		log.Printf("delivering message to %s queue", m.Host)
		if _, ok := q.hosts[m.Host]; !ok {
			q.hosts[m.Host] = NewHost(m.Host, q.storage)
		}
		q.hosts[m.Host].Deliver(id)
	} else {
		log.Print(err)
	}
}

// Check for inactive host queues and shut them down.
func (q *Queue) checkForInactiveQueues() {
	for h := range q.hosts {
		if q.hosts[h].Idle() > time.Minute {
			q.hosts[h].Stop()
			delete(q.hosts, h)
		}
	}
}

// Receive new messages and deliver them to the specified host queue.
func (q *Queue) run() {
	defer close(q.stop)
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()
loop:
	for {
		select {
		case i := <-q.newMessage.Recv:
			q.deliverMessage(i.(*Message))
		case <-ticker.C:
			q.checkForInactiveQueues()
		case <-q.stop:
			break loop
		}
	}
	log.Println("shutting down host queues")
	for h := range q.hosts {
		q.hosts[h].Stop()
	}
}

// Create a new message queue. Any undelivered messages on disk will be added
// to the appropriate queue.
func NewQueue(directory string) (*Queue, error) {
	if s, messages, err := NewStorage(directory); err == nil {
		q := &Queue{
			storage:    s,
			hosts:      make(map[string]*Host),
			newMessage: util.NewNonBlockingChan(),
			stop:       make(chan bool),
		}
		for _, m := range messages {
			q.newMessage.Send <- m
		}
		go q.run()
		return q, nil
	} else {
		return nil, err
	}
}

// Deliver the specified message to the appropriate host queue.
func (q *Queue) Deliver(m *Message) {
	q.newMessage.Send <- m
}

// Stop all active host queues.
func (q *Queue) Stop() {
	q.stop <- true
	<-q.stop
}
<|endoftext|>


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

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/tomasen/fcgi_client"
)

var (
	phpfpmUpDesc = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "up"),
		"Whether scraping PHP-FPM's metrics was successful.",
		[]string{"socket_path"}, nil)

	phpfpmAcceptedConnections = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "accepted_connections_total"),
		"Number of request accepted by the pool.",
		[]string{"socket_path"}, nil)

	phpfpmStartTime = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "start_time_seconds"),
		"Unix time when FPM has started or reloaded.",
		[]string{"socket_path"}, nil)

	phpfpmGauges = map[string]*prometheus.Desc{
		"listen queue": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "listen_queue"),
			"Number of request in the queue of pending connections.",
			[]string{"socket_path"}, nil),
		"max listen queue": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_listen_queue"),
			"Maximum number of requests in the queue of pending connections since FPM has started.",
			[]string{"socket_path"}, nil),
		"listen queue len": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "listen_queue_length"),
			"The size of the socket queue of pending connections.",
			[]string{"socket_path"}, nil),
		"idle processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "idle_processes"),
			"Number of idle processes.",
			[]string{"socket_path"}, nil),
		"active processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "active_processes"),
			"Number of active processes.",
			[]string{"socket_path"}, nil),
		"max active processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_active_processes"),
			"Maximum number of active processes since FPM has started.",
			[]string{"socket_path"}, nil),
		"max children reached": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_children_reached"),
			"Number of times, the process limit has been reached.",
			[]string{"socket_path"}, nil),
		"slow requests": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "slow_requests"),
			"Enable php-fpm slow-log before you consider this. If this value is non-zero you may have slow php processes.",
			[]string{"socket_path"}, nil),
	}
)

// Converts the output of Dovecot's EXPORT command to metrics.
func CollectFromReader(reader io.Reader, socketPath string, ch chan<- prometheus.Metric) error {
	scanner := bufio.NewScanner(reader)
	re := regexp.MustCompile("^(.*): +(.*)$")

	// Scrape the interesting values:
	for scanner.Scan() {
		fields := re.FindStringSubmatch(scanner.Text())
		if fields == nil {
			return fmt.Errorf("Failed to parse %s", scanner.Text())
		}

		if gauge, ok := phpfpmGauges[fields[1]]; ok {
			f, err := strconv.ParseFloat(fields[2], 64)
			if err != nil {
				return err
			}
			ch <- prometheus.MustNewConstMetric(
				gauge,
				prometheus.GaugeValue,
				f,
				socketPath)
		} else if fields[1] == "accepted conn" {
			f, err := strconv.ParseFloat(fields[2], 64)
			if err != nil {
				return err
			}
			ch <- prometheus.MustNewConstMetric(
				phpfpmAcceptedConnections,
				prometheus.CounterValue,
				f,
				socketPath)
		} else if fields[1] == "start time" {
			location, err := time.LoadLocation("Local")
			if err != nil {
				return err
			}
			since, err := time.ParseInLocation("02/Jan/2006:15:04:05 -0700", fields[2], location)
			if err != nil {
				return err
			}
			f := float64(since.Unix())
			ch <- prometheus.MustNewConstMetric(
				phpfpmStartTime,
				prometheus.GaugeValue,
				f,
				socketPath)
		}
	}
	return nil
}

func CollectFromSocket(path string, scriptName string, ch chan<- prometheus.Metric) error {

	env := make(map[string]string)
	env["SCRIPT_FILENAME"] = scriptName
	env["SCRIPT_NAME"] = scriptName
	env["REQUEST_METHODGET"] = "GET"

	fcgi, err := fcgiclient.Dial("unix", path)
	if err != nil {
		return err
	}

	resp, err := fcgi.Get(env)
	if err != nil {
		return err
	}

	return CollectFromReader(resp.Body, path, ch)
}

type PhpfpmExporter struct {
	socketPaths []string
	scriptName  string
}

func NewPhpfpmExporter(socketPaths []string, scriptName string) (*PhpfpmExporter, error) {
	return &PhpfpmExporter{
		socketPaths: socketPaths,
		scriptName:  scriptName,
	}, nil
}

func (e *PhpfpmExporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- phpfpmUpDesc
	ch <- phpfpmAcceptedConnections
	ch <- phpfpmStartTime
	for _, desc := range phpfpmGauges {
		ch <- desc
	}
}

func (e *PhpfpmExporter) Collect(ch chan<- prometheus.Metric) {
	for _, socketPath := range e.socketPaths {
		err := CollectFromSocket(socketPath, e.scriptName, ch)
		if err == nil {
			ch <- prometheus.MustNewConstMetric(
				phpfpmUpDesc,
				prometheus.GaugeValue,
				1.0,
				socketPath)
		} else {
			log.Printf("Failed to scrape socket: %s", err)
			ch <- prometheus.MustNewConstMetric(
				phpfpmUpDesc,
				prometheus.GaugeValue,
				0.0,
				socketPath)
		}
	}
}

func main() {
	var (
		listenAddress = flag.String("web.listen-address", ":9253", "Address to listen on for web interface and telemetry.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		socketPaths   = flag.String("phpfpm.socket-paths", "", "Paths of the PHP-FPM sockets.")
		scriptName = flag.String("phpfpm.scriptname", "/server-status-fpm.php", "Scriptname for fcgi socket communication.")
	)
	flag.Parse()

	exporter, err := NewPhpfpmExporter(strings.Split(*socketPaths, ","), *scriptName)
	if err != nil {
		panic(err)
	}
	prometheus.MustRegister(exporter)

	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`

			<title>PHP-FPM Exporter</title>

PHP-FPM Exporter

<a href='` + *metricsPath + `'>Metrics

			`))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<commit_msg>Fix style.<commit_after>// Copyright 2017 Kumina, https://kumina.nl/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/tomasen/fcgi_client"
)

var (
	phpfpmUpDesc = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "up"),
		"Whether scraping PHP-FPM's metrics was successful.",
		[]string{"socket_path"}, nil)

	phpfpmAcceptedConnections = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "accepted_connections_total"),
		"Number of request accepted by the pool.",
		[]string{"socket_path"}, nil)

	phpfpmStartTime = prometheus.NewDesc(
		prometheus.BuildFQName("php", "fpm", "start_time_seconds"),
		"Unix time when FPM has started or reloaded.",
		[]string{"socket_path"}, nil)

	phpfpmGauges = map[string]*prometheus.Desc{
		"listen queue": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "listen_queue"),
			"Number of request in the queue of pending connections.",
			[]string{"socket_path"}, nil),
		"max listen queue": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_listen_queue"),
			"Maximum number of requests in the queue of pending connections since FPM has started.",
			[]string{"socket_path"}, nil),
		"listen queue len": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "listen_queue_length"),
			"The size of the socket queue of pending connections.",
			[]string{"socket_path"}, nil),
		"idle processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "idle_processes"),
			"Number of idle processes.",
			[]string{"socket_path"}, nil),
		"active processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "active_processes"),
			"Number of active processes.",
			[]string{"socket_path"}, nil),
		"max active processes": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_active_processes"),
			"Maximum number of active processes since FPM has started.",
			[]string{"socket_path"}, nil),
		"max children reached": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "max_children_reached"),
			"Number of times, the process limit has been reached.",
			[]string{"socket_path"}, nil),
		"slow requests": prometheus.NewDesc(
			prometheus.BuildFQName("php", "fpm", "slow_requests"),
			"Enable php-fpm slow-log before you consider this. If this value is non-zero you may have slow php processes.",
			[]string{"socket_path"}, nil),
	}
)

// Converts the output of Dovecot's EXPORT command to metrics.
func CollectFromReader(reader io.Reader, socketPath string, ch chan<- prometheus.Metric) error {
	scanner := bufio.NewScanner(reader)
	re := regexp.MustCompile("^(.*): +(.*)$")

	// Scrape the interesting values:
	for scanner.Scan() {
		fields := re.FindStringSubmatch(scanner.Text())
		if fields == nil {
			return fmt.Errorf("Failed to parse %s", scanner.Text())
		}

		if gauge, ok := phpfpmGauges[fields[1]]; ok {
			f, err := strconv.ParseFloat(fields[2], 64)
			if err != nil {
				return err
			}
			ch <- prometheus.MustNewConstMetric(
				gauge,
				prometheus.GaugeValue,
				f,
				socketPath)
		} else if fields[1] == "accepted conn" {
			f, err := strconv.ParseFloat(fields[2], 64)
			if err != nil {
				return err
			}
			ch <- prometheus.MustNewConstMetric(
				phpfpmAcceptedConnections,
				prometheus.CounterValue,
				f,
				socketPath)
		} else if fields[1] == "start time" {
			location, err := time.LoadLocation("Local")
			if err != nil {
				return err
			}
			since, err := time.ParseInLocation("02/Jan/2006:15:04:05 -0700", fields[2], location)
			if err != nil {
				return err
			}
			f := float64(since.Unix())
			ch <- prometheus.MustNewConstMetric(
				phpfpmStartTime,
				prometheus.GaugeValue,
				f,
				socketPath)
		}
	}
	return nil
}

func CollectFromSocket(path string, scriptName string, ch chan<- prometheus.Metric) error {

	env := make(map[string]string)
	env["SCRIPT_FILENAME"] = scriptName
	env["SCRIPT_NAME"] = scriptName
	env["REQUEST_METHODGET"] = "GET"

	fcgi, err := fcgiclient.Dial("unix", path)
	if err != nil {
		return err
	}

	resp, err := fcgi.Get(env)
	if err != nil {
		return err
	}

	return CollectFromReader(resp.Body, path, ch)
}

type PhpfpmExporter struct {
	socketPaths []string
	scriptName  string
}

func NewPhpfpmExporter(socketPaths []string, scriptName string) (*PhpfpmExporter, error) {
	return &PhpfpmExporter{
		socketPaths: socketPaths,
		scriptName:  scriptName,
	}, nil
}

func (e *PhpfpmExporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- phpfpmUpDesc
	ch <- phpfpmAcceptedConnections
	ch <- phpfpmStartTime
	for _, desc := range phpfpmGauges {
		ch <- desc
	}
}

func (e *PhpfpmExporter) Collect(ch chan<- prometheus.Metric) {
	for _, socketPath := range e.socketPaths {
		err := CollectFromSocket(socketPath, e.scriptName, ch)
		if err == nil {
			ch <- prometheus.MustNewConstMetric(
				phpfpmUpDesc,
				prometheus.GaugeValue,
				1.0,
				socketPath)
		} else {
			log.Printf("Failed to scrape socket: %s", err)
			ch <- prometheus.MustNewConstMetric(
				phpfpmUpDesc,
				prometheus.GaugeValue,
				0.0,
				socketPath)
		}
	}
}

func main() {
	var (
		listenAddress = flag.String("web.listen-address", ":9253", "Address to listen on for web interface and telemetry.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		socketPaths   = flag.String("phpfpm.socket-paths", "", "Paths of the PHP-FPM sockets.")
		scriptName    = flag.String("phpfpm.scriptname", "/server-status-fpm.php", "Scriptname for fcgi socket communication.")
	)
	flag.Parse()

	exporter, err := NewPhpfpmExporter(strings.Split(*socketPaths, ","), *scriptName)
	if err != nil {
		panic(err)
	}
	prometheus.MustRegister(exporter)

	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`

			<title>PHP-FPM Exporter</title>

PHP-FPM Exporter

<a href='` + *metricsPath + `'>Metrics

			`))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<|endoftext|>


<commit_before>package lorica

import (
	"fmt"

	"github.com/miekg/pkcs11"
)

// A Token represents a cryptographic token that implements PKCS #11.
type Token struct {
	module  *pkcs11.Ctx
	session pkcs11.SessionHandle
}

// findSlot retrieves ID of the slot with matching token label.
func findSlot(module *pkcs11.Ctx, tokenLabel string) (uint, error) {
	var nilSlot uint

	slots, err := module.GetSlotList(true)
	if err != nil {
		return nilSlot, fmt.Errorf("failed to get slot list: %s", err)
	}

	for _, slot := range slots {
		tokenInfo, err := module.GetTokenInfo(slot)
		if err != nil {
			return nilSlot, fmt.Errorf("failed to get token info: %s", err)
		}

		if tokenInfo.Label == tokenLabel {
			return slot, nil
		}
	}

	return nilSlot, fmt.Errorf("no slot with token label '%q'", tokenLabel)
}

// OpenToken opens a new session with the given cryptographic token.
func OpenToken(modulePath, tokenLabel, pin string, readOnly bool) (*Token, error) {
	module := pkcs11.New(modulePath)
	if module == nil {
		return nil, fmt.Errorf("failed to load module '%s'", modulePath)
	}

	err := module.Initialize()
	if err != nil {
		return nil, err
	}

	slotID, err := findSlot(module, tokenLabel)
	if err != nil {
		return nil, err
	}

	var flags uint
	if readOnly {
		flags = pkcs11.CKF_SERIAL_SESSION
	} else {
		flags = pkcs11.CKF_SERIAL_SESSION | pkcs11.CKF_RW_SESSION
	}
	session, err := module.OpenSession(slotID, flags)
	if err != nil {
		return nil, err
	}

	// Log in as a normal user with given PIN.
	//
	// NOTE: Login status is application-wide, not per session. It is fine
	// if the token complains user already logged in.
	err = module.Login(session, pkcs11.CKU_USER, pin)
	if err != nil && err != pkcs11.Error(pkcs11.CKR_USER_ALREADY_LOGGED_IN) {
		module.CloseSession(session)
		return nil, err
	}

	return &Token{module, session}, nil
}

// Close closes the current session with the token.
//
// NOTE: We do not explicitly log out the session or unload the module
// here, as it may cause problem if there are multiple sessions active.
// In general, it will log out once the last session is closed and the
// module will be unloaded at the end of the process.
func (tk *Token) Close() error {
	err := tk.module.CloseSession(tk.session)
	if err != nil {
		return fmt.Errorf("failed to close session: %s", err)
	}

	return nil
}

// Info obtains information about the token.
func (tk *Token) Info() (pkcs11.TokenInfo, error) {
	var nilTokenInfo pkcs11.TokenInfo

	sessionInfo, err := tk.module.GetSessionInfo(tk.session)
	if err != nil {
		return nilTokenInfo, fmt.Errorf("failed to get session info: %s", err)
	}

	tokenInfo, err := tk.module.GetTokenInfo(sessionInfo.SlotID)
	if err != nil {
		return nilTokenInfo, fmt.Errorf("failed to get token info: %s", err)
	}

	return tokenInfo, nil
}
<commit_msg>Implement RSA key pair generation<commit_after>package lorica

import (
	"crypto"
	"crypto/rsa"
	"errors"
	"fmt"
	"hash/crc64"
	"math/big"

	"github.com/miekg/pkcs11"
)

// A Token represents a cryptographic token that implements PKCS #11.
type Token struct {
	module  *pkcs11.Ctx
	session pkcs11.SessionHandle
}

// findSlot retrieves ID of the slot with matching token label.
func findSlot(module *pkcs11.Ctx, tokenLabel string) (uint, error) {
	var nilSlot uint

	slots, err := module.GetSlotList(true)
	if err != nil {
		return nilSlot, fmt.Errorf("failed to get slot list: %s", err)
	}

	for _, slot := range slots {
		tokenInfo, err := module.GetTokenInfo(slot)
		if err != nil {
			return nilSlot, fmt.Errorf("failed to get token info: %s", err)
		}

		if tokenInfo.Label == tokenLabel {
			return slot, nil
		}
	}

	return nilSlot, fmt.Errorf("no slot with token label '%q'", tokenLabel)
}

// OpenToken opens a new session with the given cryptographic token.
func OpenToken(modulePath, tokenLabel, pin string, readOnly bool) (*Token, error) {
	module := pkcs11.New(modulePath)
	if module == nil {
		return nil, fmt.Errorf("failed to load module '%s'", modulePath)
	}

	err := module.Initialize()
	if err != nil {
		return nil, err
	}

	slotID, err := findSlot(module, tokenLabel)
	if err != nil {
		return nil, err
	}

	var flags uint
	if readOnly {
		flags = pkcs11.CKF_SERIAL_SESSION
	} else {
		flags = pkcs11.CKF_SERIAL_SESSION | pkcs11.CKF_RW_SESSION
	}
	session, err := module.OpenSession(slotID, flags)
	if err != nil {
		return nil, err
	}

	// Log in as a normal user with given PIN.
	//
	// NOTE: Login status is application-wide, not per session. It is fine
	// if the token complains user already logged in.
	err = module.Login(session, pkcs11.CKU_USER, pin)
	if err != nil && err != pkcs11.Error(pkcs11.CKR_USER_ALREADY_LOGGED_IN) {
		module.CloseSession(session)
		return nil, err
	}

	return &Token{module, session}, nil
}

// Close closes the current session with the token.
//
// NOTE: We do not explicitly log out the session or unload the module
// here, as it may cause problem if there are multiple sessions active.
// In general, it will log out once the last session is closed and the
// module will be unloaded at the end of the process.
func (tk *Token) Close() error {
	err := tk.module.CloseSession(tk.session)
	if err != nil {
		return fmt.Errorf("failed to close session: %s", err)
	}

	return nil
}

// Info obtains information about the token.
func (tk *Token) Info() (pkcs11.TokenInfo, error) {
	var nilTokenInfo pkcs11.TokenInfo

	sessionInfo, err := tk.module.GetSessionInfo(tk.session)
	if err != nil {
		return nilTokenInfo, fmt.Errorf("failed to get session info: %s", err)
	}

	tokenInfo, err := tk.module.GetTokenInfo(sessionInfo.SlotID)
	if err != nil {
		return nilTokenInfo, fmt.Errorf("failed to get token info: %s", err)
	}

	return tokenInfo, nil
}

// Generate an RSA key pair of the given bit size inside the token.
func (tk *Token) generateRSAKeyPair(label string, bits int) (pkcs11.ObjectHandle, pkcs11.ObjectHandle, error) {
	keyID := uint(crc64.Checksum([]byte(label), crc64.MakeTable(crc64.ECMA)))

	mechanism := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil)}
	publicKeyTemplate := []*pkcs11.Attribute{
		// Common storage object attributes (PKCS #11-B 10.4)
		pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
		pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, false),
		pkcs11.NewAttribute(pkcs11.CKA_MODIFIABLE, false),
		pkcs11.NewAttribute(pkcs11.CKA_LABEL, label),
		// Common key attributes (PKCS #11-B 10.7)
		pkcs11.NewAttribute(pkcs11.CKA_ID, keyID),
		// Common public key attributes (PKCS #11-B 10.8)
		pkcs11.NewAttribute(pkcs11.CKA_ENCRYPT, true),
		pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true),
		// RSA public key object attributes (PKCS #11-M1 6.1.2)
		pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, bits),
		pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
	}
	privateKeyTemplate := []*pkcs11.Attribute{
		// Common storage object attributes (PKCS #11-B 10.4)
		pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
		pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true),
		pkcs11.NewAttribute(pkcs11.CKA_MODIFIABLE, false),
		pkcs11.NewAttribute(pkcs11.CKA_LABEL, label),
		// Common key attributes (PKCS #11-B 10.7)
		pkcs11.NewAttribute(pkcs11.CKA_ID, keyID),
		// Common private key attributes (PKCS #11-B 10.9)
		pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true),
		pkcs11.NewAttribute(pkcs11.CKA_DECRYPT, true),
		pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
	}

	return tk.module.GenerateKeyPair(tk.session, mechanism, publicKeyTemplate, privateKeyTemplate)
}

// Get the RSA public key using the object handle.
func (tk *Token) getRSAPublicKey(handle pkcs11.ObjectHandle) (crypto.PublicKey, error) {
	template := []*pkcs11.Attribute{
		pkcs11.NewAttribute(pkcs11.CKA_MODULUS, nil),
		pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, nil),
	}
	attrs, err := tk.module.GetAttributeValue(tk.session, handle, template)
	if err != nil {
		return nil, err
	}

	n := big.NewInt(0)
	e := int(0)
	gotModulus, gotExponent := false, false
	for _, a := range attrs {
		switch a.Type {
		case pkcs11.CKA_MODULUS:
			n.SetBytes(a.Value)
			gotModulus = true
		case pkcs11.CKA_PUBLIC_EXPONENT:
			bigE := big.NewInt(0)
			bigE.SetBytes(a.Value)
			e = int(bigE.Int64())
			gotExponent = true
		}
	}
	if !gotModulus {
		return nil, errors.New("missing public modulus")
	}
	if !gotExponent {
		return nil, errors.New("missing public exponent")
	}

	return &rsa.PublicKey{N: n, E: e}, nil
}

// GenerateKeyPair generates a key pair inside the token. For obvious
// reasons, only the public key will be returned.
//
// TODO: Support elliptic curve key pair generation.
func (tk *Token) GenerateKeyPair(label, algo string, size int) (crypto.PublicKey, error) {
	switch algo {
	case "rsa":
		handle, _, err := tk.generateRSAKeyPair(label, size)
		if err != nil {
			return nil, fmt.Errorf("failed to generate key pair: %s", err)
		}

		return tk.getRSAPublicKey(handle)
	default:
		return nil, fmt.Errorf("unsupported algorithm: %s", algo)
	}
}
<|endoftext|>


<commit_before>// +build !windows

package equalfile

import (
	"crypto/sha256"
	"testing"
)

func TestCompareLimitBroken(t *testing.T) {
	SetOptions(Options{ForceFileRead: true})
	CompareSingle()
	rejectMultiple()
	buf := make([]byte, 1000)
	compare(t, -1, nil, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, 0, buf, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, 1, buf, "/etc/passwd", "/etc/passwd", expectError) // will reach 1-byte limit
	compare(t, 1000000, buf, "/etc/passwd", "/etc/passwd", expectEqual)
}

func TestCompareBufBroken(t *testing.T) {
	SetOptions(Options{ForceFileRead: true})
	CompareSingle()
	rejectMultiple()
	var limit int64 = 1000000
	compare(t, limit, nil, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 0), "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 1), "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 2), "/etc/passwd", "/etc/passwd", expectEqual)
}

func TestCompareBufSmall(t *testing.T) {
	SetOptions(Options{ForceFileRead: true})
	CompareSingle()
	rejectMultiple()
	batch(t, 1000000, make([]byte, 10))
}

func TestCompareBufLarge(t *testing.T) {
	CompareSingle()
	rejectMultiple()
	batch(t, 100000000, make([]byte, 10000000))
}

func TestCompareLimitBrokenMultiple(t *testing.T) {
	SetOptions(Options{ForceFileRead: true})
	CompareSingle()
	rejectMultiple()
	CompareMultiple(sha256.New(), true)
	requireMultiple()
	buf := make([]byte, 1000)
	compare(t, -1, nil, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, 0, buf, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, 1, buf, "/etc/passwd", "/etc/passwd", expectError) // will reach 1-byte limit
	compare(t, 1000000, buf, "/etc/passwd", "/etc/passwd", expectEqual)
}

func TestCompareBufBrokenMultiple(t *testing.T) {
	SetOptions(Options{ForceFileRead: true})
	CompareSingle()
	rejectMultiple()
	CompareMultiple(sha256.New(), true)
	requireMultiple()
	var limit int64 = 1000000
	compare(t, limit, nil, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 0), "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 1), "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 2), "/etc/passwd", "/etc/passwd", expectEqual)
}

func TestCompareBufSmallMultiple(t *testing.T) {
	CompareSingle()
	rejectMultiple()
	CompareMultiple(sha256.New(), true)
	requireMultiple()
	batch(t, 1000000, make([]byte, 10))
}

func TestCompareBufLargeMultiple(t *testing.T) {
	CompareSingle()
	rejectMultiple()
	CompareMultiple(sha256.New(), true)
	requireMultiple()
	batch(t, 100000000, make([]byte, 10000000))
}

func batch(t *testing.T, limit int64, buf []byte) {
	SetOptions(Options{ForceFileRead: true})
	compare(t, limit, buf, "/etc", "/etc", expectError)
	compare(t, limit, buf, "/etc/ERROR", "/etc/passwd", expectError)
	compare(t, limit, buf, "/etc/passwd", "/etc/ERROR", expectError)
	compare(t, limit, buf, "/etc/passwd", "/etc/passwd", expectEqual)
	compare(t, limit, buf, "/etc/passwd", "/etc/group", expectUnequal)
	compare(t, limit, buf, "/dev/null", "/dev/null", expectEqual)
	compare(t, limit, buf, "/dev/urandom", "/dev/urandom", expectUnequal)
	compare(t, limit, buf, "/dev/zero", "/dev/zero", expectError)
}

func compare(t *testing.T, limit int64, buf []byte, path1, path2 string, expect int) {
	equal, err := CompareFileBufLimit(path1, path2, buf, limit)
	if err != nil {
		if expect != expectError {
			t.Errorf("compare: unexpected error: CompareFileBufLimit(%s,%s,%d,%d): %v", path1, path2, limit, len(buf), err)
		}
		return
	}
	if equal {
		if expect != expectEqual {
			t.Errorf("compare: unexpected equal: CompareFileBufLimit(%s,%s,%d,%d)", path1, path2, limit, len(buf))
		}
		return
	}
	if expect != expectUnequal {
		t.Errorf("compare: unexpected unequal: CompareFileBufLimit(%s,%s,%d,%d)", path1, path2, limit, len(buf))
	}
}
<commit_msg>Clean-up.<commit_after>// +build !windows

package equalfile

import (
	"crypto/sha256"
	"testing"
)

func TestCompareLimitBroken(t *testing.T) {
	SetOptions(Options{ForceFileRead: true})
	CompareSingle()
	rejectMultiple()
	buf := make([]byte, 1000)
	compare(t, -1, nil, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, 0, buf, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, 1, buf, "/etc/passwd", "/etc/passwd", expectError) // will reach 1-byte limit
	compare(t, 1000000, buf, "/etc/passwd", "/etc/passwd", expectEqual)
}

func TestCompareBufBroken(t *testing.T) {
	SetOptions(Options{ForceFileRead: true})
	CompareSingle()
	rejectMultiple()
	var limit int64 = 1000000
	compare(t, limit, nil, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 0), "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 1), "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 2), "/etc/passwd", "/etc/passwd", expectEqual)
}

func TestCompareBufSmall(t *testing.T) {
	CompareSingle()
	rejectMultiple()
	batch(t, 1000000, make([]byte, 10))
}

func TestCompareBufLarge(t *testing.T) {
	CompareSingle()
	rejectMultiple()
	batch(t, 100000000, make([]byte, 10000000))
}

func TestCompareLimitBrokenMultiple(t *testing.T) {
	SetOptions(Options{ForceFileRead: true})
	CompareSingle()
	rejectMultiple()
	CompareMultiple(sha256.New(), true)
	requireMultiple()
	buf := make([]byte, 1000)
	compare(t, -1, nil, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, 0, buf, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, 1, buf, "/etc/passwd", "/etc/passwd", expectError) // will reach 1-byte limit
	compare(t, 1000000, buf, "/etc/passwd", "/etc/passwd", expectEqual)
}

func TestCompareBufBrokenMultiple(t *testing.T) {
	SetOptions(Options{ForceFileRead: true})
	CompareSingle()
	rejectMultiple()
	CompareMultiple(sha256.New(), true)
	requireMultiple()
	var limit int64 = 1000000
	compare(t, limit, nil, "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 0), "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 1), "/etc/passwd", "/etc/passwd", expectError)
	compare(t, limit, make([]byte, 2), "/etc/passwd", "/etc/passwd", expectEqual)
}

func TestCompareBufSmallMultiple(t *testing.T) {
	CompareSingle()
	rejectMultiple()
	CompareMultiple(sha256.New(), true)
	requireMultiple()
	batch(t, 1000000, make([]byte, 10))
}

func TestCompareBufLargeMultiple(t *testing.T) {
	CompareSingle()
	rejectMultiple()
	CompareMultiple(sha256.New(), true)
	requireMultiple()
	batch(t, 100000000, make([]byte, 10000000))
}

func batch(t *testing.T, limit int64, buf []byte) {
	SetOptions(Options{ForceFileRead: true})
	compare(t, limit, buf, "/etc", "/etc", expectError)
	compare(t, limit, buf, "/etc/ERROR", "/etc/passwd", expectError)
	compare(t, limit, buf, "/etc/passwd", "/etc/ERROR", expectError)
	compare(t, limit, buf, "/etc/passwd", "/etc/passwd", expectEqual)
	compare(t, limit, buf, "/etc/passwd", "/etc/group", expectUnequal)
	compare(t, limit, buf, "/dev/null", "/dev/null", expectEqual)
	compare(t, limit, buf, "/dev/urandom", "/dev/urandom", expectUnequal)
	compare(t, limit, buf, "/dev/zero", "/dev/zero", expectError)
}

func compare(t *testing.T, limit int64, buf []byte, path1, path2 string, expect int) {
	equal, err := CompareFileBufLimit(path1, path2, buf, limit)
	if err != nil {
		if expect != expectError {
			t.Errorf("compare: unexpected error: CompareFileBufLimit(%s,%s,%d,%d): %v", path1, path2, limit, len(buf), err)
		}
		return
	}
	if equal {
		if expect != expectEqual {
			t.Errorf("compare: unexpected equal: CompareFileBufLimit(%s,%s,%d,%d)", path1, path2, limit, len(buf))
		}
		return
	}
	if expect != expectUnequal {
		t.Errorf("compare: unexpected unequal: CompareFileBufLimit(%s,%s,%d,%d)", path1, path2, limit, len(buf))
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuseops

import (
	"log"
	"time"

	"github.com/jacobsa/fuse/internal/fusekernel"
	"github.com/jacobsa/fuse/internal/fuseshim"
	"golang.org/x/net/context"
)

// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied fuse kernel message to an Op. finished will be called
// with the error supplied to o.Respond when the user invokes that method,
// before a response is sent to the kernel. o.Respond will destroy the message.
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
//
// The debug logging function and error logger may be nil.
func Convert(
	opCtx context.Context,
	m *fuseshim.Message,
	debugLogForOp func(int, string, ...interface{}),
	errorLogger *log.Logger,
	finished func(error)) (o Op) {
	var co *commonOp

	var io internalOp
	switch m.Hdr.Opcode {
	case fusekernel.OpLookup:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &LookUpInodeOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.GetattrRequest:
		to := &GetInodeAttributesOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.SetattrRequest:
		to := &SetInodeAttributesOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
		}

		if typed.Valid&fusekernel.SetattrSize != 0 {
			to.Size = &typed.Size
		}

		if typed.Valid&fusekernel.SetattrMode != 0 {
			to.Mode = &typed.Mode
		}

		if typed.Valid&fusekernel.SetattrAtime != 0 {
			to.Atime = &typed.Atime
		}

		if typed.Valid&fusekernel.SetattrMtime != 0 {
			to.Mtime = &typed.Mtime
		}

		io = to
		co = &to.commonOp

	case *fuseshim.ForgetRequest:
		to := &ForgetInodeOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
			N:     typed.N,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.MkdirRequest:
		to := &MkDirOp{
			bfReq:  typed,
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.CreateRequest:
		to := &CreateFileOp{
			bfReq:  typed,
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.SymlinkRequest:
		to := &CreateSymlinkOp{
			bfReq:  typed,
			Parent: InodeID(typed.Header.Node),
			Name:   typed.NewName,
			Target: typed.Target,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.RenameRequest:
		to := &RenameOp{
			bfReq:     typed,
			OldParent: InodeID(typed.Header.Node),
			OldName:   typed.OldName,
			NewParent: InodeID(typed.NewDir),
			NewName:   typed.NewName,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.RemoveRequest:
		if typed.Dir {
			to := &RmDirOp{
				bfReq:  typed,
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &UnlinkOp{
				bfReq:  typed,
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.OpenRequest:
		if typed.Dir {
			to := &OpenDirOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &OpenFileOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReadRequest:
		if typed.Dir {
			to := &ReadDirOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReadFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReleaseRequest:
		if typed.Dir {
			to := &ReleaseDirHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReleaseFileHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.WriteRequest:
		to := &WriteFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Data:   typed.Data,
			Offset: typed.Offset,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.FsyncRequest:
		// We don't currently support this for directories.
		if typed.Dir {
			to := &unknownOp{}
			io = to
			co = &to.commonOp
		} else {
			to := &SyncFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.FlushRequest:
		to := &FlushFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.ReadlinkRequest:
		to := &ReadSymlinkOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
		}
		io = to
		co = &to.commonOp

	default:
		to := &unknownOp{}
		io = to
		co = &to.commonOp
	}

	co.init(
		opCtx,
		io,
		r,
		debugLogForOp,
		errorLogger,
		finished)

	o = io
	return
}

func convertAttributes(
	inode InodeID,
	attr InodeAttributes,
	expiration time.Time) fuseshim.Attr {
	return fuseshim.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Nlink:  uint32(attr.Nlink),
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
		Valid:  convertExpirationTime(expiration),
	}
}

// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
	// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
	// package converts time.Duration values to this form in a straightforward
	// way (cf. http://goo.gl/FJhV8j).
	//
	// So negative durations are right out. There is no need to cap the positive
	// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
	// time.Duration.
	d = t.Sub(time.Now())
	if d < 0 {
		d = 0
	}

	return
}

func convertChildInodeEntry(
	in *ChildInodeEntry,
	out *fuseshim.LookupResponse) {
	out.Node = fuseshim.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes, in.AttributesExpiration)
	out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
<commit_msg>GetInodeAttributesOp<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuseops

import (
	"log"
	"time"

	"github.com/jacobsa/fuse/internal/fusekernel"
	"github.com/jacobsa/fuse/internal/fuseshim"
	"golang.org/x/net/context"
)

// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied fuse kernel message to an Op. finished will be called
// with the error supplied to o.Respond when the user invokes that method,
// before a response is sent to the kernel. o.Respond will destroy the message.
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
//
// The debug logging function and error logger may be nil.
func Convert(
	opCtx context.Context,
	m *fuseshim.Message,
	debugLogForOp func(int, string, ...interface{}),
	errorLogger *log.Logger,
	finished func(error)) (o Op) {
	var co *commonOp

	var io internalOp
	switch m.Hdr.Opcode {
	case fusekernel.OpLookup:
		buf := m.Bytes()
		n := len(buf)
		if n == 0 || buf[n-1] != '\x00' {
			goto corrupt
		}

		to := &LookUpInodeOp{
			Parent: InodeID(m.Header().Node),
			Name:   string(buf[:n-1]),
		}
		io = to
		co = &to.commonOp

	case fusekernel.OpGetattr:
		to := &GetInodeAttributesOp{
			Inode: InodeID(m.Header().Node),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.SetattrRequest:
		to := &SetInodeAttributesOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
		}

		if typed.Valid&fusekernel.SetattrSize != 0 {
			to.Size = &typed.Size
		}

		if typed.Valid&fusekernel.SetattrMode != 0 {
			to.Mode = &typed.Mode
		}

		if typed.Valid&fusekernel.SetattrAtime != 0 {
			to.Atime = &typed.Atime
		}

		if typed.Valid&fusekernel.SetattrMtime != 0 {
			to.Mtime = &typed.Mtime
		}

		io = to
		co = &to.commonOp

	case *fuseshim.ForgetRequest:
		to := &ForgetInodeOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
			N:     typed.N,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.MkdirRequest:
		to := &MkDirOp{
			bfReq:  typed,
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.CreateRequest:
		to := &CreateFileOp{
			bfReq:  typed,
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.SymlinkRequest:
		to := &CreateSymlinkOp{
			bfReq:  typed,
			Parent: InodeID(typed.Header.Node),
			Name:   typed.NewName,
			Target: typed.Target,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.RenameRequest:
		to := &RenameOp{
			bfReq:     typed,
			OldParent: InodeID(typed.Header.Node),
			OldName:   typed.OldName,
			NewParent: InodeID(typed.NewDir),
			NewName:   typed.NewName,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.RemoveRequest:
		if typed.Dir {
			to := &RmDirOp{
				bfReq:  typed,
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &UnlinkOp{
				bfReq:  typed,
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.OpenRequest:
		if typed.Dir {
			to := &OpenDirOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &OpenFileOp{
				bfReq: typed,
				Inode: InodeID(typed.Header.Node),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReadRequest:
		if typed.Dir {
			to := &ReadDirOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReadFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.ReleaseRequest:
		if typed.Dir {
			to := &ReleaseDirHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReleaseFileHandleOp{
				bfReq:  typed,
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.WriteRequest:
		to := &WriteFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Data:   typed.Data,
			Offset: typed.Offset,
		}
		io = to
		co = &to.commonOp

	case *fuseshim.FsyncRequest:
		// We don't currently support this for directories.
		if typed.Dir {
			to := &unknownOp{}
			io = to
			co = &to.commonOp
		} else {
			to := &SyncFileOp{
				bfReq:  typed,
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *fuseshim.FlushRequest:
		to := &FlushFileOp{
			bfReq:  typed,
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
		}
		io = to
		co = &to.commonOp

	case *fuseshim.ReadlinkRequest:
		to := &ReadSymlinkOp{
			bfReq: typed,
			Inode: InodeID(typed.Header.Node),
		}
		io = to
		co = &to.commonOp

	default:
		to := &unknownOp{}
		io = to
		co = &to.commonOp
	}

	co.init(
		opCtx,
		io,
		r,
		debugLogForOp,
		errorLogger,
		finished)

	o = io
	return
}

func convertAttributes(
	inode InodeID,
	attr InodeAttributes,
	expiration time.Time) fuseshim.Attr {
	return fuseshim.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Nlink:  uint32(attr.Nlink),
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
		Valid:  convertExpirationTime(expiration),
	}
}

// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
	// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
	// package converts time.Duration values to this form in a straightforward
	// way (cf. http://goo.gl/FJhV8j).
	//
	// So negative durations are right out. There is no need to cap the positive
	// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
	// time.Duration.
	d = t.Sub(time.Now())
	if d < 0 {
		d = 0
	}

	return
}

func convertChildInodeEntry(
	in *ChildInodeEntry,
	out *fuseshim.LookupResponse) {
	out.Node = fuseshim.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes, in.AttributesExpiration)
	out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
<|endoftext|>


<commit_before>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

package plantuml

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

	"github.com/documize/community/core/env"
	"github.com/documize/community/domain/section/provider"
	"github.com/documize/community/domain/store"
)

// Provider represents PlantUML Text Diagram
type Provider struct {
	Runtime *env.Runtime
	Store   *store.Store
}

// Meta describes us
func (*Provider) Meta() provider.TypeMeta {
	section := provider.TypeMeta{}

	section.ID = "f1067a60-45e5-40b5-89f6-aa3b03dd7f35"
	section.Title = "PlantUML Diagram"
	section.Description = "Diagrams generated from text"
	section.ContentType = "plantuml"
	section.PageType = "tab"
	section.Order = 9990

	return section
}

// Command stub.
func (p *Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	method := query.Get("method")

	if len(method) == 0 {
		provider.WriteMessage(w, "plantuml", "missing method name")
		return
	}

	switch method {
	case "preview":
		var payload struct {
			Data string `json:"data"`
		}

		defer r.Body.Close()
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			provider.WriteMessage(w, "plantuml", "Bad payload")
			return
		}

		err = json.Unmarshal(body, &payload)
		if err != nil {
			provider.WriteMessage(w, "plantuml", "Cannot unmarshal")
			return
		}

		// Generate diagram if we have data.
		var diagram string
		if len(payload.Data) > 0 {
			diagram = p.generateDiagram(ctx, payload.Data)
		}
		payload.Data = diagram

		provider.WriteJSON(w, payload)
		return
	}

	provider.WriteEmpty(w)
}

// Render returns data as-is (HTML).
func (p *Provider) Render(ctx *provider.Context, config, data string) string {
	return p.generateDiagram(ctx, data)
}

// Refresh just sends back data as-is.
func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
	return data
}

func (p *Provider) generateDiagram(ctx *provider.Context, data string) string {
	org, _ := p.Store.Organization.GetOrganization(ctx.Request, ctx.OrgID)

	var transport = &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true, // TODO should be glick.InsecureSkipVerifyTLS (from -insecure flag) but get error: x509: certificate signed by unknown authority
		}}
	client := &http.Client{Transport: transport}

	resp, _ := client.Post(org.ConversionEndpoint+"/api/plantuml", "application/text; charset=utf-8", bytes.NewReader([]byte(data)))
	defer func() {
		if e := resp.Body.Close(); e != nil {
			fmt.Println("resp.Body.Close error: " + e.Error())
		}
	}()

	png, _ := ioutil.ReadAll(resp.Body)
	pngEncoded := base64.StdEncoding.EncodeToString(png)

	return string(fmt.Sprintf("data:image/png;base64,%s", pngEncoded))
}
<commit_msg>Use latest PlantUML lib and render as SVG<commit_after>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

package plantuml

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

	"github.com/documize/community/core/env"
	"github.com/documize/community/domain/section/provider"
	"github.com/documize/community/domain/store"
)

// Provider represents PlantUML Text Diagram
type Provider struct {
	Runtime *env.Runtime
	Store   *store.Store
}

// Meta describes us
func (*Provider) Meta() provider.TypeMeta {
	section := provider.TypeMeta{}

	section.ID = "f1067a60-45e5-40b5-89f6-aa3b03dd7f35"
	section.Title = "PlantUML Diagram"
	section.Description = "Diagrams generated from text"
	section.ContentType = "plantuml"
	section.PageType = "tab"
	section.Order = 9990

	return section
}

// Command stub.
func (p *Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	method := query.Get("method")

	if len(method) == 0 {
		provider.WriteMessage(w, "plantuml", "missing method name")
		return
	}

	switch method {
	case "preview":
		var payload struct {
			Data string `json:"data"`
		}

		defer r.Body.Close()
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			provider.WriteMessage(w, "plantuml", "Bad payload")
			return
		}

		err = json.Unmarshal(body, &payload)
		if err != nil {
			provider.WriteMessage(w, "plantuml", "Cannot unmarshal")
			return
		}

		// Generate diagram if we have data.
		var diagram string
		if len(payload.Data) > 0 {
			diagram = p.generateDiagram(ctx, payload.Data)
		}
		payload.Data = diagram

		provider.WriteJSON(w, payload)
		return
	}

	provider.WriteEmpty(w)
}

// Render returns data as-is (HTML).
func (p *Provider) Render(ctx *provider.Context, config, data string) string {
	return p.generateDiagram(ctx, data)
}

// Refresh just sends back data as-is.
func (*Provider) Refresh(ctx *provider.Context, config, data string) string {
	return data
}

func (p *Provider) generateDiagram(ctx *provider.Context, data string) string {
	org, _ := p.Store.Organization.GetOrganization(ctx.Request, ctx.OrgID)

	var transport = &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true, // TODO should be glick.InsecureSkipVerifyTLS (from -insecure flag) but get error: x509: certificate signed by unknown authority
		}}
	client := &http.Client{Transport: transport}

	resp, _ := client.Post(org.ConversionEndpoint+"/api/plantuml", "application/text; charset=utf-8", bytes.NewReader([]byte(data)))
	defer func() {
		if e := resp.Body.Close(); e != nil {
			fmt.Println("resp.Body.Close error: " + e.Error())
		}
	}()

	img, _ := ioutil.ReadAll(resp.Body)
	enc := base64.StdEncoding.EncodeToString(img)

	// return string(fmt.Sprintf("data:image/png;base64,%s", enc))

	return string(fmt.Sprintf("data:image/svg+xml;base64,%s", enc))
}
<|endoftext|>