<commit_before>package smux

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

	"github.com/pkg/errors"
)

// Stream implements net.Conn
type Stream struct {
	id   uint32
	sess *Session

	buffers [][]byte
	heads   [][]byte // slice heads kept for recycle

	bufferLock sync.Mutex
	frameSize  int

	// notify a read event
	chReadEvent chan struct{}

	// flag the stream has closed
	die     chan struct{}
	dieOnce sync.Once

	// FIN
	chFinEvent   chan struct{}
	finEventOnce sync.Once

	// deadlines
	readDeadline  atomic.Value
	writeDeadline atomic.Value
}

// newStream initiates a Stream struct
func newStream(id uint32, frameSize int, sess *Session) *Stream {
	s := new(Stream)
	s.id = id
	s.chReadEvent = make(chan struct{}, 1)
	s.frameSize = frameSize
	s.sess = sess
	s.die = make(chan struct{})
	s.chFinEvent = make(chan struct{})
	return s
}

// ID returns the unique stream ID.
func (s *Stream) ID() uint32 {
	return s.id
}

// Read implements net.Conn
func (s *Stream) Read(b []byte) (n int, err error) {
	if len(b) == 0 {
		return 0, nil
	}

	for {
		s.bufferLock.Lock()
		if len(s.buffers) > 0 {
			n = copy(b, s.buffers[0])
			s.buffers[0] = s.buffers[0][n:]
			if len(s.buffers[0]) == 0 {
				s.buffers[0] = nil
				s.buffers = s.buffers[1:]
				// full recycle
				defaultAllocator.Put(s.heads[0])
				s.heads = s.heads[1:]
			}
		}
		s.bufferLock.Unlock()

		if n > 0 {
			s.sess.returnTokens(n)
			return n, nil
		}

		var timer *time.Timer
		var deadline <-chan time.Time
		if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
			timer = time.NewTimer(time.Until(d))
			deadline = timer.C
		}

		select {
		case <-s.chReadEvent:
			if timer != nil {
				timer.Stop()
			}
		case <-s.chFinEvent:
			return 0, errors.WithStack(io.EOF)
		case <-s.sess.chSocketReadError:
			return 0, s.sess.socketReadError.Load().(error)
		case <-s.sess.chProtoError:
			return 0, s.sess.protoError.Load().(error)
		case <-deadline:
			return n, errors.WithStack(errTimeout)
		case <-s.die:
			return 0, errors.WithStack(io.ErrClosedPipe)
		}
	}
}

// Write implements net.Conn
func (s *Stream) Write(b []byte) (n int, err error) {
	var deadline <-chan time.Time
	if d, ok := s.writeDeadline.Load().(time.Time); ok && !d.IsZero() {
		timer := time.NewTimer(time.Until(d))
		defer timer.Stop()
		deadline = timer.C
	}

	// check if stream has closed
	select {
	case <-s.die:
		return 0, errors.WithStack(io.ErrClosedPipe)
	default:
	}

	// frame split and transmit
	sent := 0
	frame := newFrame(cmdPSH, s.id)
	bts := b
	for len(bts) > 0 {
		sz := len(bts)
		if sz > s.frameSize {
			sz = s.frameSize
		}
		frame.data = bts[:sz]
		bts = bts[sz:]
		n, err := s.sess.writeFrameInternal(frame, deadline)
		sent += n
		if err != nil {
			return sent, errors.WithStack(err)
		}
	}

	return sent, nil
}

// Close implements net.Conn
func (s *Stream) Close() error {
	var once bool
	var err error
	s.dieOnce.Do(func() {
		close(s.die)
		once = true
	})

	if once {
		_, err = s.sess.writeFrame(newFrame(cmdFIN, s.id))
		s.sess.streamClosed(s.id)
		return err
	} else {
		return errors.WithStack(io.ErrClosedPipe)
	}
}

// GetDieCh returns a readonly chan which can be readable
// when the stream is to be closed.
func (s *Stream) GetDieCh() <-chan struct{} {
	return s.die
}

// SetReadDeadline sets the read deadline as defined by
// net.Conn.SetReadDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetReadDeadline(t time.Time) error {
	s.readDeadline.Store(t)
	return nil
}

// SetWriteDeadline sets the write deadline as defined by
// net.Conn.SetWriteDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.writeDeadline.Store(t)
	return nil
}

// SetDeadline sets both read and write deadlines as defined by
// net.Conn.SetDeadline.
// A zero time value disables the deadlines.
func (s *Stream) SetDeadline(t time.Time) error {
	if err := s.SetReadDeadline(t); err != nil {
		return errors.WithStack(err)
	}
	if err := s.SetWriteDeadline(t); err != nil {
		return errors.WithStack(err)
	}
	return nil
}

// session closes
func (s *Stream) sessionClose() { s.dieOnce.Do(func() { close(s.die) }) }

// LocalAddr satisfies net.Conn interface
func (s *Stream) LocalAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		LocalAddr() net.Addr
	}); ok {
		return ts.LocalAddr()
	}
	return nil
}

// RemoteAddr satisfies net.Conn interface
func (s *Stream) RemoteAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		RemoteAddr() net.Addr
	}); ok {
		return ts.RemoteAddr()
	}
	return nil
}

// pushBytes append buf to buffers
func (s *Stream) pushBytes(buf []byte) (written int, err error) {
	s.bufferLock.Lock()
	s.buffers = append(s.buffers, buf)
	s.heads = append(s.heads, buf)
	s.bufferLock.Unlock()
	return
}

// recycleTokens transform remaining bytes to tokens(will truncate buffer)
func (s *Stream) recycleTokens() (n int) {
	s.bufferLock.Lock()
	for k := range s.buffers {
		n += len(s.buffers[k])
		defaultAllocator.Put(s.heads[k])
	}
	s.buffers = nil
	s.heads = nil
	s.bufferLock.Unlock()
	return
}

// notify read event
func (s *Stream) notifyReadEvent() {
	select {
	case s.chReadEvent <- struct{}{}:
	default:
	}
}

// mark this stream has been closed in protocol
func (s *Stream) fin() {
	s.finEventOnce.Do(func() {
		close(s.chFinEvent)
	})
}
<commit_msg>Always stop timer in Stream.Read<commit_after>package smux

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

	"github.com/pkg/errors"
)

// Stream implements net.Conn
type Stream struct {
	id   uint32
	sess *Session

	buffers [][]byte
	heads   [][]byte // slice heads kept for recycle

	bufferLock sync.Mutex
	frameSize  int

	// notify a read event
	chReadEvent chan struct{}

	// flag the stream has closed
	die     chan struct{}
	dieOnce sync.Once

	// FIN
	chFinEvent   chan struct{}
	finEventOnce sync.Once

	// deadlines
	readDeadline  atomic.Value
	writeDeadline atomic.Value
}

// newStream initiates a Stream struct
func newStream(id uint32, frameSize int, sess *Session) *Stream {
	s := new(Stream)
	s.id = id
	s.chReadEvent = make(chan struct{}, 1)
	s.frameSize = frameSize
	s.sess = sess
	s.die = make(chan struct{})
	s.chFinEvent = make(chan struct{})
	return s
}

// ID returns the unique stream ID.
func (s *Stream) ID() uint32 {
	return s.id
}

// Read implements net.Conn
func (s *Stream) Read(b []byte) (n int, err error) {
	if len(b) == 0 {
		return 0, nil
	}

	for {
		s.bufferLock.Lock()
		if len(s.buffers) > 0 {
			n = copy(b, s.buffers[0])
			s.buffers[0] = s.buffers[0][n:]
			if len(s.buffers[0]) == 0 {
				s.buffers[0] = nil
				s.buffers = s.buffers[1:]
				// full recycle
				defaultAllocator.Put(s.heads[0])
				s.heads = s.heads[1:]
			}
		}
		s.bufferLock.Unlock()

		if n > 0 {
			s.sess.returnTokens(n)
			return n, nil
		}

		var timer *time.Timer
		var deadline <-chan time.Time
		if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
			timer = time.NewTimer(time.Until(d))
			defer timer.Stop()
			deadline = timer.C
		}

		select {
		case <-s.chReadEvent:
			continue
		case <-s.chFinEvent:
			return 0, errors.WithStack(io.EOF)
		case <-s.sess.chSocketReadError:
			return 0, s.sess.socketReadError.Load().(error)
		case <-s.sess.chProtoError:
			return 0, s.sess.protoError.Load().(error)
		case <-deadline:
			return n, errors.WithStack(errTimeout)
		case <-s.die:
			return 0, errors.WithStack(io.ErrClosedPipe)
		}
	}
}

// Write implements net.Conn
func (s *Stream) Write(b []byte) (n int, err error) {
	var deadline <-chan time.Time
	if d, ok := s.writeDeadline.Load().(time.Time); ok && !d.IsZero() {
		timer := time.NewTimer(time.Until(d))
		defer timer.Stop()
		deadline = timer.C
	}

	// check if stream has closed
	select {
	case <-s.die:
		return 0, errors.WithStack(io.ErrClosedPipe)
	default:
	}

	// frame split and transmit
	sent := 0
	frame := newFrame(cmdPSH, s.id)
	bts := b
	for len(bts) > 0 {
		sz := len(bts)
		if sz > s.frameSize {
			sz = s.frameSize
		}
		frame.data = bts[:sz]
		bts = bts[sz:]
		n, err := s.sess.writeFrameInternal(frame, deadline)
		sent += n
		if err != nil {
			return sent, errors.WithStack(err)
		}
	}

	return sent, nil
}

// Close implements net.Conn
func (s *Stream) Close() error {
	var once bool
	var err error
	s.dieOnce.Do(func() {
		close(s.die)
		once = true
	})

	if once {
		_, err = s.sess.writeFrame(newFrame(cmdFIN, s.id))
		s.sess.streamClosed(s.id)
		return err
	} else {
		return errors.WithStack(io.ErrClosedPipe)
	}
}

// GetDieCh returns a readonly chan which can be readable
// when the stream is to be closed.
func (s *Stream) GetDieCh() <-chan struct{} {
	return s.die
}

// SetReadDeadline sets the read deadline as defined by
// net.Conn.SetReadDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetReadDeadline(t time.Time) error {
	s.readDeadline.Store(t)
	return nil
}

// SetWriteDeadline sets the write deadline as defined by
// net.Conn.SetWriteDeadline.
// A zero time value disables the deadline.
func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.writeDeadline.Store(t)
	return nil
}

// SetDeadline sets both read and write deadlines as defined by
// net.Conn.SetDeadline.
// A zero time value disables the deadlines.
func (s *Stream) SetDeadline(t time.Time) error {
	if err := s.SetReadDeadline(t); err != nil {
		return errors.WithStack(err)
	}
	if err := s.SetWriteDeadline(t); err != nil {
		return errors.WithStack(err)
	}
	return nil
}

// session closes
func (s *Stream) sessionClose() { s.dieOnce.Do(func() { close(s.die) }) }

// LocalAddr satisfies net.Conn interface
func (s *Stream) LocalAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		LocalAddr() net.Addr
	}); ok {
		return ts.LocalAddr()
	}
	return nil
}

// RemoteAddr satisfies net.Conn interface
func (s *Stream) RemoteAddr() net.Addr {
	if ts, ok := s.sess.conn.(interface {
		RemoteAddr() net.Addr
	}); ok {
		return ts.RemoteAddr()
	}
	return nil
}

// pushBytes append buf to buffers
func (s *Stream) pushBytes(buf []byte) (written int, err error) {
	s.bufferLock.Lock()
	s.buffers = append(s.buffers, buf)
	s.heads = append(s.heads, buf)
	s.bufferLock.Unlock()
	return
}

// recycleTokens transform remaining bytes to tokens(will truncate buffer)
func (s *Stream) recycleTokens() (n int) {
	s.bufferLock.Lock()
	for k := range s.buffers {
		n += len(s.buffers[k])
		defaultAllocator.Put(s.heads[k])
	}
	s.buffers = nil
	s.heads = nil
	s.bufferLock.Unlock()
	return
}

// notify read event
func (s *Stream) notifyReadEvent() {
	select {
	case s.chReadEvent <- struct{}{}:
	default:
	}
}

// mark this stream has been closed in protocol
func (s *Stream) fin() {
	s.finEventOnce.Do(func() {
		close(s.chFinEvent)
	})
}
<|endoftext|>


<commit_before>package yamux

import (
	"bytes"
	"io"
	"sync"
	"time"
)

type streamState int

const (
	streamInit streamState = iota
	streamSYNSent
	streamSYNReceived
	streamEstablished
	streamLocalClose
	streamRemoteClose
	streamClosed
)

// Stream is used to represent a logical stream
// within a session.
type Stream struct {
	id      uint32
	session *Session

	state streamState
	lock  sync.Mutex

	recvBuf bytes.Buffer
	sendHdr header

	recvWindow uint32
	sendWindow uint32

	notifyCh chan struct{}

	readDeadline  time.Time
	writeDeadline time.Time
}

// newStream is used to construct a new stream within
// a given session for an ID
func newStream(session *Session, id uint32, state streamState) *Stream {
	s := &Stream{
		id:         id,
		session:    session,
		state:      state,
		sendHdr:    header(make([]byte, headerSize)),
		recvWindow: initialStreamWindow,
		sendWindow: initialStreamWindow,
		notifyCh:   make(chan struct{}, 1),
	}
	return s
}

// Session returns the associated stream session
func (s *Stream) Session() *Session {
	return s.session
}

// StreamID returns the ID of this stream
func (s *Stream) StreamID() uint32 {
	return s.id
}

// Read is used to read from the stream
func (s *Stream) Read(b []byte) (n int, err error) {
START:
	s.lock.Lock()
	switch s.state {
	case streamRemoteClose:
		fallthrough
	case streamClosed:
		if s.recvBuf.Len() == 0 {
			s.lock.Unlock()
			return 0, io.EOF
		}
	}

	// If there is no data available, block
	if s.recvBuf.Len() == 0 {
		s.lock.Unlock()
		goto WAIT
	}

	// Read any bytes
	n, _ = s.recvBuf.Read(b)

	// Send a window update potentially
	err = s.sendWindowUpdate()
	s.lock.Unlock()
	return n, err

WAIT:
	var timeout <-chan time.Time
	if !s.readDeadline.IsZero() {
		delay := s.readDeadline.Sub(time.Now())
		timeout = time.After(delay)
	}
	select {
	case <-s.notifyCh:
		goto START
	case <-timeout:
		return 0, ErrTimeout
	}
}

// Write is used to write to the stream
func (s *Stream) Write(b []byte) (n int, err error) {
	total := 0
	for total < len(b) {
		n, err := s.write(b[total:])
		total += n
		if err != nil {
			return total, err
		}
	}
	return total, nil
}

// write is used to write to the stream, may return on
// a short write.
func (s *Stream) write(b []byte) (n int, err error) {
	var flags uint16
	var max uint32
	var body io.Reader
START:
	s.lock.Lock()
	switch s.state {
	case streamLocalClose:
		fallthrough
	case streamClosed:
		s.lock.Unlock()
		return 0, ErrStreamClosed
	}

	// If there is no data available, block
	if s.sendWindow == 0 {
		s.lock.Unlock()
		goto WAIT
	}

	// Determine the flags if any
	flags = s.sendFlags()

	// Send up to our send window
	max = min(s.sendWindow, uint32(len(b)))
	body = bytes.NewReader(b[:max])

	// Send the header
	s.sendHdr.encode(typeData, flags, s.id, max)
	if err := s.session.waitForSend(s.sendHdr, body); err != nil {
		s.lock.Unlock()
		return 0, err
	}

	// Reduce our send window
	s.sendWindow -= max

	// Unlock
	s.lock.Unlock()
	return int(max), err

WAIT:
	var timeout <-chan time.Time
	if !s.writeDeadline.IsZero() {
		delay := s.writeDeadline.Sub(time.Now())
		timeout = time.After(delay)
	}
	select {
	case <-s.notifyCh:
		goto START
	case <-timeout:
		return 0, ErrTimeout
	}
	return 0, nil
}

// sendFlags determines any flags that are appropriate
// based on the current stream state
func (s *Stream) sendFlags() uint16 {
	// Determine the flags if any
	var flags uint16
	switch s.state {
	case streamInit:
		flags |= flagSYN
		s.state = streamSYNSent
	case streamSYNReceived:
		flags |= flagACK
		s.state = streamEstablished
	}
	return flags
}

// sendWindowUpdate potentially sends a window update enabling
// further writes to take place. Must be invoked with the lock.
func (s *Stream) sendWindowUpdate() error {
	// Determine the delta update
	max := s.session.config.MaxStreamWindowSize
	delta := max - s.recvWindow

	// Determine the flags if any
	flags := s.sendFlags()

	// Check if we can omit the update
	if delta < (max/2) && flags == 0 {
		return nil
	}

	// Send the header
	s.sendHdr.encode(typeWindowUpdate, flags, s.id, delta)
	if err := s.session.waitForSend(s.sendHdr, nil); err != nil {
		return err
	}

	// Update our window
	s.recvWindow += delta
	return nil
}

// sendClose is used to send a FIN
func (s *Stream) sendClose() error {
	flags := s.sendFlags()
	flags |= flagFIN
	s.sendHdr.encode(typeWindowUpdate, flags, s.id, 0)
	if err := s.session.waitForSend(s.sendHdr, nil); err != nil {
		return err
	}
	return nil
}

// Close is used to close the stream
func (s *Stream) Close() error {
	s.lock.Lock()
	defer s.lock.Unlock()

	switch s.state {
	// Local or full close means nothing to do
	case streamLocalClose:
		fallthrough
	case streamClosed:
		return nil

	// Remote close, weneed to send FIN and we are done
	case streamRemoteClose:
		s.state = streamClosed
		s.session.closeStream(s.id, false)
		s.sendClose()
		return nil

	// Opened means we need to signal a close
	case streamSYNSent:
		fallthrough
	case streamSYNReceived:
		fallthrough
	case streamEstablished:
		s.state = streamLocalClose
		s.sendClose()
		return nil
	}
	panic("unhandled state")
}

// forceClose is used for when the session is exiting
func (s *Stream) forceClose() {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.state = streamClosed
	asyncNotify(s.notifyCh)
}

// processFlags is used to update the state of the stream
// based on set flags, if any. Lock must be held
func (s *Stream) processFlags(flags uint16) error {
	if flags&flagACK == flagACK {
		if s.state == streamSYNSent {
			s.state = streamEstablished
		}

	} else if flags&flagFIN == flagFIN {
		switch s.state {
		case streamSYNSent:
			fallthrough
		case streamSYNReceived:
			fallthrough
		case streamEstablished:
			s.state = streamRemoteClose
		case streamLocalClose:
			s.state = streamClosed
			s.session.closeStream(s.id, true)
		default:
			return ErrUnexpectedFlag
		}
	} else if flags&flagRST == flagRST {
		s.state = streamClosed
		s.session.closeStream(s.id, true)
	}
	return nil
}

// incrSendWindow updates the size of our send window
func (s *Stream) incrSendWindow(hdr header, flags uint16) error {
	s.lock.Lock()
	defer s.lock.Unlock()
	if err := s.processFlags(flags); err != nil {
		return err
	}

	// Increase window, unblock a sender
	s.sendWindow += hdr.Length()
	asyncNotify(s.notifyCh)
	return nil
}

// readData is used to handle a data frame
func (s *Stream) readData(hdr header, flags uint16, conn io.Reader) error {
	s.lock.Lock()
	defer s.lock.Unlock()
	if err := s.processFlags(flags); err != nil {
		return err
	}

	// Check that our recv window is not exceeded
	length := hdr.Length()
	if length > s.recvWindow {
		return ErrRecvWindowExceeded
	}

	// Decrement the receive window
	s.recvWindow -= length

	// Wrap in a limited reader
	conn = &io.LimitedReader{R: conn, N: int64(length)}

	// Copy to our buffer
	if _, err := io.Copy(&s.recvBuf, conn); err != nil {
		return err
	}

	// Unblock any readers
	asyncNotify(s.notifyCh)
	return nil
}

// SetDeadline sets the read and write deadlines
func (s *Stream) SetDeadline(t time.Time) error {
	if err := s.SetReadDeadline(t); err != nil {
		return err
	}
	if err := s.SetWriteDeadline(t); err != nil {
		return err
	}
	return nil
}

// SetReadDeadline sets the deadline for future Read calls.
func (s *Stream) SetReadDeadline(t time.Time) error {
	s.readDeadline = t
	return nil
}

// SetWriteDeadline sets the deadline for future Write calls
func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.writeDeadline = t
	return nil
}
<commit_msg>Do not wait for sendClose<commit_after>package yamux

import (
	"bytes"
	"io"
	"sync"
	"time"
)

type streamState int

const (
	streamInit streamState = iota
	streamSYNSent
	streamSYNReceived
	streamEstablished
	streamLocalClose
	streamRemoteClose
	streamClosed
)

// Stream is used to represent a logical stream
// within a session.
type Stream struct {
	id      uint32
	session *Session

	state streamState
	lock  sync.Mutex

	recvBuf bytes.Buffer
	sendHdr header

	recvWindow uint32
	sendWindow uint32

	notifyCh chan struct{}

	readDeadline  time.Time
	writeDeadline time.Time
}

// newStream is used to construct a new stream within
// a given session for an ID
func newStream(session *Session, id uint32, state streamState) *Stream {
	s := &Stream{
		id:         id,
		session:    session,
		state:      state,
		sendHdr:    header(make([]byte, headerSize)),
		recvWindow: initialStreamWindow,
		sendWindow: initialStreamWindow,
		notifyCh:   make(chan struct{}, 1),
	}
	return s
}

// Session returns the associated stream session
func (s *Stream) Session() *Session {
	return s.session
}

// StreamID returns the ID of this stream
func (s *Stream) StreamID() uint32 {
	return s.id
}

// Read is used to read from the stream
func (s *Stream) Read(b []byte) (n int, err error) {
START:
	s.lock.Lock()
	switch s.state {
	case streamRemoteClose:
		fallthrough
	case streamClosed:
		if s.recvBuf.Len() == 0 {
			s.lock.Unlock()
			return 0, io.EOF
		}
	}

	// If there is no data available, block
	if s.recvBuf.Len() == 0 {
		s.lock.Unlock()
		goto WAIT
	}

	// Read any bytes
	n, _ = s.recvBuf.Read(b)

	// Send a window update potentially
	err = s.sendWindowUpdate()
	s.lock.Unlock()
	return n, err

WAIT:
	var timeout <-chan time.Time
	if !s.readDeadline.IsZero() {
		delay := s.readDeadline.Sub(time.Now())
		timeout = time.After(delay)
	}
	select {
	case <-s.notifyCh:
		goto START
	case <-timeout:
		return 0, ErrTimeout
	}
}

// Write is used to write to the stream
func (s *Stream) Write(b []byte) (n int, err error) {
	total := 0
	for total < len(b) {
		n, err := s.write(b[total:])
		total += n
		if err != nil {
			return total, err
		}
	}
	return total, nil
}

// write is used to write to the stream, may return on
// a short write.
func (s *Stream) write(b []byte) (n int, err error) {
	var flags uint16
	var max uint32
	var body io.Reader
START:
	s.lock.Lock()
	switch s.state {
	case streamLocalClose:
		fallthrough
	case streamClosed:
		s.lock.Unlock()
		return 0, ErrStreamClosed
	}

	// If there is no data available, block
	if s.sendWindow == 0 {
		s.lock.Unlock()
		goto WAIT
	}

	// Determine the flags if any
	flags = s.sendFlags()

	// Send up to our send window
	max = min(s.sendWindow, uint32(len(b)))
	body = bytes.NewReader(b[:max])

	// Send the header
	s.sendHdr.encode(typeData, flags, s.id, max)
	if err := s.session.waitForSend(s.sendHdr, body); err != nil {
		s.lock.Unlock()
		return 0, err
	}

	// Reduce our send window
	s.sendWindow -= max

	// Unlock
	s.lock.Unlock()
	return int(max), err

WAIT:
	var timeout <-chan time.Time
	if !s.writeDeadline.IsZero() {
		delay := s.writeDeadline.Sub(time.Now())
		timeout = time.After(delay)
	}
	select {
	case <-s.notifyCh:
		goto START
	case <-timeout:
		return 0, ErrTimeout
	}
	return 0, nil
}

// sendFlags determines any flags that are appropriate
// based on the current stream state
func (s *Stream) sendFlags() uint16 {
	// Determine the flags if any
	var flags uint16
	switch s.state {
	case streamInit:
		flags |= flagSYN
		s.state = streamSYNSent
	case streamSYNReceived:
		flags |= flagACK
		s.state = streamEstablished
	}
	return flags
}

// sendWindowUpdate potentially sends a window update enabling
// further writes to take place. Must be invoked with the lock.
func (s *Stream) sendWindowUpdate() error {
	// Determine the delta update
	max := s.session.config.MaxStreamWindowSize
	delta := max - s.recvWindow

	// Determine the flags if any
	flags := s.sendFlags()

	// Check if we can omit the update
	if delta < (max/2) && flags == 0 {
		return nil
	}

	// Send the header
	s.sendHdr.encode(typeWindowUpdate, flags, s.id, delta)
	if err := s.session.waitForSend(s.sendHdr, nil); err != nil {
		return err
	}

	// Update our window
	s.recvWindow += delta
	return nil
}

// sendClose is used to send a FIN
func (s *Stream) sendClose() error {
	flags := s.sendFlags()
	flags |= flagFIN
	s.sendHdr.encode(typeWindowUpdate, flags, s.id, 0)
	if err := s.session.sendNoWait(s.sendHdr); err != nil {
		return err
	}
	return nil
}

// Close is used to close the stream
func (s *Stream) Close() error {
	s.lock.Lock()
	defer s.lock.Unlock()

	switch s.state {
	// Local or full close means nothing to do
	case streamLocalClose:
		fallthrough
	case streamClosed:
		return nil

	// Remote close, weneed to send FIN and we are done
	case streamRemoteClose:
		s.state = streamClosed
		s.session.closeStream(s.id, false)
		s.sendClose()
		return nil

	// Opened means we need to signal a close
	case streamSYNSent:
		fallthrough
	case streamSYNReceived:
		fallthrough
	case streamEstablished:
		s.state = streamLocalClose
		s.sendClose()
		return nil
	}
	panic("unhandled state")
}

// forceClose is used for when the session is exiting
func (s *Stream) forceClose() {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.state = streamClosed
	asyncNotify(s.notifyCh)
}

// processFlags is used to update the state of the stream
// based on set flags, if any. Lock must be held
func (s *Stream) processFlags(flags uint16) error {
	if flags&flagACK == flagACK {
		if s.state == streamSYNSent {
			s.state = streamEstablished
		}

	} else if flags&flagFIN == flagFIN {
		switch s.state {
		case streamSYNSent:
			fallthrough
		case streamSYNReceived:
			fallthrough
		case streamEstablished:
			s.state = streamRemoteClose
		case streamLocalClose:
			s.state = streamClosed
			s.session.closeStream(s.id, true)
		default:
			return ErrUnexpectedFlag
		}
	} else if flags&flagRST == flagRST {
		s.state = streamClosed
		s.session.closeStream(s.id, true)
	}
	return nil
}

// incrSendWindow updates the size of our send window
func (s *Stream) incrSendWindow(hdr header, flags uint16) error {
	s.lock.Lock()
	defer s.lock.Unlock()
	if err := s.processFlags(flags); err != nil {
		return err
	}

	// Increase window, unblock a sender
	s.sendWindow += hdr.Length()
	asyncNotify(s.notifyCh)
	return nil
}

// readData is used to handle a data frame
func (s *Stream) readData(hdr header, flags uint16, conn io.Reader) error {
	s.lock.Lock()
	defer s.lock.Unlock()
	if err := s.processFlags(flags); err != nil {
		return err
	}

	// Check that our recv window is not exceeded
	length := hdr.Length()
	if length > s.recvWindow {
		return ErrRecvWindowExceeded
	}

	// Decrement the receive window
	s.recvWindow -= length

	// Wrap in a limited reader
	conn = &io.LimitedReader{R: conn, N: int64(length)}

	// Copy to our buffer
	if _, err := io.Copy(&s.recvBuf, conn); err != nil {
		return err
	}

	// Unblock any readers
	asyncNotify(s.notifyCh)
	return nil
}

// SetDeadline sets the read and write deadlines
func (s *Stream) SetDeadline(t time.Time) error {
	if err := s.SetReadDeadline(t); err != nil {
		return err
	}
	if err := s.SetWriteDeadline(t); err != nil {
		return err
	}
	return nil
}

// SetReadDeadline sets the deadline for future Read calls.
func (s *Stream) SetReadDeadline(t time.Time) error {
	s.readDeadline = t
	return nil
}

// SetWriteDeadline sets the deadline for future Write calls
func (s *Stream) SetWriteDeadline(t time.Time) error {
	s.writeDeadline = t
	return nil
}
<|endoftext|>


<commit_before>package gobus

import (
	"bytes"
	"encoding/binary"
	"io"
)

type CustomReader struct {
	Reader io.Reader
	buf8   []byte
	buf16  []byte
	buf32  []byte
	buf64  []byte
}

func NewCustomReader(reader io.Reader) *CustomReader {
	this := &CustomReader{Reader: reader}
	return this
}

func (this *CustomReader) ReadUI8() (uint8, error) {
	if this.buf8 == nil {
		this.buf8 = make([]byte, 1)
	}
	_, err := this.Reader.Read(this.buf8)
	if err != nil {
		return 0, err
	} else {
		return this.buf8[0], err
	}
}

func (this *CustomReader) ReadUI16() (uint16, error) {
	if this.buf16 == nil {
		this.buf16 = make([]byte, 2)
	}
	_, err := io.ReadFull(this.Reader, this.buf16)
	if err != nil {
		return 0, err
	}
	data := binary.LittleEndian.Uint16(this.buf16)
	return data, nil
}

func (this *CustomReader) ReadUI32() (uint32, error) {
	if this.buf32 == nil {
		this.buf32 = make([]byte, 4)
	}
	_, err := io.ReadFull(this.Reader, this.buf32)
	if err != nil {
		return 0, err
	}
	data := binary.LittleEndian.Uint32(this.buf32)
	return data, nil
}

func (this *CustomReader) ReadUI64() (uint64, error) {
	if this.buf64 == nil {
		this.buf64 = make([]byte, 8)
	}
	_, err := io.ReadFull(this.Reader, this.buf64)
	if err != nil {
		return 0, err
	}
	data := binary.LittleEndian.Uint64(this.buf64)
	return data, nil
}

func (this *CustomReader) ReadString() (string, error) {
	// reads message name size
	size, err := this.ReadUI16()
	if err != nil {
		return "", err
	}
	if size > 0 {
		in := bytes.NewBuffer(make([]byte, 0))
		_, err = io.CopyN(in, this.Reader, int64(size))
		if err != nil {
			return "", err
		}
		return string(in.Bytes()), nil
	}
	return "", nil
}

func (this *CustomReader) ReadBytes() ([]byte, error) {
	// reads message name size
	size, err := this.ReadUI32()
	if err != nil {
		return nil, err
	}
	if size > 0 {
		in := bytes.NewBuffer(make([]byte, 0))
		_, err = io.CopyN(in, this.Reader, int64(size))
		if err != nil {
			return nil, err
		}
		return in.Bytes(), nil
	}
	return nil, nil
}

// =============

type CustomWriter struct {
	Writer io.Writer
	buf8   []byte
	buf16  []byte
	buf32  []byte
	buf64  []byte
}

func NewCustomWriter(writer io.Writer) *CustomWriter {
	this := &CustomWriter{Writer: writer}
	return this
}

func (this *CustomWriter) WriteUI8(data uint8) error {
	if this.buf8 == nil {
		this.buf8 = make([]byte, 1)
	}
	this.buf8[0] = data
	_, err := this.Writer.Write(this.buf8)
	return err
}

func (this *CustomWriter) WriteUI16(data uint16) error {
	if this.buf16 == nil {
		this.buf16 = make([]byte, 2)
	}
	binary.LittleEndian.PutUint16(this.buf16, data)
	_, err := this.Writer.Write(this.buf16)
	return err
}

func (this *CustomWriter) WriteUI32(data uint32) error {
	if this.buf32 == nil {
		this.buf32 = make([]byte, 4)
	}
	binary.LittleEndian.PutUint32(this.buf32, data)
	_, err := this.Writer.Write(this.buf32)
	return err
}

func (this *CustomWriter) WriteUI64(data uint64) error {
	if this.buf64 == nil {
		this.buf64 = make([]byte, 8)
	}
	binary.LittleEndian.PutUint64(this.buf64, data)
	_, err := this.Writer.Write(this.buf64)
	return err
}

func (this *CustomWriter) WriteString(data string) error {
	// string size
	size := uint16(len(data))
	err := this.WriteUI16(size)
	if err != nil {
		return err
	}
	// string data
	if size > 0 {
		_, err = this.Writer.Write([]byte(data))
		if err != nil {
			return err
		}
	}
	return nil
}

func (this *CustomWriter) WriteBytes(data []byte) error {
	// string size
	size := uint32(len(data))
	err := this.WriteUI32(size)
	if err != nil {
		return err
	}
	// string data
	if size > 0 {
		_, err = this.Writer.Write([]byte(data))
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>total refactor<commit_after>package gomsg

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"io"
)

type InputStream struct {
	reader io.Reader
}

func NewInputStream(reader io.Reader) *InputStream {
	this := &InputStream{reader}
	return this
}

func (this *InputStream) readBytes(size int) ([]byte, error) {
	var data = make([]byte, 8)
	if _, err := io.ReadFull(this.reader, data); err != nil {
		return nil, err
	}
	return data, nil
}

func (this *InputStream) ReadUI8() (uint8, error) {
	buf, err := this.readBytes(1)
	if err != nil {
		return 0, err
	}
	return buf[0], err
}

func (this *InputStream) ReadUI16() (uint16, error) {
	buf, err := this.readBytes(2)
	if err != nil {
		return 0, err
	}
	return binary.LittleEndian.Uint16(buf), nil
}

func (this *InputStream) ReadUI32() (uint32, error) {
	buf, err := this.readBytes(4)
	if err != nil {
		return 0, err
	}
	return binary.LittleEndian.Uint32(buf), nil
}

func (this *InputStream) ReadUI64() (uint64, error) {
	buf, err := this.readBytes(8)
	if err != nil {
		return 0, err
	}
	return binary.LittleEndian.Uint64(buf), nil
}

func (this *InputStream) ReadString() (string, error) {
	return bufio.NewReader(this.reader).ReadString(0)
}

/*
func (this *InputStream) ReadString() (string, error) {
	data, err := this.ReadBytes()
	if err != nil {
		return "", err
	}
	return string(data), nil
}
*/

func (this *InputStream) ReadBytes() ([]byte, error) {
	// reads byte array size
	size, err := this.ReadUI16()
	if err != nil {
		return nil, err
	}
	if size > 0 {
		in := bytes.NewBuffer(make([]byte, 0))
		_, err = io.CopyN(in, this.reader, int64(size))
		if err != nil {
			return nil, err
		}
		return in.Bytes(), nil
	}
	return nil, nil
}

// =============

type OutputStream struct {
	writer io.Writer
}

func NewOutputStream(writer io.Writer) *OutputStream {
	this := &OutputStream{writer}
	return this
}

func (this *OutputStream) WriteUI8(data uint8) error {
	var buf8 = make([]byte, 1)
	buf8[0] = data
	_, err := this.writer.Write(buf8)
	return err
}

func (this *OutputStream) WriteUI16(data uint16) error {
	var buf16 = make([]byte, 2)
	binary.LittleEndian.PutUint16(buf16, data)
	_, err := this.writer.Write(buf16)
	return err
}

func (this *OutputStream) WriteUI32(data uint32) error {
	var buf32 = make([]byte, 4)
	binary.LittleEndian.PutUint32(buf32, data)
	_, err := this.writer.Write(buf32)
	return err
}

func (this *OutputStream) WriteUI64(data uint64) error {
	var buf64 = make([]byte, 8)
	binary.LittleEndian.PutUint64(buf64, data)
	_, err := this.writer.Write(buf64)
	return err
}

func (this *OutputStream) WriteString(s string) error {
	data := append([]byte(s), 0)
	_, err := this.writer.Write([]byte(data))
	if err != nil {
		return err
	}
	return nil
}

/*
func (this *OutputStream) WriteString(data string) error {
	return this.WriteBytes([]byte(data))
}
*/

func (this *OutputStream) WriteBytes(data []byte) error {
	// array size
	size := uint16(len(data))
	err := this.WriteUI16(size)
	if err != nil {
		return err
	}
	// string data
	if size > 0 {
		_, err = this.writer.Write(data)
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Package stripe provides a client for Stripe REST APIs.
package stripe

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// uri is the public Stripe URL for APIs.
const uri = "https://api.stripe.com/v1"

// apiversion is the currently supported API version
const apiversion = "2014-08-20"

// clientversion is the binding version
const clientversion = "1.0"

// debug is a global variable that enables additional tracing
// to help with troubleshooting while testing.
var debug bool

// Api is an interface for making calls against a Stripe service.
// This interface exists to enable mocking for during testing if needed.
type Api interface {
	Call(method, path, token string, body *url.Values, v interface{}) error
}

// Client is the Stripe client. It contains all the different resources available.
type Client struct {
	// Token is the secret key used for authentication.
	Token string
	// api is the Api implementation used to invoke Stripe APIs.
	api Api
	// Charges is the client used to invoke /charges APIs.
	// For more details see https://stripe.com/docs/api/#charges.
	Charges *ChargeClient
	// Customers is the client used to invoke /customers APIs.
	// For more details see https://stripe.com/docs/api/#customers.
	Customers *CustomerClient
	// Cards is the client used to invoke /cards APIs.
	// For more details see https://stripe.com/docs/api/#cards.
	Cards *CardClient
	// Subs is the client used to invoke /subscriptions APIs.
	// For more details see https://stripe.com/docs/api/#subscriptions.
	Subs *SubscriptionClient
	// Plans is the client used to invoke /plans APIs.
	// For more details see https://stripe.com/docs/api/#plans.
	Plans *PlanClient
	// Coupons is the client used to invoke /coupons APIs.
	// For more details see https://stripe.com/docs/api/#coupons.
	Coupons *CouponClient
	// Discounts is the client used to invoke discount-related APIs.
	// For mode details see https://stripe.com/docs/api/#discounts.
	Discounts *DiscountClient
	// Invoices is the client used to invoke /invoices APIs.
	// For more details see https://stripe.com/docs/api/#invoices.
	Invoices *InvoiceClient
	// InvoiceItems is the client used to invoke /invoiceitems APIs.
	// For more details see https://stripe.com/docs/api/#invoiceitems.
	InvoiceItems *InvoiceItemClient
	// Disputes is the client used to invoke dispute-related APIs.
	// For more details see https://stripe.com/docs/api/#disputes.
	Disputes *DisputeClient
	// Transfers is the client used to invoke /transfers APIs.
	// For more details see https://stripe.com/docs/api/#transfers.
	Transfers *TransferClient
	// Recipients is the client used to invoke /recipients APIs.
	// For more details see https://stripe.com/docs/api/#recipients.
	Recipients *RecipientClient
	// Refunds is the client used to invoke /refunds APIs.
	// For more details see https://stripe.com/docs/api#refunds.
	Refunds *RefundClient
	// Fees is the client used to invoke /application_fees APIs.
	// For more details see https://stripe.com/docs/api/#application_fees.
	Fees *AppFeeClient
	// FeeRefunds is the client used to invoke /application_fees/refunds APIs.
	// For more details see https://stripe.com/docs/api/#fee_refundss.
	FeeRefunds *FeeRefundClient
	// Account is the client used to invoke /account APIs.
	// For more details see https://stripe.com/docs/api/#account.
	Account *AccountClient
	// Balance is the client used to invoke /balance and transaction-related APIs.
	// For more details see https://stripe.com/docs/api/#balance.
	Balance *BalanceClient
	// Events is the client used to invoke /events APIs.
	// For more details see https://stripe.com/docs/api#events.
	Events *EventClient
	// Tokens is the client used to invoke /tokens APIs.
	// For more details see https://stripe.com/docs/api/#tokens.
	Tokens *TokenClient
}

// s is the internal implementation for making HTTP calls to Stripe.
type s struct {
	httpClient *http.Client
}

// Filters is a structure that contains a collection of filters for list-related APIs.
type Filters struct {
	f []*filter
}

// filter is the structure that contains a filter for list-related APIs.
// It ends up passing query string parameters in the format key[op]=value.
type filter struct {
	Key, Op, Val string
}

// ListResponse is the structure that contains the common properties
// of any *List structure.
type ListResponse struct {
	Count uint16 `json:"total_count"`
	More  bool   `json:"has_more"`
	Url   string `json:"url"`
}

// Init initializes the Stripe client with the appropriate token secret key
// as well as providing the ability to override the HTTP client and api used.
func (c *Client) Init(token string, client *http.Client, api Api) {
	if client == nil {
		client = http.DefaultClient
	}

	if api == nil {
		api = &s{httpClient: client}
	}
	c.api = api
	c.Token = token

	c.Charges = &ChargeClient{api: c.api, token: c.Token}
	c.Customers = &CustomerClient{api: c.api, token: c.Token}
	c.Cards = &CardClient{api: c.api, token: c.Token}
	c.Subs = &SubscriptionClient{api: c.api, token: c.Token}
	c.Plans = &PlanClient{api: c.api, token: c.Token}
	c.Coupons = &CouponClient{api: c.api, token: c.Token}
	c.Discounts = &DiscountClient{api: c.api, token: c.Token}
	c.Invoices = &InvoiceClient{api: c.api, token: c.Token}
	c.InvoiceItems = &InvoiceItemClient{api: c.api, token: c.Token}
	c.Disputes = &DisputeClient{api: c.api, token: c.Token}
	c.Transfers = &TransferClient{api: c.api, token: c.Token}
	c.Recipients = &RecipientClient{api: c.api, token: c.Token}
	c.Refunds = &RefundClient{api: c.api, token: c.Token}
	c.Fees = &AppFeeClient{api: c.api, token: c.Token}
	c.FeeRefunds = &FeeRefundClient{api: c.api, token: c.Token}
	c.Account = &AccountClient{api: c.api, token: c.Token}
	c.Balance = &BalanceClient{api: c.api, token: c.Token}
	c.Events = &EventClient{api: c.api, token: c.Token}
	c.Tokens = &TokenClient{api: c.api, token: c.Token}
}

// SetDebug enables additional tracing globally.
// The method is designed for used during testing.
func (c *Client) SetDebug(value bool) {
	debug = value
}

// Call is the Api.Call implementation for invoking Stripe APIs.
func (s *s) Call(method, path, token string, body *url.Values, v interface{}) error {
	if !strings.HasPrefix(path, "/") {
		path = "/" + path
	}

	path = uri + path

	if body != nil && len(*body) > 0 {
		path += "?" + body.Encode()
	}

	req, err := http.NewRequest(method, path, nil)
	if err != nil {
		log.Printf("Cannot create Stripe request: %v\n", err)
		return err
	}

	req.SetBasicAuth(token, "")
	req.Header.Add("Stripe-Version", apiversion)
	req.Header.Add("X-Stripe-Client-User-Agent", "Stripe.Go-"+clientversion)

	log.Printf("Requesting %v %q\n", method, path)
	start := time.Now()

	res, err := s.httpClient.Do(req)

	if debug {
		log.Printf("Completed in %v\n", time.Since(start))
	}

	if err != nil {
		log.Printf("Request to Stripe failed: %v\n", err)
		return err
	}
	defer res.Body.Close()

	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Printf("Cannot parse Stripe response: %v\n", err)
		return err
	}

	if res.StatusCode >= 400 {
		// for some odd reason, the Erro structure doesn't unmarshal
		// initially I thought it was because it's a struct inside of a struct
		// but even after trying that, it still didn't work
		// so unmarshalling to a map for now and parsing the results manually
		// but should investigate later
		var errMap map[string]interface{}
		json.Unmarshal(resBody, &errMap)

		if e, found := errMap["error"]; !found {
			err := errors.New(string(resBody))
			log.Printf("Unparsable error returned from Stripe: %v\n", err)
			return err
		} else {
			root := e.(map[string]interface{})
			err := &Error{
				Type: ErrorType(root["type"].(string)),
				Msg:  root["message"].(string),
			}

			if code, found := root["code"]; found {
				err.Code = ErrorCode(code.(string))
			}

			if param, found := root["param"]; found {
				err.Param = param.(string)
			}

			log.Printf("Error encountered from Stripe: %v\n", err)
			return err
		}
	}

	if debug {
		log.Printf("Stripe Response: %q\n", resBody)
	}

	if v != nil {
		return json.Unmarshal(resBody, v)
	}

	return nil
}

// AddFilter adds a new filter with a given key, op and value.
func (f *Filters) AddFilter(key, op, value string) {
	filter := &filter{Key: key, Op: op, Val: value}
	f.f = append(f.f, filter)
}

// appendTo adds the list of filters to the query string values.
func (f *Filters) appendTo(values *url.Values) {
	for _, v := range f.f {
		values.Add(fmt.Sprintf("%v[%v]", v.Key, v.Op), v.Val)
	}
}
<commit_msg>Clarify that Count isn't always populated<commit_after>// Package stripe provides a client for Stripe REST APIs.
package stripe

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// uri is the public Stripe URL for APIs.
const uri = "https://api.stripe.com/v1"

// apiversion is the currently supported API version
const apiversion = "2014-08-20"

// clientversion is the binding version
const clientversion = "1.0"

// debug is a global variable that enables additional tracing
// to help with troubleshooting while testing.
var debug bool

// Api is an interface for making calls against a Stripe service.
// This interface exists to enable mocking for during testing if needed.
type Api interface {
	Call(method, path, token string, body *url.Values, v interface{}) error
}

// Client is the Stripe client. It contains all the different resources available.
type Client struct {
	// Token is the secret key used for authentication.
	Token string
	// api is the Api implementation used to invoke Stripe APIs.
	api Api
	// Charges is the client used to invoke /charges APIs.
	// For more details see https://stripe.com/docs/api/#charges.
	Charges *ChargeClient
	// Customers is the client used to invoke /customers APIs.
	// For more details see https://stripe.com/docs/api/#customers.
	Customers *CustomerClient
	// Cards is the client used to invoke /cards APIs.
	// For more details see https://stripe.com/docs/api/#cards.
	Cards *CardClient
	// Subs is the client used to invoke /subscriptions APIs.
	// For more details see https://stripe.com/docs/api/#subscriptions.
	Subs *SubscriptionClient
	// Plans is the client used to invoke /plans APIs.
	// For more details see https://stripe.com/docs/api/#plans.
	Plans *PlanClient
	// Coupons is the client used to invoke /coupons APIs.
	// For more details see https://stripe.com/docs/api/#coupons.
	Coupons *CouponClient
	// Discounts is the client used to invoke discount-related APIs.
	// For mode details see https://stripe.com/docs/api/#discounts.
	Discounts *DiscountClient
	// Invoices is the client used to invoke /invoices APIs.
	// For more details see https://stripe.com/docs/api/#invoices.
	Invoices *InvoiceClient
	// InvoiceItems is the client used to invoke /invoiceitems APIs.
	// For more details see https://stripe.com/docs/api/#invoiceitems.
	InvoiceItems *InvoiceItemClient
	// Disputes is the client used to invoke dispute-related APIs.
	// For more details see https://stripe.com/docs/api/#disputes.
	Disputes *DisputeClient
	// Transfers is the client used to invoke /transfers APIs.
	// For more details see https://stripe.com/docs/api/#transfers.
	Transfers *TransferClient
	// Recipients is the client used to invoke /recipients APIs.
	// For more details see https://stripe.com/docs/api/#recipients.
	Recipients *RecipientClient
	// Refunds is the client used to invoke /refunds APIs.
	// For more details see https://stripe.com/docs/api#refunds.
	Refunds *RefundClient
	// Fees is the client used to invoke /application_fees APIs.
	// For more details see https://stripe.com/docs/api/#application_fees.
	Fees *AppFeeClient
	// FeeRefunds is the client used to invoke /application_fees/refunds APIs.
	// For more details see https://stripe.com/docs/api/#fee_refundss.
	FeeRefunds *FeeRefundClient
	// Account is the client used to invoke /account APIs.
	// For more details see https://stripe.com/docs/api/#account.
	Account *AccountClient
	// Balance is the client used to invoke /balance and transaction-related APIs.
	// For more details see https://stripe.com/docs/api/#balance.
	Balance *BalanceClient
	// Events is the client used to invoke /events APIs.
	// For more details see https://stripe.com/docs/api#events.
	Events *EventClient
	// Tokens is the client used to invoke /tokens APIs.
	// For more details see https://stripe.com/docs/api/#tokens.
	Tokens *TokenClient
}

// s is the internal implementation for making HTTP calls to Stripe.
type s struct {
	httpClient *http.Client
}

// Filters is a structure that contains a collection of filters for list-related APIs.
type Filters struct {
	f []*filter
}

// filter is the structure that contains a filter for list-related APIs.
// It ends up passing query string parameters in the format key[op]=value.
type filter struct {
	Key, Op, Val string
}

// ListResponse is the structure that contains the common properties
// of any *List structure. The Count property is only populated if the
// total_count include option is passed in (see tests for example).
type ListResponse struct {
	Count uint16 `json:"total_count"`
	More  bool   `json:"has_more"`
	Url   string `json:"url"`
}

// Init initializes the Stripe client with the appropriate token secret key
// as well as providing the ability to override the HTTP client and api used.
func (c *Client) Init(token string, client *http.Client, api Api) {
	if client == nil {
		client = http.DefaultClient
	}

	if api == nil {
		api = &s{httpClient: client}
	}
	c.api = api
	c.Token = token

	c.Charges = &ChargeClient{api: c.api, token: c.Token}
	c.Customers = &CustomerClient{api: c.api, token: c.Token}
	c.Cards = &CardClient{api: c.api, token: c.Token}
	c.Subs = &SubscriptionClient{api: c.api, token: c.Token}
	c.Plans = &PlanClient{api: c.api, token: c.Token}
	c.Coupons = &CouponClient{api: c.api, token: c.Token}
	c.Discounts = &DiscountClient{api: c.api, token: c.Token}
	c.Invoices = &InvoiceClient{api: c.api, token: c.Token}
	c.InvoiceItems = &InvoiceItemClient{api: c.api, token: c.Token}
	c.Disputes = &DisputeClient{api: c.api, token: c.Token}
	c.Transfers = &TransferClient{api: c.api, token: c.Token}
	c.Recipients = &RecipientClient{api: c.api, token: c.Token}
	c.Refunds = &RefundClient{api: c.api, token: c.Token}
	c.Fees = &AppFeeClient{api: c.api, token: c.Token}
	c.FeeRefunds = &FeeRefundClient{api: c.api, token: c.Token}
	c.Account = &AccountClient{api: c.api, token: c.Token}
	c.Balance = &BalanceClient{api: c.api, token: c.Token}
	c.Events = &EventClient{api: c.api, token: c.Token}
	c.Tokens = &TokenClient{api: c.api, token: c.Token}
}

// SetDebug enables additional tracing globally.
// The method is designed for used during testing.
func (c *Client) SetDebug(value bool) {
	debug = value
}

// Call is the Api.Call implementation for invoking Stripe APIs.
func (s *s) Call(method, path, token string, body *url.Values, v interface{}) error {
	if !strings.HasPrefix(path, "/") {
		path = "/" + path
	}

	path = uri + path

	if body != nil && len(*body) > 0 {
		path += "?" + body.Encode()
	}

	req, err := http.NewRequest(method, path, nil)
	if err != nil {
		log.Printf("Cannot create Stripe request: %v\n", err)
		return err
	}

	req.SetBasicAuth(token, "")
	req.Header.Add("Stripe-Version", apiversion)
	req.Header.Add("X-Stripe-Client-User-Agent", "Stripe.Go-"+clientversion)

	log.Printf("Requesting %v %q\n", method, path)
	start := time.Now()

	res, err := s.httpClient.Do(req)

	if debug {
		log.Printf("Completed in %v\n", time.Since(start))
	}

	if err != nil {
		log.Printf("Request to Stripe failed: %v\n", err)
		return err
	}
	defer res.Body.Close()

	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Printf("Cannot parse Stripe response: %v\n", err)
		return err
	}

	if res.StatusCode >= 400 {
		// for some odd reason, the Erro structure doesn't unmarshal
		// initially I thought it was because it's a struct inside of a struct
		// but even after trying that, it still didn't work
		// so unmarshalling to a map for now and parsing the results manually
		// but should investigate later
		var errMap map[string]interface{}
		json.Unmarshal(resBody, &errMap)

		if e, found := errMap["error"]; !found {
			err := errors.New(string(resBody))
			log.Printf("Unparsable error returned from Stripe: %v\n", err)
			return err
		} else {
			root := e.(map[string]interface{})
			err := &Error{
				Type: ErrorType(root["type"].(string)),
				Msg:  root["message"].(string),
			}

			if code, found := root["code"]; found {
				err.Code = ErrorCode(code.(string))
			}

			if param, found := root["param"]; found {
				err.Param = param.(string)
			}

			log.Printf("Error encountered from Stripe: %v\n", err)
			return err
		}
	}

	if debug {
		log.Printf("Stripe Response: %q\n", resBody)
	}

	if v != nil {
		return json.Unmarshal(resBody, v)
	}

	return nil
}

// AddFilter adds a new filter with a given key, op and value.
func (f *Filters) AddFilter(key, op, value string) {
	filter := &filter{Key: key, Op: op, Val: value}
	f.f = append(f.f, filter)
}

// appendTo adds the list of filters to the query string values.
func (f *Filters) appendTo(values *url.Values) {
	for _, v := range f.f {
		values.Add(fmt.Sprintf("%v[%v]", v.Key, v.Op), v.Val)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cloudup

import (
	"fmt"
	"net/url"

	"github.com/blang/semver/v4"

	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/assets"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/util/pkg/architectures"
	"k8s.io/kops/util/pkg/hashing"
)

const (
	// containerd packages URLs for v1.4.x+
	containerdVersionUrlAmd64 = "https://github.com/containerd/containerd/releases/download/v%s/cri-containerd-cni-%s-linux-amd64.tar.gz"
	// containerd legacy packages URLs for v1.2.x and v1.3.x
	containerdLegacyUrlAmd64 = "https://storage.googleapis.com/cri-containerd-release/cri-containerd-%s.linux-amd64.tar.gz"
	// containerd version that is available for both AMD64 and ARM64, used in case the selected version is not available for ARM64
	containerdFallbackVersion = "1.4.6"
)

func findContainerdAsset(c *kops.Cluster, assetBuilder *assets.AssetBuilder, arch architectures.Architecture) (*url.URL, *hashing.Hash, error) {
	if c.Spec.Containerd == nil {
		return nil, nil, fmt.Errorf("unable to find containerd config")
	}
	containerd := c.Spec.Containerd

	if containerd.Packages != nil {
		if arch == architectures.ArchitectureAmd64 && containerd.Packages.UrlAmd64 != nil && containerd.Packages.HashAmd64 != nil {
			assetUrl := fi.StringValue(containerd.Packages.UrlAmd64)
			assetHash := fi.StringValue(containerd.Packages.HashAmd64)
			return findAssetsUrlHash(assetBuilder, assetUrl, assetHash)
		}
		if arch == architectures.ArchitectureArm64 && containerd.Packages.UrlArm64 != nil && containerd.Packages.HashArm64 != nil {
			assetUrl := fi.StringValue(containerd.Packages.UrlArm64)
			assetHash := fi.StringValue(containerd.Packages.HashArm64)
			return findAssetsUrlHash(assetBuilder, assetUrl, assetHash)
		}
	}

	version := fi.StringValue(containerd.Version)
	if version == "" {
		return nil, nil, fmt.Errorf("unable to find containerd version")
	}
	assetUrl, assetHash, err := findContainerdVersionUrlHash(arch, version)
	if err != nil {
		return nil, nil, err
	}

	return findAssetsUrlHash(assetBuilder, assetUrl, assetHash)
}

func findContainerdVersionUrlHash(arch architectures.Architecture, version string) (u string, h string, e error) {
	var containerdAssetUrl, containerdAssetHash string

	if findAllContainerdHashesAmd64()[version] != "" {
		var err error
		containerdAssetUrl, err = findContainerdVersionUrl(arch, version)
		if err != nil {
			return "", "", err
		}
		containerdAssetHash, err = findContainerdVersionHash(arch, version)
		if err != nil {
			return "", "", err
		}
	} else {
		// Fall back to Docker packages
		dv := findAllContainerdDockerMappings()[version]
		if dv != "" {
			var err error
			containerdAssetUrl, err = findDockerVersionUrl(arch, dv)
			if err != nil {
				return "", "", err
			}
			containerdAssetHash, err = findDockerVersionHash(arch, dv)
			if err != nil {
				return "", "", err
			}
			println(dv)
		} else {
			return "", "", fmt.Errorf("unknown url and hash for containerd version: %s - %s", arch, version)
		}
	}

	return containerdAssetUrl, containerdAssetHash, nil
}

func findContainerdVersionUrl(arch architectures.Architecture, version string) (string, error) {
	sv, err := semver.ParseTolerant(version)
	if err != nil {
		return "", fmt.Errorf("unable to parse version string: %q", version)
	}
	if sv.LT(semver.MustParse("1.3.4")) {
		return "", fmt.Errorf("unsupported legacy containerd version: %q", version)
	}

	var u string
	switch arch {
	case architectures.ArchitectureAmd64:
		if sv.GTE(semver.MustParse("1.3.8")) {
			u = fmt.Sprintf(containerdVersionUrlAmd64, version, version)
		} else {
			u = fmt.Sprintf(containerdLegacyUrlAmd64, version)
		}
	case architectures.ArchitectureArm64:
		// For now there are only official AMD64 builds, always using fallback Docker version instead
		if findAllContainerdHashesAmd64()[version] != "" {
			if findAllContainerdDockerMappings()[version] != "" {
				u = fmt.Sprintf(dockerVersionUrlArm64, findAllContainerdDockerMappings()[version])
			} else {
				u = fmt.Sprintf(dockerVersionUrlArm64, findAllContainerdDockerMappings()[containerdFallbackVersion])
			}
		}
	default:
		return "", fmt.Errorf("unknown arch: %q", arch)
	}

	if u == "" {
		return "", fmt.Errorf("unknown url for containerd version: %s - %s", arch, version)
	}

	return u, nil
}

func findContainerdVersionHash(arch architectures.Architecture, version string) (string, error) {
	sv, err := semver.ParseTolerant(version)
	if err != nil {
		return "", fmt.Errorf("unable to parse version string: %q", version)
	}
	if sv.LT(semver.MustParse("1.3.4")) {
		return "", fmt.Errorf("unsupported legacy containerd version: %q", version)
	}

	var h string
	switch arch {
	case architectures.ArchitectureAmd64:
		h = findAllContainerdHashesAmd64()[version]
	case architectures.ArchitectureArm64:
		// For now there are only official AMD64 builds, always using fallback Docker version instead
		if findAllContainerdHashesAmd64()[version] != "" {
			if findAllContainerdDockerMappings()[version] != "" {
				h = findAllDockerHashesArm64()[findAllContainerdDockerMappings()[version]]
			} else {
				h = findAllDockerHashesArm64()[findAllContainerdDockerMappings()[containerdFallbackVersion]]
			}
		}
	default:
		return "", fmt.Errorf("unknown arch: %q", arch)
	}

	if h == "" {
		return "", fmt.Errorf("unknown hash for containerd version: %s - %s", arch, version)
	}

	return h, nil
}

func findAllContainerdHashesAmd64() map[string]string {
	hashes := map[string]string{
		"1.3.4":  "4616971c3ad21c24f2f2320fa1c085577a91032a068dd56a41c7c4b71a458087",
		"1.3.9":  "96663699e0f888fbf232ae6629a367aa7421f6b95044e7ee5d4d4e02841fac75",
		"1.3.10": "69e23e49cdf1232d475a77bf7ecd7145ff4a80295154e190125c4d8a20e241da",
		"1.4.0":  "b379f29417efd583f77e095173d4d0bd6bb001f0081b2a63d152ee7aef653ce1",
		"1.4.1":  "757efb93a4f3161efc447a943317503d8a7ded5cb4cc0cba3f3318d7ce1542ed",
		"1.4.2":  "9d0fd5f4d2bc58b345728432b7daac75fc99c1da91afa4f41e6103f618e74012",
		"1.4.3":  "2697a342e3477c211ab48313e259fd7e32ad1f5ded19320e6a559f50a82bff3d",
		"1.4.4":  "96641849cb78a0a119223a427dfdc1ade88412ef791a14193212c8c8e29d447b",
		"1.4.5":  "f8155278fd256526ca9804219e1ee46f5db11c6ddf455086b04c0887c868822a",
		"1.4.6":  "6ae4763598c9583f8b50605f19d6c7e9ef93c216706465e73dfc84ee6b63a238",
		"1.4.7":  "daa14638344fe0772f645e190e4d8eb9549b52743364cb8000521490f9e410b8",
		"1.4.8":  "96e815c9ab664a02dd5be35e31d15890ea6bef04dfaa39f99f14676c3d6561e8",
		"1.4.9":  "9911479f86012d6eab7e0f532da8f807a8b0f555ee09ef89367d8c31243073bb",
		"1.4.10": "5b256b372a02fd37c84939c87a6a81cae06058a7881a60d682525c11f6dea7d1",
		"1.5.0":  "aee7b553ab88842fdafe43955757abe746b8e9995b2be55c603f0a236186ff9b",
		"1.5.1":  "2fd97916b24396c13849cfcd89805170e1ef0265a2f7fce8e74ae044a6a6a169",
		"1.5.2":  "e7adbb6c6f6e67639460579a8aa991e9ce4de2062ed36d3261e6e4865574d947",
		"1.5.3":  "32a9bf1b7ab2adbd9d2a16b17bf1aa6e61592938655adfb5114c40d527aa9be7",
		"1.5.4":  "591e4e087ea2f5007e6c64deb382df58d419b7b6922eab45a1923d843d57615f",
		"1.5.5":  "45f02cfc65db47cf088c95555906e1dcba7baf5a3fbad3d947dd6b9af476a144",
		"1.5.6":  "afc51718ebe46cb9b985edac816e63fe86c07e37d28cdd21b2c0302dec6fa7ae",
	}

	return hashes
}

func findAllContainerdDockerMappings() map[string]string {
	versions := map[string]string{
		"1.3.7": "19.03.13",
		"1.3.9": "19.03.14",
		"1.4.3": "20.10.0",
		"1.4.4": "20.10.6",
		"1.4.6": "20.10.7",
		"1.4.9": "20.10.8",
	}

	return versions
}
<commit_msg>Add hash for containerd v1.5.7<commit_after>/*
Copyright 2020 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cloudup

import (
	"fmt"
	"net/url"

	"github.com/blang/semver/v4"

	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/pkg/assets"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/util/pkg/architectures"
	"k8s.io/kops/util/pkg/hashing"
)

const (
	// containerd packages URLs for v1.4.x+
	containerdVersionUrlAmd64 = "https://github.com/containerd/containerd/releases/download/v%s/cri-containerd-cni-%s-linux-amd64.tar.gz"
	// containerd legacy packages URLs for v1.2.x and v1.3.x
	containerdLegacyUrlAmd64 = "https://storage.googleapis.com/cri-containerd-release/cri-containerd-%s.linux-amd64.tar.gz"
	// containerd version that is available for both AMD64 and ARM64, used in case the selected version is not available for ARM64
	containerdFallbackVersion = "1.4.6"
)

func findContainerdAsset(c *kops.Cluster, assetBuilder *assets.AssetBuilder, arch architectures.Architecture) (*url.URL, *hashing.Hash, error) {
	if c.Spec.Containerd == nil {
		return nil, nil, fmt.Errorf("unable to find containerd config")
	}
	containerd := c.Spec.Containerd

	if containerd.Packages != nil {
		if arch == architectures.ArchitectureAmd64 && containerd.Packages.UrlAmd64 != nil && containerd.Packages.HashAmd64 != nil {
			assetUrl := fi.StringValue(containerd.Packages.UrlAmd64)
			assetHash := fi.StringValue(containerd.Packages.HashAmd64)
			return findAssetsUrlHash(assetBuilder, assetUrl, assetHash)
		}
		if arch == architectures.ArchitectureArm64 && containerd.Packages.UrlArm64 != nil && containerd.Packages.HashArm64 != nil {
			assetUrl := fi.StringValue(containerd.Packages.UrlArm64)
			assetHash := fi.StringValue(containerd.Packages.HashArm64)
			return findAssetsUrlHash(assetBuilder, assetUrl, assetHash)
		}
	}

	version := fi.StringValue(containerd.Version)
	if version == "" {
		return nil, nil, fmt.Errorf("unable to find containerd version")
	}
	assetUrl, assetHash, err := findContainerdVersionUrlHash(arch, version)
	if err != nil {
		return nil, nil, err
	}

	return findAssetsUrlHash(assetBuilder, assetUrl, assetHash)
}

func findContainerdVersionUrlHash(arch architectures.Architecture, version string) (u string, h string, e error) {
	var containerdAssetUrl, containerdAssetHash string

	if findAllContainerdHashesAmd64()[version] != "" {
		var err error
		containerdAssetUrl, err = findContainerdVersionUrl(arch, version)
		if err != nil {
			return "", "", err
		}
		containerdAssetHash, err = findContainerdVersionHash(arch, version)
		if err != nil {
			return "", "", err
		}
	} else {
		// Fall back to Docker packages
		dv := findAllContainerdDockerMappings()[version]
		if dv != "" {
			var err error
			containerdAssetUrl, err = findDockerVersionUrl(arch, dv)
			if err != nil {
				return "", "", err
			}
			containerdAssetHash, err = findDockerVersionHash(arch, dv)
			if err != nil {
				return "", "", err
			}
			println(dv)
		} else {
			return "", "", fmt.Errorf("unknown url and hash for containerd version: %s - %s", arch, version)
		}
	}

	return containerdAssetUrl, containerdAssetHash, nil
}

func findContainerdVersionUrl(arch architectures.Architecture, version string) (string, error) {
	sv, err := semver.ParseTolerant(version)
	if err != nil {
		return "", fmt.Errorf("unable to parse version string: %q", version)
	}
	if sv.LT(semver.MustParse("1.3.4")) {
		return "", fmt.Errorf("unsupported legacy containerd version: %q", version)
	}

	var u string
	switch arch {
	case architectures.ArchitectureAmd64:
		if sv.GTE(semver.MustParse("1.3.8")) {
			u = fmt.Sprintf(containerdVersionUrlAmd64, version, version)
		} else {
			u = fmt.Sprintf(containerdLegacyUrlAmd64, version)
		}
	case architectures.ArchitectureArm64:
		// For now there are only official AMD64 builds, always using fallback Docker version instead
		if findAllContainerdHashesAmd64()[version] != "" {
			if findAllContainerdDockerMappings()[version] != "" {
				u = fmt.Sprintf(dockerVersionUrlArm64, findAllContainerdDockerMappings()[version])
			} else {
				u = fmt.Sprintf(dockerVersionUrlArm64, findAllContainerdDockerMappings()[containerdFallbackVersion])
			}
		}
	default:
		return "", fmt.Errorf("unknown arch: %q", arch)
	}

	if u == "" {
		return "", fmt.Errorf("unknown url for containerd version: %s - %s", arch, version)
	}

	return u, nil
}

func findContainerdVersionHash(arch architectures.Architecture, version string) (string, error) {
	sv, err := semver.ParseTolerant(version)
	if err != nil {
		return "", fmt.Errorf("unable to parse version string: %q", version)
	}
	if sv.LT(semver.MustParse("1.3.4")) {
		return "", fmt.Errorf("unsupported legacy containerd version: %q", version)
	}

	var h string
	switch arch {
	case architectures.ArchitectureAmd64:
		h = findAllContainerdHashesAmd64()[version]
	case architectures.ArchitectureArm64:
		// For now there are only official AMD64 builds, always using fallback Docker version instead
		if findAllContainerdHashesAmd64()[version] != "" {
			if findAllContainerdDockerMappings()[version] != "" {
				h = findAllDockerHashesArm64()[findAllContainerdDockerMappings()[version]]
			} else {
				h = findAllDockerHashesArm64()[findAllContainerdDockerMappings()[containerdFallbackVersion]]
			}
		}
	default:
		return "", fmt.Errorf("unknown arch: %q", arch)
	}

	if h == "" {
		return "", fmt.Errorf("unknown hash for containerd version: %s - %s", arch, version)
	}

	return h, nil
}

func findAllContainerdHashesAmd64() map[string]string {
	hashes := map[string]string{
		"1.3.4":  "4616971c3ad21c24f2f2320fa1c085577a91032a068dd56a41c7c4b71a458087",
		"1.3.9":  "96663699e0f888fbf232ae6629a367aa7421f6b95044e7ee5d4d4e02841fac75",
		"1.3.10": "69e23e49cdf1232d475a77bf7ecd7145ff4a80295154e190125c4d8a20e241da",
		"1.4.0":  "b379f29417efd583f77e095173d4d0bd6bb001f0081b2a63d152ee7aef653ce1",
		"1.4.1":  "757efb93a4f3161efc447a943317503d8a7ded5cb4cc0cba3f3318d7ce1542ed",
		"1.4.2":  "9d0fd5f4d2bc58b345728432b7daac75fc99c1da91afa4f41e6103f618e74012",
		"1.4.3":  "2697a342e3477c211ab48313e259fd7e32ad1f5ded19320e6a559f50a82bff3d",
		"1.4.4":  "96641849cb78a0a119223a427dfdc1ade88412ef791a14193212c8c8e29d447b",
		"1.4.5":  "f8155278fd256526ca9804219e1ee46f5db11c6ddf455086b04c0887c868822a",
		"1.4.6":  "6ae4763598c9583f8b50605f19d6c7e9ef93c216706465e73dfc84ee6b63a238",
		"1.4.7":  "daa14638344fe0772f645e190e4d8eb9549b52743364cb8000521490f9e410b8",
		"1.4.8":  "96e815c9ab664a02dd5be35e31d15890ea6bef04dfaa39f99f14676c3d6561e8",
		"1.4.9":  "9911479f86012d6eab7e0f532da8f807a8b0f555ee09ef89367d8c31243073bb",
		"1.4.10": "5b256b372a02fd37c84939c87a6a81cae06058a7881a60d682525c11f6dea7d1",
		"1.5.0":  "aee7b553ab88842fdafe43955757abe746b8e9995b2be55c603f0a236186ff9b",
		"1.5.1":  "2fd97916b24396c13849cfcd89805170e1ef0265a2f7fce8e74ae044a6a6a169",
		"1.5.2":  "e7adbb6c6f6e67639460579a8aa991e9ce4de2062ed36d3261e6e4865574d947",
		"1.5.3":  "32a9bf1b7ab2adbd9d2a16b17bf1aa6e61592938655adfb5114c40d527aa9be7",
		"1.5.4":  "591e4e087ea2f5007e6c64deb382df58d419b7b6922eab45a1923d843d57615f",
		"1.5.5":  "45f02cfc65db47cf088c95555906e1dcba7baf5a3fbad3d947dd6b9af476a144",
		"1.5.6":  "afc51718ebe46cb9b985edac816e63fe86c07e37d28cdd21b2c0302dec6fa7ae",
		"1.5.7":  "7fce75bab43a39d6f9efb3c370de2da49723f0e1dbaa9732d68fa7f620d720c8",
	}

	return hashes
}

func findAllContainerdDockerMappings() map[string]string {
	versions := map[string]string{
		"1.3.7": "19.03.13",
		"1.3.9": "19.03.14",
		"1.4.3": "20.10.0",
		"1.4.4": "20.10.6",
		"1.4.6": "20.10.7",
		"1.4.9": "20.10.8",
	}

	return versions
}
<|endoftext|>


<commit_before>package db

import (
	"github.com/jinzhu/gorm"

	_ "github.com/jinzhu/gorm/dialects/postgres"
)

// Connection represents a socket connection to the database. The C member may
// be used directly to perform queries.
type Connection struct {
	C *gorm.DB
}

// Connect makes an attempt to connect to the database using the provided
// configuration.
func Connect(cfg *Config) (*Connection, error) {
	d, err := gorm.Open(cfg.Driver, cfg.Args)
	if err != nil {
		return nil, err
	}
	return &Connection{
		C: d,
	}, nil
}

// Migrate performs all pending database migrations.
func (c *Connection) Migrate() error {
	return c.C.AutoMigrate(
		&User{},
		&Account{},
		&Suggestion{},
		&QueueItem{},
		&Tweet{},
	).Error
}

// Transaction executes the provided callback in a transaction, automatically
// rolling back the database if an error occurs and committing the changes if
// none occurs.
func (c *Connection) Transaction(fn func(*Connection) error) error {
	d := c.C.Begin()
	if err := fn(&Connection{C: d}); err != nil {
		d.Rollback()
		return err
	}
	d.Commit()
	return nil
}

// Close disconnects from the database.
func (c *Connection) Close() {
	c.C.Close()
}
<commit_msg>Add logging to database connection.<commit_after>package db

import (
	"github.com/jinzhu/gorm"
	"github.com/sirupsen/logrus"

	_ "github.com/jinzhu/gorm/dialects/postgres"
)

// Connection represents a socket connection to the database. The C member may
// be used directly to perform queries.
type Connection struct {
	C   *gorm.DB
	log *logrus.Entry
}

// Connect makes an attempt to connect to the database using the provided
// configuration.
func Connect(cfg *Config) (*Connection, error) {
	d, err := gorm.Open(cfg.Driver, cfg.Args)
	if err != nil {
		return nil, err
	}
	c := &Connection{
		C:   d,
		log: logrus.WithField("context", "db"),
	}
	c.log.Info("connected to database")
	return c, nil
}

// Migrate performs all pending database migrations.
func (c *Connection) Migrate() error {
	c.log.Info("performing migrations...")
	return c.C.AutoMigrate(
		&User{},
		&Account{},
		&Suggestion{},
		&QueueItem{},
		&Tweet{},
	).Error
}

// Transaction executes the provided callback in a transaction, automatically
// rolling back the database if an error occurs and committing the changes if
// none occurs.
func (c *Connection) Transaction(fn func(*Connection) error) error {
	d := c.C.Begin()
	if err := fn(&Connection{C: d, log: c.log}); err != nil {
		d.Rollback()
		return err
	}
	d.Commit()
	return nil
}

// Close disconnects from the database.
func (c *Connection) Close() {
	c.C.Close()
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// logging.go contains the logic to configure and interact with the
// logging and metrics libraries.

package logging

import (
	"context"
	"flag"
	"fmt"
	"strings"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/golang/glog"
	"github.com/knative/pkg/logging"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/trace"
	"go.uber.org/zap"
)

const (
	// VerboseLogLevel defines verbose log level as 10
	VerboseLogLevel glog.Level = 10

	// 1 second was chosen arbitrarily
	metricViewReportingPeriod = 1 * time.Second

	// prefix attached to metric name that indicates to the
	// ExportSpan method that span needs to be emitted.
	emitableSpanNamePrefix = "emitspan-"
)

var baseLogger *BaseLogger

var exporter *zapMetricExporter

// zapMetricExporter is a stats and trace exporter that logs the
// exported data to the provided (probably test specific) zap logger.
// It conforms to the view.Exporter and trace.Exporter interfaces.
type zapMetricExporter struct {
	logger *zap.SugaredLogger
}

// BaseLogger is a common knative test files logger.
type BaseLogger struct {
	Logger *zap.SugaredLogger
}

// ExportView will emit the view data vd (i.e. the stats that have been
// recorded) to the zap logger.
func (e *zapMetricExporter) ExportView(vd *view.Data) {
	// We are not currently consuming these metrics, so for now we'll juse
	// dump the view.Data object as is.
	e.logger.Debug(spew.Sprint(vd))
}

// GetEmitableSpan starts and returns a trace.Span with a name that
// is used by the ExportSpan method to emit the span.
func GetEmitableSpan(ctx context.Context, metricName string) *trace.Span {
	_, span := trace.StartSpan(ctx, emitableSpanNamePrefix + metricName)
	return span
}

// ExportSpan will emit the trace data to the zap logger. The span is emitted
// only if the metric name is prefix with emitableSpanNamePrefix constant.
func (e *zapMetricExporter) ExportSpan(vd *trace.SpanData) {
	if strings.HasPrefix(vd.Name, emitableSpanNamePrefix) {
		duration := vd.EndTime.Sub(vd.StartTime)
		// We will start the log entry with `metric` to identify it as a metric for parsing
		e.logger.Infof("metric %s %d %d %s", vd.Name[len(emitableSpanNamePrefix):], vd.StartTime.UnixNano(), vd.EndTime.UnixNano(), duration)
	}
}

func newLogger(logLevel string) *BaseLogger {
	configJSONTemplate := `{
	  "level": "%s",
	  "encoding": "console",
	  "outputPaths": ["stdout"],
	  "errorOutputPaths": ["stderr"],
	  "encoderConfig": {
	    "timeKey": "ts",
	    "messageKey": "message",
	    "levelKey": "level",
	    "nameKey": "logger",
	    "callerKey": "caller",
	    "messageKey": "msg",
	    "stacktraceKey": "stacktrace",
	    "lineEnding": "",
	    "levelEncoder": "",
	    "timeEncoder": "iso8601",
	    "durationEncoder": "",
	    "callerEncoder": ""
	  }
	}`
	configJSON := fmt.Sprintf(configJSONTemplate, logLevel)
	l, _ := logging.NewLogger(string(configJSON), logLevel, zap.AddCallerSkip(1))
	return &BaseLogger{Logger: l}
}

// InitializeMetricExporter initializes the metric exporter logger
func InitializeMetricExporter() {
	trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
	view.SetReportingPeriod(metricViewReportingPeriod)
}

// InitializeLogger initializes the base logger
func InitializeLogger(logVerbose bool) {
	logLevel := "info"
	if logVerbose {
		// Both gLog and "go test" use -v flag. The code below is a work around so that we can still set v value for gLog
		flag.StringVar(&logLevel, "logLevel", fmt.Sprint(VerboseLogLevel), "verbose log level")
		flag.Lookup("v").Value.Set(logLevel)
		glog.Infof("Logging set to verbose mode with logLevel %d", VerboseLogLevel)

		logLevel = "debug"
	}
	baseLogger = newLogger(logLevel)
}

// GetContextLogger creates a Named logger (https://godoc.org/go.uber.org/zap#Logger.Named)
// using the provided context as a name. This will also register the logger as a metric exporter,
// which is unfortunately global, so calling `GetContextLogger` will have the side effect of
// changing the context in which all metrics are logged from that point forward.
func GetContextLogger(context string) *BaseLogger {
	// If there was a previously registered exporter, unregister it so we only emit
	// the metrics in the current context.
	if exporter != nil {
		view.UnregisterExporter(exporter)
		trace.UnregisterExporter(exporter)
	}

	logger := baseLogger.Logger.Named(context)

	exporter = &zapMetricExporter{logger: logger}
	view.RegisterExporter(exporter)
	trace.RegisterExporter(exporter)

	return &BaseLogger{Logger: logger}
}

// Infof logs a templated message.
func (b *BaseLogger) Infof(template string, args ...interface{}) {
	b.Logger.Infof(template, args...)
}

// Info logs an info message.
func (b *BaseLogger) Info(args ...interface{}) {
	b.Logger.Info(args...)
}

// Fatal logs a fatal message.
func (b *BaseLogger) Fatal(args ...interface{}) {
	b.Logger.Fatal(args...)
}

// Fatalf logs a templated fatal message.
func (b *BaseLogger) Fatalf(template string, args ...interface{}) {
	b.Logger.Fatalf(template, args...)
}

// Debugf logs a templated debug message.
func (b *BaseLogger) Debugf(template string, args ...interface{}) {
	b.Logger.Debugf(template, args...)
}

// Debug logs a debug message.
func (b *BaseLogger) Debug(args ...interface{}) {
	b.Logger.Debug(args...)
}

// Errorf logs a templated error message.
func (b *BaseLogger) Errorf(template string, args ...interface{}) {
	b.Logger.Errorf(template, args...)
}

// Error logs an error message.
func (b *BaseLogger) Error(args ...interface{}) {
	b.Logger.Error(args...)
}
<commit_msg>golang format tools (#287)<commit_after>/*
Copyright 2018 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// logging.go contains the logic to configure and interact with the
// logging and metrics libraries.

package logging

import (
	"context"
	"flag"
	"fmt"
	"strings"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/golang/glog"
	"github.com/knative/pkg/logging"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/trace"
	"go.uber.org/zap"
)

const (
	// VerboseLogLevel defines verbose log level as 10
	VerboseLogLevel glog.Level = 10

	// 1 second was chosen arbitrarily
	metricViewReportingPeriod = 1 * time.Second

	// prefix attached to metric name that indicates to the
	// ExportSpan method that span needs to be emitted.
	emitableSpanNamePrefix = "emitspan-"
)

var baseLogger *BaseLogger

var exporter *zapMetricExporter

// zapMetricExporter is a stats and trace exporter that logs the
// exported data to the provided (probably test specific) zap logger.
// It conforms to the view.Exporter and trace.Exporter interfaces.
type zapMetricExporter struct {
	logger *zap.SugaredLogger
}

// BaseLogger is a common knative test files logger.
type BaseLogger struct {
	Logger *zap.SugaredLogger
}

// ExportView will emit the view data vd (i.e. the stats that have been
// recorded) to the zap logger.
func (e *zapMetricExporter) ExportView(vd *view.Data) {
	// We are not currently consuming these metrics, so for now we'll juse
	// dump the view.Data object as is.
	e.logger.Debug(spew.Sprint(vd))
}

// GetEmitableSpan starts and returns a trace.Span with a name that
// is used by the ExportSpan method to emit the span.
func GetEmitableSpan(ctx context.Context, metricName string) *trace.Span {
	_, span := trace.StartSpan(ctx, emitableSpanNamePrefix+metricName)
	return span
}

// ExportSpan will emit the trace data to the zap logger. The span is emitted
// only if the metric name is prefix with emitableSpanNamePrefix constant.
func (e *zapMetricExporter) ExportSpan(vd *trace.SpanData) {
	if strings.HasPrefix(vd.Name, emitableSpanNamePrefix) {
		duration := vd.EndTime.Sub(vd.StartTime)
		// We will start the log entry with `metric` to identify it as a metric for parsing
		e.logger.Infof("metric %s %d %d %s", vd.Name[len(emitableSpanNamePrefix):], vd.StartTime.UnixNano(), vd.EndTime.UnixNano(), duration)
	}
}

func newLogger(logLevel string) *BaseLogger {
	configJSONTemplate := `{
	  "level": "%s",
	  "encoding": "console",
	  "outputPaths": ["stdout"],
	  "errorOutputPaths": ["stderr"],
	  "encoderConfig": {
	    "timeKey": "ts",
	    "messageKey": "message",
	    "levelKey": "level",
	    "nameKey": "logger",
	    "callerKey": "caller",
	    "messageKey": "msg",
	    "stacktraceKey": "stacktrace",
	    "lineEnding": "",
	    "levelEncoder": "",
	    "timeEncoder": "iso8601",
	    "durationEncoder": "",
	    "callerEncoder": ""
	  }
	}`
	configJSON := fmt.Sprintf(configJSONTemplate, logLevel)
	l, _ := logging.NewLogger(string(configJSON), logLevel, zap.AddCallerSkip(1))
	return &BaseLogger{Logger: l}
}

// InitializeMetricExporter initializes the metric exporter logger
func InitializeMetricExporter() {
	trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
	view.SetReportingPeriod(metricViewReportingPeriod)
}

// InitializeLogger initializes the base logger
func InitializeLogger(logVerbose bool) {
	logLevel := "info"
	if logVerbose {
		// Both gLog and "go test" use -v flag. The code below is a work around so that we can still set v value for gLog
		flag.StringVar(&logLevel, "logLevel", fmt.Sprint(VerboseLogLevel), "verbose log level")
		flag.Lookup("v").Value.Set(logLevel)
		glog.Infof("Logging set to verbose mode with logLevel %d", VerboseLogLevel)

		logLevel = "debug"
	}
	baseLogger = newLogger(logLevel)
}

// GetContextLogger creates a Named logger (https://godoc.org/go.uber.org/zap#Logger.Named)
// using the provided context as a name. This will also register the logger as a metric exporter,
// which is unfortunately global, so calling `GetContextLogger` will have the side effect of
// changing the context in which all metrics are logged from that point forward.
func GetContextLogger(context string) *BaseLogger {
	// If there was a previously registered exporter, unregister it so we only emit
	// the metrics in the current context.
	if exporter != nil {
		view.UnregisterExporter(exporter)
		trace.UnregisterExporter(exporter)
	}

	logger := baseLogger.Logger.Named(context)

	exporter = &zapMetricExporter{logger: logger}
	view.RegisterExporter(exporter)
	trace.RegisterExporter(exporter)

	return &BaseLogger{Logger: logger}
}

// Infof logs a templated message.
func (b *BaseLogger) Infof(template string, args ...interface{}) {
	b.Logger.Infof(template, args...)
}

// Info logs an info message.
func (b *BaseLogger) Info(args ...interface{}) {
	b.Logger.Info(args...)
}

// Fatal logs a fatal message.
func (b *BaseLogger) Fatal(args ...interface{}) {
	b.Logger.Fatal(args...)
}

// Fatalf logs a templated fatal message.
func (b *BaseLogger) Fatalf(template string, args ...interface{}) {
	b.Logger.Fatalf(template, args...)
}

// Debugf logs a templated debug message.
func (b *BaseLogger) Debugf(template string, args ...interface{}) {
	b.Logger.Debugf(template, args...)
}

// Debug logs a debug message.
func (b *BaseLogger) Debug(args ...interface{}) {
	b.Logger.Debug(args...)
}

// Errorf logs a templated error message.
func (b *BaseLogger) Errorf(template string, args ...interface{}) {
	b.Logger.Errorf(template, args...)
}

// Error logs an error message.
func (b *BaseLogger) Error(args ...interface{}) {
	b.Logger.Error(args...)
}
<|endoftext|>


<commit_before>package job

import (
	"github.com/vmware/harbor/dao"
	"github.com/vmware/harbor/job/config"
	"github.com/vmware/harbor/models"
	"github.com/vmware/harbor/utils/log"
)

var WorkerPool chan *Worker

type Worker struct {
	ID      int
	RepJobs chan int64
	SM      *JobSM
	quit    chan bool
}

func (w *Worker) Start() {
	go func() {
		for {
			WorkerPool <- w
			select {
			case jobID := <-w.RepJobs:
				log.Debugf("worker: %d, will handle job: %d", w.ID, jobID)
				w.handleRepJob(jobID)
			case q := <-w.quit:
				if q {
					log.Debugf("worker: %d, will stop.", w.ID)
					return
				}
			}
		}
	}()
}

func (w *Worker) Stop() {
	go func() {
		w.quit <- true
	}()
}

func (w *Worker) handleRepJob(id int64) {
	err := w.SM.Reset(id)
	if err != nil {
		log.Errorf("Worker %d, failed to re-initialize statemachine, error: %v", w.ID, err)
		err2 := dao.UpdateRepJobStatus(id, models.JobError)
		if err2 != nil {
			log.Errorf("Failed to update job status to ERROR, error:%v", err2)
		}
		return
	}
	if w.SM.Parms.Enabled == 0 {
		log.Debugf("The policy of job:%d is disabled, will cancel the job")
		_ = dao.UpdateRepJobStatus(id, models.JobCanceled)
	} else {
		w.SM.Start(models.JobRunning)
	}
}

func NewWorker(id int) *Worker {
	w := &Worker{
		ID:      id,
		RepJobs: make(chan int64),
		quit:    make(chan bool),
		SM:      &JobSM{},
	}
	w.SM.Init()
	return w
}

func InitWorkerPool() {
	WorkerPool = make(chan *Worker, config.MaxJobWorkers())
	for i := 0; i < config.MaxJobWorkers(); i++ {
		worker := NewWorker(i)
		worker.Start()
		log.Debugf("worker %d started", worker.ID)
	}
}

func Dispatch() {
	for {
		select {
		case job := <-JobQueue:
			go func(jobID int64) {
				log.Debugf("Trying to dispatch job: %d", jobID)
				worker := <-WorkerPool
				worker.RepJobs <- jobID
			}(job)
		}
	}
}
<commit_msg>add change in workerpool.go<commit_after>package job

import (
	"github.com/vmware/harbor/dao"
	"github.com/vmware/harbor/job/config"
	"github.com/vmware/harbor/models"
	"github.com/vmware/harbor/utils/log"
)

type workerPool struct {
	workerChan chan *Worker
	workerList []*Worker
}

var WorkerPool *workerPool

func (wp *workerPool) StopJobs(jobs []int64) {
	log.Debugf("Works working on jobs: %v will be stopped", jobs)
	for _, id := range jobs {
		for _, w := range wp.workerList {
			if w.SM.JobID == id {
				log.Debugf("found a worker whose job ID is %d, will try to stop it", id)
				w.SM.Stop(id)
			}
		}
	}
}

type Worker struct {
	ID      int
	RepJobs chan int64
	SM      *JobSM
	quit    chan bool
}

func (w *Worker) Start() {
	go func() {
		for {
			WorkerPool.workerChan <- w
			select {
			case jobID := <-w.RepJobs:
				log.Debugf("worker: %d, will handle job: %d", w.ID, jobID)
				w.handleRepJob(jobID)
			case q := <-w.quit:
				if q {
					log.Debugf("worker: %d, will stop.", w.ID)
					return
				}
			}
		}
	}()
}

func (w *Worker) Stop() {
	go func() {
		w.quit <- true
	}()
}

func (w *Worker) handleRepJob(id int64) {
	err := w.SM.Reset(id)
	if err != nil {
		log.Errorf("Worker %d, failed to re-initialize statemachine for job: %d, error: %v", w.ID, id, err)
		err2 := dao.UpdateRepJobStatus(id, models.JobError)
		if err2 != nil {
			log.Errorf("Failed to update job status to ERROR, job: %d, error:%v", id, err2)
		}
		return
	}
	if w.SM.Parms.Enabled == 0 {
		log.Debugf("The policy of job:%d is disabled, will cancel the job")
		_ = dao.UpdateRepJobStatus(id, models.JobCanceled)
	} else {
		w.SM.Start(models.JobRunning)
	}
}

func NewWorker(id int) *Worker {
	w := &Worker{
		ID:      id,
		RepJobs: make(chan int64),
		quit:    make(chan bool),
		SM:      &JobSM{},
	}
	w.SM.Init()
	return w
}

func InitWorkerPool() {
	WorkerPool = &workerPool{
		workerChan: make(chan *Worker, config.MaxJobWorkers()),
		workerList: make([]*Worker, 0, config.MaxJobWorkers()),
	}
	for i := 0; i < config.MaxJobWorkers(); i++ {
		worker := NewWorker(i)
		WorkerPool.workerList = append(WorkerPool.workerList, worker)
		worker.Start()
		log.Debugf("worker %d started", worker.ID)
	}
}

func Dispatch() {
	for {
		select {
		case job := <-JobQueue:
			go func(jobID int64) {
				log.Debugf("Trying to dispatch job: %d", jobID)
				worker := <-WorkerPool.workerChan
				worker.RepJobs <- jobID
			}(job)
		}
	}
}
<|endoftext|>


<commit_before>package controller

import (
	"time"

	"github.com/enow-dev/enow/app"
	"github.com/enow-dev/enow/config"
	"github.com/enow-dev/enow/model"
	"github.com/enow-dev/enow/util"
	"github.com/goadesign/goa"
	"github.com/mjibson/goon"
	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
)

// AuthController implements the auth resource.
type AuthController struct {
	*goa.Controller
}

// NewAuthController creates a auth controller.
func NewAuthController(service *goa.Service) *AuthController {
	return &AuthController{Controller: service.NewController("AuthController")}
}

// GithubUser Githubから取得したユーザー情報
type GithubUser struct {
	ID        int    `json:"id"`
	Login     string `json:"login"`
	Email     string `json:"email"`
	AvaterURL string `json:"avatar_url"`
}

// email Githubに登録されたメールアドレス
type email struct {
	Email    string `json:"email"`
	Primary  bool   `json:"primary"`
	Verified bool   `json:"verified"`
}

// Login runs the login action.
func (c *AuthController) Login(ctx *app.LoginAuthContext) error {
	// AuthController_Login: start_implement

	// Put your logic here
	appCtx := appengine.NewContext(ctx.Request)
	oauthConfs, err := config.NewOauthsFromFile("../config/oauth.yaml")
	if err != nil {
		return ctx.InternalServerError(goa.ErrInternal(err))
	}
	oauthConf, err := oauthConfs.Get("github.com")
	if err != nil {
		return ctx.InternalServerError(goa.ErrInternal(err))
	}

	// Github OAuthトークン取得
	tok, err := oauthConf.Exchange(appCtx, ctx.Code)
	if err != nil {
		return ctx.BadRequest(goa.ErrBadRequest(err))
	}
	client := oauthConf.Client(appCtx, tok)
	// Github ユーザー情報取得
	githubuser := GithubUser{}
	err = util.FetchAPIresponse(client, "https://api.github.com/user", &githubuser)
	if err != nil {
		return ctx.InternalServerError(goa.ErrInternal(err))
	}
	// Githubに登録しているメールアドレスを取得(非公開でも取れる)
	emails := []email{}
	err = util.FetchAPIresponse(client, "https://api.github.com/user/emails", &emails)
	if err != nil {
		return ctx.InternalServerError(goa.ErrInternal(err))
	}
	// メインのメアドを使う
	for _, v := range emails {
		if v.Primary && v.Verified {
			githubuser.Email = v.Email
			break
		}
	}

	// 既にユーザーが存在しているか確認する
	uDB := model.UsersDB{}
	var loginUserKey *datastore.Key
	loginUserKey, err = uDB.GetKeyFindByOauthID(appCtx, githubuser.ID, model.GithubOAuth)
	if err != nil {
		return ctx.InternalServerError(goa.ErrInternal(err))
	}
	g := goon.FromContext(appCtx)
	now := time.Now()

	// tokenを生成するが、被っている可能生があるのでチェックする
	token := util.CreateTokenHash(string(githubuser.ID))
	alreadyRegisteredUser, err := uDB.GetUserKeyFindByToken(appCtx, token)
	for {
		// 既に同じTokenがあれば再生成する
		if err != nil && alreadyRegisteredUser != nil {
			token = util.CreateTokenHash(string(githubuser.ID))
		} else {
			break
		}
	}
	// 1トランザクションにしたいが、Datastoreの仕様上、commitしてないユーザーの取得が無理だった
	// ユーザーが存在していないので、作成する
	if loginUserKey == nil {
		newUser := &model.Users{
			Name:      githubuser.Login,
			Email:     githubuser.Email,
			AvaterURL: githubuser.AvaterURL,
			GithubID:  githubuser.ID,
			CreatedAt: now,
			Expire:    now.AddDate(0, 0, 7),
			Token:     token,
		}
		loginUserKey, err = g.Put(newUser)
		if err != nil {
			return ctx.InternalServerError(goa.ErrInternal(err))
		}
	}
	loginUser, err := uDB.UpdateToken(appCtx, loginUserKey, now, token)
	if err != nil {
		return ctx.InternalServerError(goa.ErrInternal(err))
	}
	// AuthController_Login: end_implement
	res := &app.Session{
		Token:     loginUser.Token,
		Name:      loginUser.Name,
		Expire:    loginUser.Expire,
		AvaterURL: loginUser.AvaterURL,
	}
	return ctx.OK(res)
}

// Logout runs the logout action.
func (c *AuthController) Logout(ctx *app.LogoutAuthContext) error {
	// AuthController_Logout: start_implement

	// Put your logic here

	// AuthController_Logout: end_implement
	res := &app.Session{}
	return ctx.OK(res)
}
<commit_msg>add: GAE上のStorageを参照するようにした<commit_after>package controller

import (
	"time"

	yaml "gopkg.in/yaml.v1"

	"golang.org/x/oauth2"

	"os"

	"github.com/enow-dev/enow/app"
	"github.com/enow-dev/enow/config"
	"github.com/enow-dev/enow/model"
	"github.com/enow-dev/enow/util"
	"github.com/goadesign/goa"
	"github.com/mjibson/goon"
	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/log"
)

// AuthController implements the auth resource.
type AuthController struct {
	*goa.Controller
}

// NewAuthController creates a auth controller.
func NewAuthController(service *goa.Service) *AuthController {
	return &AuthController{Controller: service.NewController("AuthController")}
}

// GithubUser Githubから取得したユーザー情報
type GithubUser struct {
	ID        int    `json:"id"`
	Login     string `json:"login"`
	Email     string `json:"email"`
	AvaterURL string `json:"avatar_url"`
}

// email Githubに登録されたメールアドレス
type email struct {
	Email    string `json:"email"`
	Primary  bool   `json:"primary"`
	Verified bool   `json:"verified"`
}

// Login runs the login action.
func (c *AuthController) Login(ctx *app.LoginAuthContext) error {
	// AuthController_Login: start_implement

	// Put your logic here
	appCtx := appengine.NewContext(ctx.Request)
	var oauthConf *oauth2.Config
	// TODO: 関数を別で作成する
	if os.Getenv("Op") == "develop" {
		oauthConfs, err := config.NewOauthsFromFile("../config/oauth.yaml")
		if err != nil {
			log.Errorf(appCtx, "設定ファイル読み込みエラー(1): %v", err)
			return ctx.InternalServerError(goa.ErrInternal(err))
		}
		oauthConf, err = oauthConfs.Get("github.com")
		if err != nil {
			log.Errorf(appCtx, "設定ファイル読み込みエラー(2): %v", err)
			return ctx.InternalServerError(goa.ErrInternal(err))
		}
	} else {
		cs, err := util.ReadFileFromBucket(appCtx, "oauth.yaml")
		if err != nil {
			log.Errorf(appCtx, "設定ファイル読み込みエラー(3): %v", err)
			return ctx.InternalServerError(goa.ErrInternal(err))
		}
		var conf config.Oauths
		if err = yaml.Unmarshal(cs, &conf); err != nil {
			log.Errorf(appCtx, "設定ファイル読み込みエラー(4): %v", err)
			return ctx.InternalServerError(goa.ErrInternal(err))
		}
		oauthConf, err = conf.Get("github.com")
		if err != nil {
			log.Errorf(appCtx, "設定ファイル読み込みエラー(5): %v", err)
			return ctx.InternalServerError(goa.ErrInternal(err))
		}
	}

	// Github OAuthトークン取得
	tok, err := oauthConf.Exchange(appCtx, ctx.Code)
	if err != nil {
		return ctx.BadRequest(goa.ErrBadRequest(err))
	}
	client := oauthConf.Client(appCtx, tok)
	// Github ユーザー情報取得
	githubuser := GithubUser{}
	err = util.FetchAPIresponse(client, "https://api.github.com/user", &githubuser)
	if err != nil {
		return ctx.InternalServerError(goa.ErrInternal(err))
	}
	// Githubに登録しているメールアドレスを取得(非公開でも取れる)
	emails := []email{}
	err = util.FetchAPIresponse(client, "https://api.github.com/user/emails", &emails)
	if err != nil {
		return ctx.InternalServerError(goa.ErrInternal(err))
	}
	// メインのメアドを使う
	for _, v := range emails {
		if v.Primary && v.Verified {
			githubuser.Email = v.Email
			break
		}
	}

	// 既にユーザーが存在しているか確認する
	uDB := model.UsersDB{}
	var loginUserKey *datastore.Key
	loginUserKey, err = uDB.GetKeyFindByOauthID(appCtx, githubuser.ID, model.GithubOAuth)
	if err != nil {
		return ctx.InternalServerError(goa.ErrInternal(err))
	}
	g := goon.FromContext(appCtx)
	now := time.Now()

	// tokenを生成するが、被っている可能生があるのでチェックする
	token := util.CreateTokenHash(string(githubuser.ID))
	alreadyRegisteredUser, err := uDB.GetUserKeyFindByToken(appCtx, token)
	for {
		// 既に同じTokenがあれば再生成する
		if err != nil && alreadyRegisteredUser != nil {
			token = util.CreateTokenHash(string(githubuser.ID))
		} else {
			break
		}
	}
	// 1トランザクションにしたいが、Datastoreの仕様上、commitしてないユーザーの取得が無理だった
	// ユーザーが存在していないので、作成する
	if loginUserKey == nil {
		newUser := &model.Users{
			Name:      githubuser.Login,
			Email:     githubuser.Email,
			AvaterURL: githubuser.AvaterURL,
			GithubID:  githubuser.ID,
			CreatedAt: now,
			Expire:    now.AddDate(0, 0, 7),
			Token:     token,
		}
		loginUserKey, err = g.Put(newUser)
		if err != nil {
			return ctx.InternalServerError(goa.ErrInternal(err))
		}
	}
	loginUser, err := uDB.UpdateToken(appCtx, loginUserKey, now, token)
	if err != nil {
		return ctx.InternalServerError(goa.ErrInternal(err))
	}
	// AuthController_Login: end_implement
	res := &app.Session{
		Token:     loginUser.Token,
		Name:      loginUser.Name,
		Expire:    loginUser.Expire,
		AvaterURL: loginUser.AvaterURL,
	}
	return ctx.OK(res)
}

// Logout runs the logout action.
func (c *AuthController) Logout(ctx *app.LogoutAuthContext) error {
	// AuthController_Logout: start_implement

	// Put your logic here

	// AuthController_Logout: end_implement
	res := &app.Session{}
	return ctx.OK(res)
}
<|endoftext|>


<commit_before>package mockery

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

type Walker struct {
	BaseDir   string
	Recursive bool
	Filter    *regexp.Regexp
	LimitOne  bool
	BuildTags []string
}

type WalkerVisitor interface {
	VisitWalk(*Interface) error
}

func (this *Walker) Walk(visitor WalkerVisitor) (generated bool) {
	parser := NewParser()
	parser.AddBuildTags(this.BuildTags...)
	this.doWalk(parser, this.BaseDir, visitor)

	err := parser.Load()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error walking: %v\n", err)
		os.Exit(1)
	}

	for _, iface := range parser.Interfaces() {
		if !this.Filter.MatchString(iface.Name) {
			continue
		}
		err := visitor.VisitWalk(iface)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error walking %s: %s\n", iface.Name, err)
			os.Exit(1)
		}
		generated = true
		if this.LimitOne {
			return
		}
	}

	return
}

func (this *Walker) doWalk(p *Parser, dir string, visitor WalkerVisitor) (generated bool) {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return
	}

	for _, file := range files {
		if strings.HasPrefix(file.Name(), ".") || strings.HasPrefix(file.Name(), "_") {
			continue
		}

		path := filepath.Join(dir, file.Name())

		if file.IsDir() {
			if this.Recursive {
				generated = this.doWalk(p, path, visitor) || generated
				if generated && this.LimitOne {
					return
				}
			}
			continue
		}

		if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
			continue
		}

		err = p.Parse(path)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error parsing file: ", err)
			return
		}
	}

	return
}

type GeneratorVisitor struct {
	InPackage bool
	Note      string
	Osp       OutputStreamProvider
	// The name of the output package, if InPackage is false (defaults to "mocks")
	PackageName string
}

func (this *GeneratorVisitor) VisitWalk(iface *Interface) error {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Unable to generated mock for '%s': %s\n", iface.Name, r)
			return
		}
	}()

	var out io.Writer
	var pkg string

	if this.InPackage {
		pkg = iface.Path
	} else {
		pkg = this.PackageName
	}

	out, err, closer := this.Osp.GetWriter(iface, pkg)
	if err != nil {
		fmt.Printf("Unable to get writer for %s: %s", iface.Name, err)
		os.Exit(1)
	}
	defer closer()

	gen := NewGenerator(iface, pkg, this.InPackage)
	gen.GeneratePrologueNote(this.Note)
	gen.GeneratePrologue(pkg)

	err = gen.Generate()
	if err != nil {
		return err
	}

	err = gen.Write(out)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Report and ignore unparsable files. Fixes #184<commit_after>package mockery

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

type Walker struct {
	BaseDir   string
	Recursive bool
	Filter    *regexp.Regexp
	LimitOne  bool
	BuildTags []string
}

type WalkerVisitor interface {
	VisitWalk(*Interface) error
}

func (this *Walker) Walk(visitor WalkerVisitor) (generated bool) {
	parser := NewParser()
	parser.AddBuildTags(this.BuildTags...)
	this.doWalk(parser, this.BaseDir, visitor)

	err := parser.Load()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error walking: %v\n", err)
		os.Exit(1)
	}

	for _, iface := range parser.Interfaces() {
		if !this.Filter.MatchString(iface.Name) {
			continue
		}
		err := visitor.VisitWalk(iface)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error walking %s: %s\n", iface.Name, err)
			os.Exit(1)
		}
		generated = true
		if this.LimitOne {
			return
		}
	}

	return
}

func (this *Walker) doWalk(p *Parser, dir string, visitor WalkerVisitor) (generated bool) {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return
	}

	for _, file := range files {
		if strings.HasPrefix(file.Name(), ".") || strings.HasPrefix(file.Name(), "_") {
			continue
		}

		path := filepath.Join(dir, file.Name())

		if file.IsDir() {
			if this.Recursive {
				generated = this.doWalk(p, path, visitor) || generated
				if generated && this.LimitOne {
					return
				}
			}
			continue
		}

		if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
			continue
		}

		err = p.Parse(path)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error parsing file: ", err)
			continue
		}
	}

	return
}

type GeneratorVisitor struct {
	InPackage bool
	Note      string
	Osp       OutputStreamProvider
	// The name of the output package, if InPackage is false (defaults to "mocks")
	PackageName string
}

func (this *GeneratorVisitor) VisitWalk(iface *Interface) error {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Unable to generated mock for '%s': %s\n", iface.Name, r)
			return
		}
	}()

	var out io.Writer
	var pkg string

	if this.InPackage {
		pkg = iface.Path
	} else {
		pkg = this.PackageName
	}

	out, err, closer := this.Osp.GetWriter(iface, pkg)
	if err != nil {
		fmt.Printf("Unable to get writer for %s: %s", iface.Name, err)
		os.Exit(1)
	}
	defer closer()

	gen := NewGenerator(iface, pkg, this.InPackage)
	gen.GeneratePrologueNote(this.Note)
	gen.GeneratePrologue(pkg)

	err = gen.Generate()
	if err != nil {
		return err
	}

	err = gen.Write(out)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>