<commit_before>package swp

import (
	"fmt"
	"sync"
	"time"
)

// AckStatus conveys info from the receiver to the sender when an Ack is received.
type AckStatus struct {
	OnlyUpdateFlowCtrl  bool // don't send ack, just update flow info.
	AckNum              Seqno
	AckCameWithPacket   Seqno
	AvailReaderBytesCap int64 // for sender throttling/flow-control
	AvailReaderMsgCap   int64 // for sender throttling/flow-control
}

// SenderState tracks the sender's sliding window state.
// To avoid circulate deadlocks, the Sender never talks
// directly to the RecvState. The RecvState will
// tell the Sender stuff on GotAck.
type SenderState struct {
	Net              Network
	Inbox            string
	Dest             string
	LastAckRec       Seqno
	LastFrameSent    Seqno
	Txq              []*TxqSlot
	SenderWindowSize Seqno
	mut              sync.Mutex
	Timeout          time.Duration

	// the main goroutine safe way to request
	// sending a packet:
	BlockingSend chan *Packet
	GotAck       chan AckStatus

	ReqStop      chan bool
	Done         chan bool
	SendHistory  []*Packet
	SendSz       int64
	SendAck      chan *Packet
	DiscardCount int64

	LastSendTime      time.Time
	KeepAliveInterval time.Duration
	keepAlive         <-chan time.Time

	SentButNotAcked map[Seqno]*TxqSlot

	// flow control params
	// last seen from our downstream
	// receiver, we throttle ourselves
	// based on these.
	LastSeenAvailReaderBytesCap int64
	LastSeenAvailReaderMsgCap   int64

	// do synchronized access via GetFlow()
	// and UpdateFlow(s.Net)
	FlowCt         *FlowCtrl
	TotalBytesSent int64
}

func NewSenderState(net Network, sendSz int64, timeout time.Duration,
	inbox string, destInbox string) *SenderState {
	s := &SenderState{
		Net:              net,
		Inbox:            inbox,
		Dest:             destInbox,
		SenderWindowSize: Seqno(sendSz),
		Txq:              make([]*TxqSlot, sendSz),
		Timeout:          timeout,
		LastFrameSent:    -1,
		LastAckRec:       -1,
		ReqStop:          make(chan bool),
		Done:             make(chan bool),
		SendHistory:      make([]*Packet, 0),
		BlockingSend:     make(chan *Packet),
		SendSz:           sendSz,
		GotAck:           make(chan AckStatus),
		SendAck:          make(chan *Packet),
		SentButNotAcked:  make(map[Seqno]*TxqSlot),

		// send keepalives (important especially for resuming flow from a
		// stopped state) at least this often:
		KeepAliveInterval: 100 * time.Millisecond,
		FlowCt: &FlowCtrl{flow: Flow{
			ReservedByteCap: 64 * 1024,
			ReservedMsgCap:  32,
		}},
		// don't start fast, as we could overwhelm
		// the receiver. Instead start very slowly,
		// allowing 2 messages so our 003 reorder test runs.
		// It will get updated after the first ack
		// or keep alive.
		LastSeenAvailReaderMsgCap:   2,
		LastSeenAvailReaderBytesCap: 1024 * 1024,
	}
	for i := range s.Txq {
		s.Txq[i] = &TxqSlot{}
	}
	return s
}

func (s *SenderState) ComputeInflight() (bytesInflight int64, msgInflight int64) {
	for _, slot := range s.SentButNotAcked {
		msgInflight++
		bytesInflight += int64(len(slot.Pack.Data))
	}
	return
}

// Start initiates the SenderState goroutine, which manages
// sends, timeouts, and resends
func (s *SenderState) Start() {

	go func() {

		var acceptSend chan *Packet

		// check for expired timers at wakeFreq
		wakeFreq := s.Timeout / 2

		// send keepalives (for resuming flow from a
		// stopped state) at least this often:
		s.keepAlive = time.After(s.KeepAliveInterval)

		regularIntervalWakeup := time.After(wakeFreq)

	sendloop:
		for {
			//q("%v top of sendloop, sender LAR: %v, LFS: %v \n",
			//	s.Inbox, s.LastAckRec, s.LastFrameSent)

			// does the downstream reader have capacity to accept a send?
			// Block any new sends if so. We do a conditional receive. Start by
			// assuming no:
			acceptSend = nil

			// then check if we can set acceptSend.
			//
			// We accept a packet for sending if flow control info
			// from the receiver allows it.
			//
			bytesInflight, msgInflight := s.ComputeInflight()
			//q("%v bytesInflight = %v", s.Inbox, bytesInflight)
			//q("%v msgInflight = %v", s.Inbox, msgInflight)

			if s.LastSeenAvailReaderMsgCap-msgInflight > 0 &&
				s.LastSeenAvailReaderBytesCap-bytesInflight > 0 {
				q("%v flow-control: okay to send. s.LastSeenAvailReaderMsgCap: %v > msgInflight: %v",
					s.Inbox, s.LastSeenAvailReaderMsgCap, msgInflight)
				acceptSend = s.BlockingSend
			} else {
				q("%v flow-control kicked in: not sending. s.LastSeenAvailReaderMsgCap = %v,"+
					" msgInflight=%v, s.LastSeenAvailReaderBytesCap=%v bytesInflight=%v",
					s.Inbox, s.LastSeenAvailReaderMsgCap, msgInflight,
					s.LastSeenAvailReaderBytesCap, bytesInflight)
			}

			q("%v top of sender select loop", s.Inbox)
			select {
			case <-s.keepAlive:
				q("%v keepAlive at %v", s.Inbox, time.Now())
				s.doKeepAlive()

			case <-regularIntervalWakeup:
				q("%v regularIntervalWakeup at %v", s.Inbox, time.Now())

				// have any of our packets timed-out and need to be
				// sent again?
				retry := []*TxqSlot{}
				for _, slot := range s.SentButNotAcked {
					if slot.RetryDeadline.Before(time.Now()) {
						retry = append(retry, slot)
					}
				}
			doRetryLoop:
				for _, slot := range retry {
					if slot.Pack == nil {
						//q("retry loop, slot = %#v", slot)
					}
					_, ok := s.SentButNotAcked[slot.Pack.SeqNum]
					if !ok {
						//q("already acked and gone from SentButNotAcked, so skip SeqNum %v and PopTop",
						//	slot.Pack.SeqNum)
						continue doRetryLoop
					}
					if slot.Pack.SeqNum <= s.LastAckRec {
						//q("already acked; is <= s.LastAckRecv (%v), so skip SeqNum %v and PopTop",
						//	s.LastAckRec, slot.Pack.SeqNum)
						continue doRetryLoop
					}

					// reset deadline and resend
					slot.RetryDeadline = time.Now().Add(s.Timeout)

					flow := s.FlowCt.UpdateFlow(s.Inbox, s.Net, -1, -1)
					slot.Pack.AvailReaderBytesCap = flow.AvailReaderBytesCap
					slot.Pack.AvailReaderMsgCap = flow.AvailReaderMsgCap
					q("%v doing retry Net.Send() for pack = '%#v' of paydirt '%s'",
						s.Inbox, slot.Pack, string(slot.Pack.Data))
					err := s.Net.Send(slot.Pack, "retry")
					panicOn(err)
				}
				regularIntervalWakeup = time.After(wakeFreq)

			case <-s.ReqStop:
				close(s.Done)
				return
			case pack := <-acceptSend:
				q("%v got <-acceptSend pack: '%#v'", s.Inbox, pack)
				s.doOrigDataSend(pack)

			case a := <-s.GotAck:
				// ack received - do sender side stuff
				//
				q("%v sender GotAck a: %#v", s.Inbox, a)
				//
				// flow control: respect a.AvailReaderBytesCap
				// and a.AvailReaderMsgCap info that we have
				// received from this ack
				//
				q("%v sender GotAck, updating s.LastSeenAvailReaderMsgCap %v -> %v",
					s.Inbox, s.LastSeenAvailReaderMsgCap, a.AvailReaderMsgCap)
				s.LastSeenAvailReaderBytesCap = a.AvailReaderBytesCap
				s.LastSeenAvailReaderMsgCap = a.AvailReaderMsgCap

				// need to update our map of SentButNotAcked
				// and remove everything before AckNum, which is cumulative.
				for _, slot := range s.SentButNotAcked {
					if slot.Pack.SeqNum <= a.AckNum {
						delete(s.SentButNotAcked, a.AckNum)
					}
				}

				if a.OnlyUpdateFlowCtrl {
					// it wasn't an Ack, just updated flow info
					// from a received data message.
					//q("%s sender Gotack: just updated flow control, continuing sendloop", s.Inbox)
					continue sendloop
				}
				//q("%s sender Gotack: more than just flowcontrol...", s.Inbox)
				delete(s.SentButNotAcked, a.AckNum)
				if !InWindow(a.AckNum, s.LastAckRec+1, s.LastFrameSent) {
					//q("%v a.AckNum = %v outside sender's window [%v, %v], dropping it.",
					//	s.Inbox, a.AckNum, s.LastAckRec+1, s.LastFrameSent)
					s.DiscardCount++
					continue sendloop
				}
				//q("%v packet.AckNum = %v inside sender's window, keeping it.", s.Inbox, a.AckNum)
				for {
					s.LastAckRec++

					// release the send slot
					// do this before changing slot, since we point into slot.
					delete(s.SentButNotAcked, s.LastAckRec)

					//slot := s.Txq[s.LastAckRec%s.SenderWindowSize]
					//q("%v ... slot = %#v", s.Inbox, slot)

					if s.LastAckRec == a.AckNum {
						//q("%v s.LastAskRec[%v] matches a.AckNum[%v], breaking",
						//	s.Inbox, s.LastAckRec, a.AckNum)
						break
					}
					//q("%v s.LastAskRec[%v] != a.AckNum[%v], looping",
					//	s.Inbox, s.LastAckRec, a.AckNum)
				}
			case ackPack := <-s.SendAck:
				// request to send an ack:
				// don't go though the BlockingSend protocol; since
				// could effectively livelock us.
				q("%v doing Net.Send() SendAck request on ackPack: '%#v'",
					s.Inbox, ackPack)
				err := s.Net.Send(ackPack, "SendAck/ackPack")
				panicOn(err)
			}
		}
	}()
}

// Stop the SenderState componennt
func (s *SenderState) Stop() {
	s.mut.Lock()
	select {
	case <-s.ReqStop:
	default:
		close(s.ReqStop)
	}
	s.mut.Unlock()
	<-s.Done
}

// for first time sends of data, not retries or acks.
func (s *SenderState) doOrigDataSend(pack *Packet) {
	//q("%v sender in acceptSend", s.Inbox)

	s.LastFrameSent++
	//q("%v LastFrameSent is now %v", s.Inbox, s.LastFrameSent)

	s.TotalBytesSent += int64(len(pack.Data))
	pack.CumulBytesTransmitted = s.TotalBytesSent

	lfs := s.LastFrameSent
	pos := lfs % s.SenderWindowSize
	slot := s.Txq[pos]

	pack.SeqNum = lfs
	if pack.From != s.Inbox {
		pack.From = s.Inbox
	}
	pack.From = s.Inbox
	slot.Pack = pack
	// data sends get stored in SentButNotAcked
	s.SentButNotAcked[lfs] = slot

	now := time.Now()
	s.SendHistory = append(s.SendHistory, pack)
	slot.RetryDeadline = now.Add(s.Timeout)
	s.LastSendTime = now

	flow := s.FlowCt.UpdateFlow(s.Inbox+":sender", s.Net, -1, -1)
	//q("%v doSend(), flow = '%#v'", s.Inbox, flow)
	pack.AvailReaderBytesCap = flow.AvailReaderBytesCap
	pack.AvailReaderMsgCap = flow.AvailReaderMsgCap
	err := s.Net.Send(slot.Pack, fmt.Sprintf("doSend() for %v", s.Inbox))
	panicOn(err)
}

func (s *SenderState) doKeepAlive() {
	if time.Since(s.LastSendTime) < s.KeepAliveInterval {
		return
	}
	flow := s.FlowCt.UpdateFlow(s.Inbox+":sender", s.Net, -1, -1)
	//q("%v doKeepAlive(), flow = '%#v'", s.Inbox, flow)
	// send a packet with no data, to elicit an ack
	// with a new advertised window. This is
	// *not* an ack, because we need it to be
	// acked itself so we get any updated
	// flow control info from the other end.
	s.LastSendTime = time.Now()
	kap := &Packet{
		From:                s.Inbox,
		Dest:                s.Dest,
		SeqNum:              -777, // => keepalive
		KeepAlive:           true,
		AvailReaderBytesCap: flow.AvailReaderBytesCap,
		AvailReaderMsgCap:   flow.AvailReaderMsgCap,
	}
	//q("%v doing keepalive Net.Send()", s.Inbox)
	err := s.Net.Send(kap, fmt.Sprintf("keepalive from %v", s.Inbox))
	panicOn(err)

	s.keepAlive = time.After(s.KeepAliveInterval)
}

func min(a, b int64) int64 {
	if a < b {
		return a
	}
	return b
}
<commit_msg>atg. polish/doc<commit_after>package swp

import (
	"fmt"
	"sync"
	"time"
)

// AckStatus conveys info from the receiver to the sender when an Ack is received.
type AckStatus struct {
	OnlyUpdateFlowCtrl  bool // don't send ack, just update flow info.
	AckNum              Seqno
	AckCameWithPacket   Seqno
	AvailReaderBytesCap int64 // for sender throttling/flow-control
	AvailReaderMsgCap   int64 // for sender throttling/flow-control
}

// SenderState tracks the sender's sliding window state.
// To avoid circulate deadlocks, the Sender never talks
// directly to the RecvState. The RecvState will
// tell the Sender stuff on GotAck.
type SenderState struct {
	Net              Network
	Inbox            string
	Dest             string
	LastAckRec       Seqno
	LastFrameSent    Seqno
	Txq              []*TxqSlot
	SenderWindowSize Seqno
	mut              sync.Mutex
	Timeout          time.Duration

	// the main goroutine safe way to request
	// sending a packet:
	BlockingSend chan *Packet
	GotAck       chan AckStatus

	ReqStop      chan bool
	Done         chan bool
	SendHistory  []*Packet
	SendSz       int64
	SendAck      chan *Packet
	DiscardCount int64

	LastSendTime      time.Time
	KeepAliveInterval time.Duration
	keepAlive         <-chan time.Time

	SentButNotAcked map[Seqno]*TxqSlot

	// flow control params
	// last seen from our downstream
	// receiver, we throttle ourselves
	// based on these.
	LastSeenAvailReaderBytesCap int64
	LastSeenAvailReaderMsgCap   int64

	// do synchronized access via GetFlow()
	// and UpdateFlow(s.Net)
	FlowCt         *FlowCtrl
	TotalBytesSent int64
}

// NewSenderState constructs a new SenderState struct.
func NewSenderState(net Network, sendSz int64, timeout time.Duration,
	inbox string, destInbox string) *SenderState {
	s := &SenderState{
		Net:              net,
		Inbox:            inbox,
		Dest:             destInbox,
		SenderWindowSize: Seqno(sendSz),
		Txq:              make([]*TxqSlot, sendSz),
		Timeout:          timeout,
		LastFrameSent:    -1,
		LastAckRec:       -1,
		ReqStop:          make(chan bool),
		Done:             make(chan bool),
		SendHistory:      make([]*Packet, 0),
		BlockingSend:     make(chan *Packet),
		SendSz:           sendSz,
		GotAck:           make(chan AckStatus),
		SendAck:          make(chan *Packet),
		SentButNotAcked:  make(map[Seqno]*TxqSlot),

		// send keepalives (important especially for resuming flow from a
		// stopped state) at least this often:
		KeepAliveInterval: 100 * time.Millisecond,
		FlowCt: &FlowCtrl{flow: Flow{
			ReservedByteCap: 64 * 1024,
			ReservedMsgCap:  32,
		}},
		// don't start fast, as we could overwhelm
		// the receiver. Instead start very slowly,
		// allowing 2 messages so our 003 reorder test runs.
		// It will get updated after the first ack
		// or keep alive.
		LastSeenAvailReaderMsgCap:   2,
		LastSeenAvailReaderBytesCap: 1024 * 1024,
	}
	for i := range s.Txq {
		s.Txq[i] = &TxqSlot{}
	}
	return s
}

// ComputeInflight returns the number of bytes and messages
// that are in-flight: they have been sent but not yet acked.
func (s *SenderState) ComputeInflight() (bytesInflight int64, msgInflight int64) {
	for _, slot := range s.SentButNotAcked {
		msgInflight++
		bytesInflight += int64(len(slot.Pack.Data))
	}
	return
}

// Start initiates the SenderState goroutine, which manages
// sends, timeouts, and resends
func (s *SenderState) Start() {

	go func() {

		var acceptSend chan *Packet

		// check for expired timers at wakeFreq
		wakeFreq := s.Timeout / 2

		// send keepalives (for resuming flow from a
		// stopped state) at least this often:
		s.keepAlive = time.After(s.KeepAliveInterval)

		regularIntervalWakeup := time.After(wakeFreq)

	sendloop:
		for {
			//q("%v top of sendloop, sender LAR: %v, LFS: %v \n",
			//	s.Inbox, s.LastAckRec, s.LastFrameSent)

			// does the downstream reader have capacity to accept a send?
			// Block any new sends if so. We do a conditional receive. Start by
			// assuming no:
			acceptSend = nil

			// then check if we can set acceptSend.
			//
			// We accept a packet for sending if flow control info
			// from the receiver allows it.
			//
			bytesInflight, msgInflight := s.ComputeInflight()
			//q("%v bytesInflight = %v", s.Inbox, bytesInflight)
			//q("%v msgInflight = %v", s.Inbox, msgInflight)

			if s.LastSeenAvailReaderMsgCap-msgInflight > 0 &&
				s.LastSeenAvailReaderBytesCap-bytesInflight > 0 {
				q("%v flow-control: okay to send. s.LastSeenAvailReaderMsgCap: %v > msgInflight: %v",
					s.Inbox, s.LastSeenAvailReaderMsgCap, msgInflight)
				acceptSend = s.BlockingSend
			} else {
				q("%v flow-control kicked in: not sending. s.LastSeenAvailReaderMsgCap = %v,"+
					" msgInflight=%v, s.LastSeenAvailReaderBytesCap=%v bytesInflight=%v",
					s.Inbox, s.LastSeenAvailReaderMsgCap, msgInflight,
					s.LastSeenAvailReaderBytesCap, bytesInflight)
			}

			q("%v top of sender select loop", s.Inbox)
			select {
			case <-s.keepAlive:
				q("%v keepAlive at %v", s.Inbox, time.Now())
				s.doKeepAlive()

			case <-regularIntervalWakeup:
				q("%v regularIntervalWakeup at %v", s.Inbox, time.Now())

				// have any of our packets timed-out and need to be
				// sent again?
				retry := []*TxqSlot{}
				for _, slot := range s.SentButNotAcked {
					if slot.RetryDeadline.Before(time.Now()) {
						retry = append(retry, slot)
					}
				}
			doRetryLoop:
				for _, slot := range retry {
					if slot.Pack == nil {
						//q("retry loop, slot = %#v", slot)
					}
					_, ok := s.SentButNotAcked[slot.Pack.SeqNum]
					if !ok {
						//q("already acked and gone from SentButNotAcked, so skip SeqNum %v and PopTop",
						//	slot.Pack.SeqNum)
						continue doRetryLoop
					}
					if slot.Pack.SeqNum <= s.LastAckRec {
						//q("already acked; is <= s.LastAckRecv (%v), so skip SeqNum %v and PopTop",
						//	s.LastAckRec, slot.Pack.SeqNum)
						continue doRetryLoop
					}

					// reset deadline and resend
					slot.RetryDeadline = time.Now().Add(s.Timeout)

					flow := s.FlowCt.UpdateFlow(s.Inbox, s.Net, -1, -1)
					slot.Pack.AvailReaderBytesCap = flow.AvailReaderBytesCap
					slot.Pack.AvailReaderMsgCap = flow.AvailReaderMsgCap
					q("%v doing retry Net.Send() for pack = '%#v' of paydirt '%s'",
						s.Inbox, slot.Pack, string(slot.Pack.Data))
					err := s.Net.Send(slot.Pack, "retry")
					panicOn(err)
				}
				regularIntervalWakeup = time.After(wakeFreq)

			case <-s.ReqStop:
				close(s.Done)
				return
			case pack := <-acceptSend:
				q("%v got <-acceptSend pack: '%#v'", s.Inbox, pack)
				s.doOrigDataSend(pack)

			case a := <-s.GotAck:
				// ack received - do sender side stuff
				//
				q("%v sender GotAck a: %#v", s.Inbox, a)
				//
				// flow control: respect a.AvailReaderBytesCap
				// and a.AvailReaderMsgCap info that we have
				// received from this ack
				//
				q("%v sender GotAck, updating s.LastSeenAvailReaderMsgCap %v -> %v",
					s.Inbox, s.LastSeenAvailReaderMsgCap, a.AvailReaderMsgCap)
				s.LastSeenAvailReaderBytesCap = a.AvailReaderBytesCap
				s.LastSeenAvailReaderMsgCap = a.AvailReaderMsgCap

				// need to update our map of SentButNotAcked
				// and remove everything before AckNum, which is cumulative.
				for _, slot := range s.SentButNotAcked {
					if slot.Pack.SeqNum <= a.AckNum {
						delete(s.SentButNotAcked, a.AckNum)
					}
				}

				if a.OnlyUpdateFlowCtrl {
					// it wasn't an Ack, just updated flow info
					// from a received data message.
					//q("%s sender Gotack: just updated flow control, continuing sendloop", s.Inbox)
					continue sendloop
				}
				//q("%s sender Gotack: more than just flowcontrol...", s.Inbox)
				delete(s.SentButNotAcked, a.AckNum)
				if !InWindow(a.AckNum, s.LastAckRec+1, s.LastFrameSent) {
					//q("%v a.AckNum = %v outside sender's window [%v, %v], dropping it.",
					//	s.Inbox, a.AckNum, s.LastAckRec+1, s.LastFrameSent)
					s.DiscardCount++
					continue sendloop
				}
				//q("%v packet.AckNum = %v inside sender's window, keeping it.", s.Inbox, a.AckNum)
				for {
					s.LastAckRec++

					// release the send slot
					// do this before changing slot, since we point into slot.
					delete(s.SentButNotAcked, s.LastAckRec)

					//slot := s.Txq[s.LastAckRec%s.SenderWindowSize]
					//q("%v ... slot = %#v", s.Inbox, slot)

					if s.LastAckRec == a.AckNum {
						//q("%v s.LastAskRec[%v] matches a.AckNum[%v], breaking",
						//	s.Inbox, s.LastAckRec, a.AckNum)
						break
					}
					//q("%v s.LastAskRec[%v] != a.AckNum[%v], looping",
					//	s.Inbox, s.LastAckRec, a.AckNum)
				}
			case ackPack := <-s.SendAck:
				// request to send an ack:
				// don't go though the BlockingSend protocol; since
				// could effectively livelock us.
				q("%v doing Net.Send() SendAck request on ackPack: '%#v'",
					s.Inbox, ackPack)
				err := s.Net.Send(ackPack, "SendAck/ackPack")
				panicOn(err)
			}
		}
	}()
}

// Stop the SenderState componennt
func (s *SenderState) Stop() {
	s.mut.Lock()
	select {
	case <-s.ReqStop:
	default:
		close(s.ReqStop)
	}
	s.mut.Unlock()
	<-s.Done
}

// for first time sends of data, not retries or acks.
func (s *SenderState) doOrigDataSend(pack *Packet) {
	//q("%v sender in acceptSend", s.Inbox)

	s.LastFrameSent++
	//q("%v LastFrameSent is now %v", s.Inbox, s.LastFrameSent)

	s.TotalBytesSent += int64(len(pack.Data))
	pack.CumulBytesTransmitted = s.TotalBytesSent

	lfs := s.LastFrameSent
	pos := lfs % s.SenderWindowSize
	slot := s.Txq[pos]

	pack.SeqNum = lfs
	if pack.From != s.Inbox {
		pack.From = s.Inbox
	}
	pack.From = s.Inbox
	slot.Pack = pack
	// data sends get stored in SentButNotAcked
	s.SentButNotAcked[lfs] = slot

	now := time.Now()
	s.SendHistory = append(s.SendHistory, pack)
	slot.RetryDeadline = now.Add(s.Timeout)
	s.LastSendTime = now

	flow := s.FlowCt.UpdateFlow(s.Inbox+":sender", s.Net, -1, -1)
	//q("%v doSend(), flow = '%#v'", s.Inbox, flow)
	pack.AvailReaderBytesCap = flow.AvailReaderBytesCap
	pack.AvailReaderMsgCap = flow.AvailReaderMsgCap
	err := s.Net.Send(slot.Pack, fmt.Sprintf("doSend() for %v", s.Inbox))
	panicOn(err)
}

func (s *SenderState) doKeepAlive() {
	if time.Since(s.LastSendTime) < s.KeepAliveInterval {
		return
	}
	flow := s.FlowCt.UpdateFlow(s.Inbox+":sender", s.Net, -1, -1)
	//q("%v doKeepAlive(), flow = '%#v'", s.Inbox, flow)
	// send a packet with no data, to elicit an ack
	// with a new advertised window. This is
	// *not* an ack, because we need it to be
	// acked itself so we get any updated
	// flow control info from the other end.
	s.LastSendTime = time.Now()
	kap := &Packet{
		From:                s.Inbox,
		Dest:                s.Dest,
		SeqNum:              -777, // => keepalive
		KeepAlive:           true,
		AvailReaderBytesCap: flow.AvailReaderBytesCap,
		AvailReaderMsgCap:   flow.AvailReaderMsgCap,
	}
	//q("%v doing keepalive Net.Send()", s.Inbox)
	err := s.Net.Send(kap, fmt.Sprintf("keepalive from %v", s.Inbox))
	panicOn(err)

	s.keepAlive = time.After(s.KeepAliveInterval)
}

func min(a, b int64) int64 {
	if a < b {
		return a
	}
	return b
}
<|endoftext|>


<commit_before>package instana

import (
	"errors"
	"os"

	"github.com/instana/go-sensor/autoprofile"
	"github.com/instana/go-sensor/logger"
)

const (
	DefaultMaxBufferedSpans = 1000
	DefaultForceSpanSendAt  = 500
)

type agentClient interface {
	Ready() bool
	SendMetrics(data *MetricsS) error
	SendEvent(event *EventData) error
	SendSpans(spans []Span) error
	SendProfiles(profiles []autoprofile.Profile) error
}

type sensorS struct {
	meter       *meterS
	agent       agentClient
	logger      LeveledLogger
	options     *Options
	serviceName string
}

var sensor *sensorS

func newSensor(options *Options) *sensorS {
	if options == nil {
		options = DefaultOptions()
	} else {
		options.setDefaults()
	}

	s := &sensorS{
		options:     options,
		serviceName: options.Service,
	}
	s.setLogger(defaultLogger)

	// override service name with an env value if set
	if name, ok := os.LookupEnv("INSTANA_SERVICE_NAME"); ok {
		s.serviceName = name
	}

	// handle the legacy (instana.Options).LogLevel value if we use logger.Logger to log
	if l, ok := s.logger.(*logger.Logger); ok {
		setLogLevel(l, options.LogLevel)
	}

	s.agent = newAgent(s.serviceName, s.options.AgentHost, s.options.AgentPort, s.logger)
	s.meter = newMeter(s.agent, s.logger)

	return s
}

func (r *sensorS) setLogger(l LeveledLogger) {
	r.logger = l

	if agent, ok := r.agent.(*agentS); ok && agent != nil {
		agent.setLogger(r.logger)
	}

	if r.meter != nil {
		r.meter.setLogger(r.logger)
	}
}

// InitSensor intializes the sensor (without tracing) to begin collecting
// and reporting metrics.
func InitSensor(options *Options) {
	if sensor != nil {
		return
	}

	sensor = newSensor(options)

	// configure auto-profiling
	autoprofile.SetLogger(sensor.logger)
	autoprofile.SetOptions(autoprofile.Options{
		IncludeProfilerFrames: options.IncludeProfilerFrames,
		MaxBufferedProfiles:   options.MaxBufferedProfiles,
	})

	autoprofile.SetSendProfilesFunc(func(profiles []autoprofile.Profile) error {
		if !sensor.agent.Ready() {
			return errors.New("sender not ready")
		}

		sensor.logger.Debug("sending profiles to agent")

		return sensor.agent.SendProfiles(profiles)
	})

	if options.EnableAutoProfile {
		autoprofile.Enable()
	}

	sensor.logger.Debug("initialized sensor")
}
<commit_msg>Activate AutoProfile™ if INSTANA_AUTO_PROFILE env var is set<commit_after>package instana

import (
	"errors"
	"os"

	"github.com/instana/go-sensor/autoprofile"
	"github.com/instana/go-sensor/logger"
)

const (
	DefaultMaxBufferedSpans = 1000
	DefaultForceSpanSendAt  = 500
)

type agentClient interface {
	Ready() bool
	SendMetrics(data *MetricsS) error
	SendEvent(event *EventData) error
	SendSpans(spans []Span) error
	SendProfiles(profiles []autoprofile.Profile) error
}

type sensorS struct {
	meter       *meterS
	agent       agentClient
	logger      LeveledLogger
	options     *Options
	serviceName string
}

var sensor *sensorS

func newSensor(options *Options) *sensorS {
	if options == nil {
		options = DefaultOptions()
	} else {
		options.setDefaults()
	}

	s := &sensorS{
		options:     options,
		serviceName: options.Service,
	}
	s.setLogger(defaultLogger)

	// override service name with an env value if set
	if name, ok := os.LookupEnv("INSTANA_SERVICE_NAME"); ok {
		s.serviceName = name
	}

	// handle the legacy (instana.Options).LogLevel value if we use logger.Logger to log
	if l, ok := s.logger.(*logger.Logger); ok {
		setLogLevel(l, options.LogLevel)
	}

	s.agent = newAgent(s.serviceName, s.options.AgentHost, s.options.AgentPort, s.logger)
	s.meter = newMeter(s.agent, s.logger)

	return s
}

func (r *sensorS) setLogger(l LeveledLogger) {
	r.logger = l

	if agent, ok := r.agent.(*agentS); ok && agent != nil {
		agent.setLogger(r.logger)
	}

	if r.meter != nil {
		r.meter.setLogger(r.logger)
	}
}

// InitSensor intializes the sensor (without tracing) to begin collecting
// and reporting metrics.
func InitSensor(options *Options) {
	if sensor != nil {
		return
	}

	sensor = newSensor(options)

	// configure auto-profiling
	autoprofile.SetLogger(sensor.logger)
	autoprofile.SetOptions(autoprofile.Options{
		IncludeProfilerFrames: options.IncludeProfilerFrames,
		MaxBufferedProfiles:   options.MaxBufferedProfiles,
	})

	autoprofile.SetSendProfilesFunc(func(profiles []autoprofile.Profile) error {
		if !sensor.agent.Ready() {
			return errors.New("sender not ready")
		}

		sensor.logger.Debug("sending profiles to agent")

		return sensor.agent.SendProfiles(profiles)
	})

	if _, ok := os.LookupEnv("INSTANA_AUTO_PROFILE"); ok || options.EnableAutoProfile {
		if !options.EnableAutoProfile {
			sensor.logger.Info("INSTANA_AUTO_PROFILE is set, activating AutoProfile™")
		}

		autoprofile.Enable()
	}

	sensor.logger.Debug("initialized sensor")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"

	"github.com/codegangsta/martini"
	"github.com/codegangsta/martini-contrib/render"
)

const version = "0.1.4"

func printDocument(res render.Render, req *http.Request, w http.ResponseWriter) {
	fetchDocument := func(url string) (*os.File, error) {
		if url == "" {
			err := fmt.Errorf("URL is blank")
			return nil, err
		}

		os.Mkdir("./tmp", 0700)

		out, _ := ioutil.TempFile("tmp", "document")
		defer out.Close()

		resp, err := http.Get(url)
		defer resp.Body.Close()

		io.Copy(out, resp.Body)

		return out, err
	}

	removeDocument := func(name string) error {
		return os.Remove(name)
	}

	sendToPrinter := func(url string, printer string, orientation string, media string) ([]string, string, error) {
		document, err := fetchDocument(url)
		if err != nil {
			log.Println(err)
			return nil, "", err
		}
		defer removeDocument(document.Name())

		var printOptions []string

		if printer != "" {
			printOptions = append(printOptions, "-P", printer)
		}
		if orientation != "" {
			printOptions = append(printOptions, "-o", orientation)
		}
		if media != "" {
			printOptions = append(printOptions, "-o", fmt.Sprintf("media=%s", media))
		}
		printOptions = append(printOptions, document.Name())

		cmd := exec.Command("lpr", printOptions...)
		var out bytes.Buffer
		cmd.Stdout = &out
		err = cmd.Run()
		if err != nil {
			log.Println(err)
			return nil, "", err
		}
		return printOptions, out.String(), nil
	}

	qs := req.URL.Query()
	printer, orientation, media, url := qs.Get("printer"), qs.Get("orientation"), qs.Get("media"), qs.Get("path")

	go sendToPrinter(url, printer, orientation, media)

	w.Header().Set("Access-Control-Allow-Origin", "*")

	res.JSON(200, map[string]interface{}{
		"status": "scheduled",
	})
}

func cancelAll(res render.Render, req *http.Request, w http.ResponseWriter) {
	qs := req.URL.Query()
	printer := qs.Get("printer")

	cmd := exec.Command("cancel", "-a", "-x", printer)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Run()

	w.Header().Set("Access-Control-Allow-Origin", "*")

	res.JSON(200, map[string]interface{}{
		"success": true,
		"printer": printer,
	})
}

func currentUser() (s string) {
	u, _ := user.Current()
	return u.Username
}

func main() {
	m := martini.Classic()
	m.Use(render.Renderer())
	m.Use(martini.Logger())

	m.Get("/", func() string {
		return "
Receipt Printer
"
	})

	m.Get("/cache/clear", func(res render.Render, w http.ResponseWriter) {
		os.RemoveAll(filepath.Join("./tmp/"))
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true})
	})

	m.Get("/print", printDocument)

	m.Get("/cancel/all", cancelAll)

	m.Get("/self_update", func(res render.Render, w http.ResponseWriter) {
		cmd := exec.Command("./update")
		cmd.Run()
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true, "version": version})
	})

	m.Get("/status", func(res render.Render, w http.ResponseWriter) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true, "version": version})
	})

	go func() {
		if err := http.ListenAndServe(":9632", m); err != nil {
			log.Fatal(err)
		}
	}()

	if err := http.ListenAndServeTLS(":9631", "cert.pem", "key.pem", m); err != nil {
		log.Fatal(err)
	}

	m.Run()
}
<commit_msg>Update version to 0.1.5<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"

	"github.com/codegangsta/martini"
	"github.com/codegangsta/martini-contrib/render"
)

const version = "0.1.5"

func printDocument(res render.Render, req *http.Request, w http.ResponseWriter) {
	fetchDocument := func(url string) (*os.File, error) {
		if url == "" {
			err := fmt.Errorf("URL is blank")
			return nil, err
		}

		os.Mkdir("./tmp", 0700)

		out, _ := ioutil.TempFile("tmp", "document")
		defer out.Close()

		resp, err := http.Get(url)
		defer resp.Body.Close()

		io.Copy(out, resp.Body)

		return out, err
	}

	removeDocument := func(name string) error {
		return os.Remove(name)
	}

	sendToPrinter := func(url string, printer string, orientation string, media string) ([]string, string, error) {
		document, err := fetchDocument(url)
		if err != nil {
			log.Println(err)
			return nil, "", err
		}
		defer removeDocument(document.Name())

		var printOptions []string

		if printer != "" {
			printOptions = append(printOptions, "-P", printer)
		}
		if orientation != "" {
			printOptions = append(printOptions, "-o", orientation)
		}
		if media != "" {
			printOptions = append(printOptions, "-o", fmt.Sprintf("media=%s", media))
		}
		printOptions = append(printOptions, document.Name())

		cmd := exec.Command("lpr", printOptions...)
		var out bytes.Buffer
		cmd.Stdout = &out
		err = cmd.Run()
		if err != nil {
			log.Println(err)
			return nil, "", err
		}
		return printOptions, out.String(), nil
	}

	qs := req.URL.Query()
	printer, orientation, media, url := qs.Get("printer"), qs.Get("orientation"), qs.Get("media"), qs.Get("path")

	go sendToPrinter(url, printer, orientation, media)

	w.Header().Set("Access-Control-Allow-Origin", "*")

	res.JSON(200, map[string]interface{}{
		"status": "scheduled",
	})
}

func cancelAll(res render.Render, req *http.Request, w http.ResponseWriter) {
	qs := req.URL.Query()
	printer := qs.Get("printer")

	cmd := exec.Command("cancel", "-a", "-x", printer)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Run()

	w.Header().Set("Access-Control-Allow-Origin", "*")

	res.JSON(200, map[string]interface{}{
		"success": true,
		"printer": printer,
	})
}

func currentUser() (s string) {
	u, _ := user.Current()
	return u.Username
}

func main() {
	m := martini.Classic()
	m.Use(render.Renderer())
	m.Use(martini.Logger())

	m.Get("/", func() string {
		return "
Receipt Printer
"
	})

	m.Get("/cache/clear", func(res render.Render, w http.ResponseWriter) {
		os.RemoveAll(filepath.Join("./tmp/"))
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true})
	})

	m.Get("/print", printDocument)

	m.Get("/cancel/all", cancelAll)

	m.Get("/self_update", func(res render.Render, w http.ResponseWriter) {
		cmd := exec.Command("./update")
		cmd.Run()
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true, "version": version})
	})

	m.Get("/status", func(res render.Render, w http.ResponseWriter) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		res.JSON(200, map[string]interface{}{"success": true, "version": version})
	})

	go func() {
		if err := http.ListenAndServe(":9632", m); err != nil {
			log.Fatal(err)
		}
	}()

	if err := http.ListenAndServeTLS(":9631", "cert.pem", "key.pem", m); err != nil {
		log.Fatal(err)
	}

	m.Run()
}
<|endoftext|>


<commit_before>package beaker

import (
	"fmt"
	"io/ioutil"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/mebiusashan/beaker/cache"
	"github.com/mebiusashan/beaker/cert"
	"github.com/mebiusashan/beaker/common"
	"github.com/mebiusashan/beaker/config"
	"github.com/mebiusashan/beaker/controller"
	"github.com/mebiusashan/beaker/database"
	"github.com/mebiusashan/beaker/net"
	"github.com/mebiusashan/beaker/view"
)

func RunServer(isRelease bool) {
	path := os.Getenv(common.SERVER_ENV_KEY)
	config, err := config.NewWithPath(path, 0x1B)
	common.Assert(err)

	cac := cache.NewCache(config.Redis.REDIS_IP, config.Redis.REDIS_PORT, config.Redis.REDIS_PREFIX)
	model, err := database.NewDao(config.Database.DB_URL, config.Database.DB_USER, config.Database.DB_PW, config.Database.DB_NAME)
	common.Assert(err)

	model.SetMaxIdleConns(config.Database.MAX_IDLE_NUM)
	model.SetMaxOpenConns(config.Database.MAX_OPEN_NUM)

	vr := view.NewViewRender(config.Website.TEMP_FOLDER)
	vr.SetDefaultVar(config.Website.SITE_NAME, config.Server.SITE_URL, config.Website.SITE_DES, config.Website.SITE_FOOTER, config.Website.SITE_KEYWORDS)

	context := controller.NewContext()
	context.Cache = cac
	context.Config = config
	context.Model = model
	context.View = vr

	if isRelease {
		gin.SetMode(gin.ReleaseMode)
	}

	router := gin.Default()
	fmt.Println(config.Server.STATIC_FILE_FOLDER)
	router.Static("/static", config.Server.STATIC_FILE_FOLDER)
	router.StaticFile("/b.css", config.Server.STATIC_FILE_FOLDER+"/b.css")
	router.StaticFile("/favicon.ico", config.Server.STATIC_FILE_FOLDER+"/favicon.ico")

	router.GET(net.SERVER_INDEX, context.Ctrl.IndC.Do)
	router.GET(net.SERVER_TWEET, context.Ctrl.TweC.Do)
	router.GET(net.SERVER_PAGE, context.Ctrl.PagC.Do)
	router.GET(net.SERVER_CAT, context.Ctrl.CatC.Do)
	router.GET(net.SERVER_ART, context.Ctrl.ArtC.Do)

	router.NoMethod(context.Ctrl.ErrC.Do404)
	router.NoRoute(context.Ctrl.ErrC.Do404)

	router.Run(config.Server.URL + config.Server.PORT)
}

func RunAdmin(isRelease bool) {
	path := os.Getenv(common.ADMIN_ENV_KEY)
	config, err := config.NewWithPath(path, 0x1D)
	common.Assert(err)

	cac := cache.NewCache(config.Redis.REDIS_IP, config.Redis.REDIS_PORT, config.Redis.REDIS_PREFIX)
	model, err := database.NewDao(config.Database.DB_URL, config.Database.DB_USER, config.Database.DB_PW, config.Database.DB_NAME)
	common.Assert(err)
	model.SetMaxIdleConns(config.Database.MAX_IDLE_NUM)
	model.SetMaxOpenConns(config.Database.MAX_OPEN_NUM)

	context := controller.NewContext()
	context.Cache = cac
	context.Config = config
	context.Model = model

	pubHas, err := controller.PathExists(config.AuthInfo.ServerKeyDir + common.SERVER_PUBLIC_KEY)
	common.Assert(err)

	priHas, err := controller.PathExists(config.AuthInfo.ServerKeyDir + common.SERVER_PRIVATE_KEY)
	common.Assert(err)

	if !pubHas || !priHas {
		pub, pri, err := cert.CreateRSAKeys()
		common.Assert(err)
		err = ioutil.WriteFile(config.AuthInfo.ServerKeyDir+common.SERVER_PUBLIC_KEY, pub, 0666)
		common.Assert(err)
		err = ioutil.WriteFile(config.AuthInfo.ServerKeyDir+common.SERVER_PRIVATE_KEY, pri, 0666)
		common.Assert(err)
	}

	pub, err := ioutil.ReadFile(config.AuthInfo.ServerKeyDir + common.SERVER_PUBLIC_KEY)
	common.Assert(err)

	pri, err := ioutil.ReadFile(config.AuthInfo.ServerKeyDir + common.SERVER_PRIVATE_KEY)
	common.Assert(err)

	rel := cert.CheckRSAKey(pub, pri)
	if !rel {
		common.Err("Secret key verification failed")
	}

	if isRelease {
		gin.SetMode(gin.ReleaseMode)
	}

	router := gin.Default()

	//登录
	user := router.Group(net.ADMIN_GROUP_USER)
	user.POST(net.ADMIN_PING, context.Ctrl.LoginC.Ping)
	user.POST(net.ADMIN_LOGIN, context.Ctrl.LoginC.Login)
	user.POST(net.ADMIN_CHECK, context.Ctrl.LoginC.Check)

	adminr := router.Group(net.ADMIN_GROUP_ADMIN)
	adminr.Use(controller.LoginExpiredCheck())
	adminr.Use(controller.DecodeForAdmin())
	adminr.Use(controller.RefreshCache())

	adminr.POST(net.ADMIN_ART_ADD, context.Ctrl.ArtC.Add)
	adminr.POST(net.ADMIN_ART_RM, context.Ctrl.ArtC.Del)
	adminr.POST(net.ADMIN_ART_LIST, context.Ctrl.ArtC.All)
	adminr.POST(net.ADMIN_ART_DOWNLOAD, context.Ctrl.ArtC.Down)
	adminr.POST(net.ADMIN_ART_MODIFY, context.Ctrl.ArtC.Modify)

	adminr.POST(net.ADMIN_PAGE_ADD, context.Ctrl.PagC.Add)
	adminr.POST(net.ADMIN_PAGE_RM, context.Ctrl.PagC.Del)
	adminr.POST(net.ADMIN_PAGE_LIST, context.Ctrl.PagC.List)
	adminr.POST(net.ADMIN_PAGE_DOWNLOAD, context.Ctrl.PagC.Down)
	adminr.POST(net.ADMIN_PAGE_MODIFY, context.Ctrl.PagC.Modify)

	adminr.POST(net.ADMIN_TWEET_ADD, context.Ctrl.TweC.Add)
	adminr.POST(net.ADMIN_TWEET_RM, context.Ctrl.TweC.Del)
	adminr.POST(net.ADMIN_TWEET_LIST, context.Ctrl.TweC.List)

	adminr.POST(net.ADMIN_CAT_ADD, context.Ctrl.CatC.Add)
	adminr.POST(net.ADMIN_CAT_RM, context.Ctrl.CatC.Del)
	adminr.POST(net.ADMIN_CAT_LIST, context.Ctrl.CatC.All)
	adminr.POST(net.ADMIN_CAT_MODIFY, context.Ctrl.CatC.Update)

	adminr.POST(net.ADMIN_OPT, context.Ctrl.OptC.Info)
	adminr.POST(net.ADMIN_CLEAN, context.Ctrl.OptC.ClearCache)

	router.Run(config.Server.PORT)
}
<commit_msg>remove debug info<commit_after>package beaker

import (
	"io/ioutil"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/mebiusashan/beaker/cache"
	"github.com/mebiusashan/beaker/cert"
	"github.com/mebiusashan/beaker/common"
	"github.com/mebiusashan/beaker/config"
	"github.com/mebiusashan/beaker/controller"
	"github.com/mebiusashan/beaker/database"
	"github.com/mebiusashan/beaker/net"
	"github.com/mebiusashan/beaker/view"
)

func RunServer(isRelease bool) {
	path := os.Getenv(common.SERVER_ENV_KEY)
	config, err := config.NewWithPath(path, 0x1B)
	common.Assert(err)

	cac := cache.NewCache(config.Redis.REDIS_IP, config.Redis.REDIS_PORT, config.Redis.REDIS_PREFIX)
	model, err := database.NewDao(config.Database.DB_URL, config.Database.DB_USER, config.Database.DB_PW, config.Database.DB_NAME)
	common.Assert(err)

	model.SetMaxIdleConns(config.Database.MAX_IDLE_NUM)
	model.SetMaxOpenConns(config.Database.MAX_OPEN_NUM)

	vr := view.NewViewRender(config.Website.TEMP_FOLDER)
	vr.SetDefaultVar(config.Website.SITE_NAME, config.Server.SITE_URL, config.Website.SITE_DES, config.Website.SITE_FOOTER, config.Website.SITE_KEYWORDS)

	context := controller.NewContext()
	context.Cache = cac
	context.Config = config
	context.Model = model
	context.View = vr

	if isRelease {
		gin.SetMode(gin.ReleaseMode)
	}

	router := gin.Default()
	router.Static("/static", config.Server.STATIC_FILE_FOLDER)
	router.StaticFile("/b.css", config.Server.STATIC_FILE_FOLDER+"/b.css")
	router.StaticFile("/favicon.ico", config.Server.STATIC_FILE_FOLDER+"/favicon.ico")

	router.GET(net.SERVER_INDEX, context.Ctrl.IndC.Do)
	router.GET(net.SERVER_TWEET, context.Ctrl.TweC.Do)
	router.GET(net.SERVER_PAGE, context.Ctrl.PagC.Do)
	router.GET(net.SERVER_CAT, context.Ctrl.CatC.Do)
	router.GET(net.SERVER_ART, context.Ctrl.ArtC.Do)

	router.NoMethod(context.Ctrl.ErrC.Do404)
	router.NoRoute(context.Ctrl.ErrC.Do404)

	router.Run(config.Server.URL + config.Server.PORT)
}

func RunAdmin(isRelease bool) {
	path := os.Getenv(common.ADMIN_ENV_KEY)
	config, err := config.NewWithPath(path, 0x1D)
	common.Assert(err)

	cac := cache.NewCache(config.Redis.REDIS_IP, config.Redis.REDIS_PORT, config.Redis.REDIS_PREFIX)
	model, err := database.NewDao(config.Database.DB_URL, config.Database.DB_USER, config.Database.DB_PW, config.Database.DB_NAME)
	common.Assert(err)
	model.SetMaxIdleConns(config.Database.MAX_IDLE_NUM)
	model.SetMaxOpenConns(config.Database.MAX_OPEN_NUM)

	context := controller.NewContext()
	context.Cache = cac
	context.Config = config
	context.Model = model

	pubHas, err := controller.PathExists(config.AuthInfo.ServerKeyDir + common.SERVER_PUBLIC_KEY)
	common.Assert(err)

	priHas, err := controller.PathExists(config.AuthInfo.ServerKeyDir + common.SERVER_PRIVATE_KEY)
	common.Assert(err)

	if !pubHas || !priHas {
		pub, pri, err := cert.CreateRSAKeys()
		common.Assert(err)
		err = ioutil.WriteFile(config.AuthInfo.ServerKeyDir+common.SERVER_PUBLIC_KEY, pub, 0666)
		common.Assert(err)
		err = ioutil.WriteFile(config.AuthInfo.ServerKeyDir+common.SERVER_PRIVATE_KEY, pri, 0666)
		common.Assert(err)
	}

	pub, err := ioutil.ReadFile(config.AuthInfo.ServerKeyDir + common.SERVER_PUBLIC_KEY)
	common.Assert(err)

	pri, err := ioutil.ReadFile(config.AuthInfo.ServerKeyDir + common.SERVER_PRIVATE_KEY)
	common.Assert(err)

	rel := cert.CheckRSAKey(pub, pri)
	if !rel {
		common.Err("Secret key verification failed")
	}

	if isRelease {
		gin.SetMode(gin.ReleaseMode)
	}

	router := gin.Default()

	//登录
	user := router.Group(net.ADMIN_GROUP_USER)
	user.POST(net.ADMIN_PING, context.Ctrl.LoginC.Ping)
	user.POST(net.ADMIN_LOGIN, context.Ctrl.LoginC.Login)
	user.POST(net.ADMIN_CHECK, context.Ctrl.LoginC.Check)

	adminr := router.Group(net.ADMIN_GROUP_ADMIN)
	adminr.Use(controller.LoginExpiredCheck())
	adminr.Use(controller.DecodeForAdmin())
	adminr.Use(controller.RefreshCache())

	adminr.POST(net.ADMIN_ART_ADD, context.Ctrl.ArtC.Add)
	adminr.POST(net.ADMIN_ART_RM, context.Ctrl.ArtC.Del)
	adminr.POST(net.ADMIN_ART_LIST, context.Ctrl.ArtC.All)
	adminr.POST(net.ADMIN_ART_DOWNLOAD, context.Ctrl.ArtC.Down)
	adminr.POST(net.ADMIN_ART_MODIFY, context.Ctrl.ArtC.Modify)

	adminr.POST(net.ADMIN_PAGE_ADD, context.Ctrl.PagC.Add)
	adminr.POST(net.ADMIN_PAGE_RM, context.Ctrl.PagC.Del)
	adminr.POST(net.ADMIN_PAGE_LIST, context.Ctrl.PagC.List)
	adminr.POST(net.ADMIN_PAGE_DOWNLOAD, context.Ctrl.PagC.Down)
	adminr.POST(net.ADMIN_PAGE_MODIFY, context.Ctrl.PagC.Modify)

	adminr.POST(net.ADMIN_TWEET_ADD, context.Ctrl.TweC.Add)
	adminr.POST(net.ADMIN_TWEET_RM, context.Ctrl.TweC.Del)
	adminr.POST(net.ADMIN_TWEET_LIST, context.Ctrl.TweC.List)

	adminr.POST(net.ADMIN_CAT_ADD, context.Ctrl.CatC.Add)
	adminr.POST(net.ADMIN_CAT_RM, context.Ctrl.CatC.Del)
	adminr.POST(net.ADMIN_CAT_LIST, context.Ctrl.CatC.All)
	adminr.POST(net.ADMIN_CAT_MODIFY, context.Ctrl.CatC.Update)

	adminr.POST(net.ADMIN_OPT, context.Ctrl.OptC.Info)
	adminr.POST(net.ADMIN_CLEAN, context.Ctrl.OptC.ClearCache)

	router.Run(config.Server.PORT)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package fuse

import (
	"fmt"
	"io"
	"log"

	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"

	bazilfuse "bazil.org/fuse"
)

// An object that terminates one end of the userspace <-> FUSE VFS connection.
type server struct {
	logger *log.Logger
	clock  timeutil.Clock
	fs     FileSystem
}

// Create a server that relays requests to the supplied file system.
func newServer(fs FileSystem) (s *server, err error) {
	s = &server{
		logger: getLogger(),
		clock:  timeutil.RealClock(),
		fs:     fs,
	}

	return
}

func convertChildInodeEntry(
	clock timeutil.Clock,
	in *ChildInodeEntry,
	out *bazilfuse.LookupResponse) {
	out.Node = bazilfuse.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes)
	out.AttrValid = in.AttributesExpiration.Sub(clock.Now())
	out.EntryValid = in.EntryExpiration.Sub(clock.Now())
}

// Serve the fuse connection by repeatedly reading requests from the supplied
// FUSE connection, responding as dictated by the file system. Return when the
// connection is closed or an unexpected error occurs.
func (s *server) Serve(c *bazilfuse.Conn) (err error) {
	// Read a message at a time, dispatching to goroutines doing the actual
	// processing.
	for {
		var fuseReq bazilfuse.Request
		fuseReq, err = c.ReadRequest()

		// ReadRequest returns EOF when the connection has been closed.
		if err == io.EOF {
			err = nil
			return
		}

		// Otherwise, forward on errors.
		if err != nil {
			err = fmt.Errorf("Conn.ReadRequest: %v", err)
			return
		}

		go s.handleFuseRequest(fuseReq)
	}
}

func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
	// Log the request.
	s.logger.Println("Received:", fuseReq)

	// TODO(jacobsa): Support cancellation when interrupted, if we can coax the
	// system into reproducing such requests.
	ctx := context.Background()

	// Attempt to handle it.
	switch typed := fuseReq.(type) {
	case *bazilfuse.InitRequest:
		// Convert the request.
		req := &InitRequest{
			Uid: typed.Header.Uid,
			Gid: typed.Header.Gid,
		}

		// Call the file system.
		_, err := s.fs.Init(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.InitResponse{}
		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.StatfsRequest:
		// Responding to this is required to make mounting work, at least on OS X.
		// We don't currently expose the capability for the file system to
		// intercept this.
		fuseResp := &bazilfuse.StatfsResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.LookupRequest:
		// Convert the request.
		req := &LookUpInodeRequest{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
		}

		// Call the file system.
		resp, err := s.fs.LookUpInode(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.LookupResponse{}
		convertChildInodeEntry(s.clock, &resp.Entry, fuseResp)

		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.GetattrRequest:
		// Convert the request.
		req := &GetInodeAttributesRequest{
			Inode: InodeID(typed.Header.Node),
		}

		// Call the file system.
		resp, err := s.fs.GetInodeAttributes(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.GetattrResponse{
			Attr:      convertAttributes(req.Inode, resp.Attributes),
			AttrValid: resp.AttributesExpiration.Sub(s.clock.Now()),
		}

		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.MkdirRequest:
		// Convert the request.
		req := &MkDirRequest{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}

		// Call the file system.
		resp, err := s.fs.MkDir(ctx, req)
		if err != nil {
			s.logger.Print("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.MkdirResponse{}
		convertChildInodeEntry(s.clock, &resp.Entry, &fuseResp.LookupResponse)

		s.logger.Print("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.OpenRequest:
		// Directory or file?
		if typed.Dir {
			// Convert the request.
			req := &OpenDirRequest{
				Inode: InodeID(typed.Header.Node),
				Flags: typed.Flags,
			}

			// Call the file system.
			resp, err := s.fs.OpenDir(ctx, req)
			if err != nil {
				s.logger.Print("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.OpenResponse{
				Handle: bazilfuse.HandleID(resp.Handle),
			}

			s.logger.Print("Responding:", fuseResp)
			typed.Respond(fuseResp)
		} else {
			// Convert the request.
			req := &OpenFileRequest{
				Inode: InodeID(typed.Header.Node),
				Flags: typed.Flags,
			}

			// Call the file system.
			resp, err := s.fs.OpenFile(ctx, req)
			if err != nil {
				s.logger.Print("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.OpenResponse{
				Handle: bazilfuse.HandleID(resp.Handle),
			}

			s.logger.Print("Responding:", fuseResp)
			typed.Respond(fuseResp)
		}

	case *bazilfuse.ReadRequest:
		// Directory or file?
		if typed.Dir {
			// Convert the request.
			req := &ReadDirRequest{
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}

			// Call the file system.
			resp, err := s.fs.ReadDir(ctx, req)
			if err != nil {
				s.logger.Print("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.ReadResponse{
				Data: resp.Data,
			}

			s.logger.Print("Responding:", fuseResp)
			typed.Respond(fuseResp)
		} else {
			// Convert the request.
			req := &ReadFileRequest{
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}

			// Call the file system.
			resp, err := s.fs.ReadFile(ctx, req)
			if err != nil {
				s.logger.Print("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.ReadResponse{
				Data: resp.Data,
			}

			s.logger.Print("Responding:", fuseResp)
			typed.Respond(fuseResp)
		}

	default:
		s.logger.Println("Unhandled type. Returning ENOSYS.")
		typed.RespondError(ENOSYS)
	}
}

func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
	return bazilfuse.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
	}
}
<commit_msg>Fixed some log spacing.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package fuse

import (
	"fmt"
	"io"
	"log"

	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"

	bazilfuse "bazil.org/fuse"
)

// An object that terminates one end of the userspace <-> FUSE VFS connection.
type server struct {
	logger *log.Logger
	clock  timeutil.Clock
	fs     FileSystem
}

// Create a server that relays requests to the supplied file system.
func newServer(fs FileSystem) (s *server, err error) {
	s = &server{
		logger: getLogger(),
		clock:  timeutil.RealClock(),
		fs:     fs,
	}

	return
}

func convertChildInodeEntry(
	clock timeutil.Clock,
	in *ChildInodeEntry,
	out *bazilfuse.LookupResponse) {
	out.Node = bazilfuse.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes)
	out.AttrValid = in.AttributesExpiration.Sub(clock.Now())
	out.EntryValid = in.EntryExpiration.Sub(clock.Now())
}

// Serve the fuse connection by repeatedly reading requests from the supplied
// FUSE connection, responding as dictated by the file system. Return when the
// connection is closed or an unexpected error occurs.
func (s *server) Serve(c *bazilfuse.Conn) (err error) {
	// Read a message at a time, dispatching to goroutines doing the actual
	// processing.
	for {
		var fuseReq bazilfuse.Request
		fuseReq, err = c.ReadRequest()

		// ReadRequest returns EOF when the connection has been closed.
		if err == io.EOF {
			err = nil
			return
		}

		// Otherwise, forward on errors.
		if err != nil {
			err = fmt.Errorf("Conn.ReadRequest: %v", err)
			return
		}

		go s.handleFuseRequest(fuseReq)
	}
}

func (s *server) handleFuseRequest(fuseReq bazilfuse.Request) {
	// Log the request.
	s.logger.Println("Received:", fuseReq)

	// TODO(jacobsa): Support cancellation when interrupted, if we can coax the
	// system into reproducing such requests.
	ctx := context.Background()

	// Attempt to handle it.
	switch typed := fuseReq.(type) {
	case *bazilfuse.InitRequest:
		// Convert the request.
		req := &InitRequest{
			Uid: typed.Header.Uid,
			Gid: typed.Header.Gid,
		}

		// Call the file system.
		_, err := s.fs.Init(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.InitResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.StatfsRequest:
		// Responding to this is required to make mounting work, at least on OS X.
		// We don't currently expose the capability for the file system to
		// intercept this.
		fuseResp := &bazilfuse.StatfsResponse{}
		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.LookupRequest:
		// Convert the request.
		req := &LookUpInodeRequest{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
		}

		// Call the file system.
		resp, err := s.fs.LookUpInode(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.LookupResponse{}
		convertChildInodeEntry(s.clock, &resp.Entry, fuseResp)

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.GetattrRequest:
		// Convert the request.
		req := &GetInodeAttributesRequest{
			Inode: InodeID(typed.Header.Node),
		}

		// Call the file system.
		resp, err := s.fs.GetInodeAttributes(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.GetattrResponse{
			Attr:      convertAttributes(req.Inode, resp.Attributes),
			AttrValid: resp.AttributesExpiration.Sub(s.clock.Now()),
		}

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.MkdirRequest:
		// Convert the request.
		req := &MkDirRequest{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}

		// Call the file system.
		resp, err := s.fs.MkDir(ctx, req)
		if err != nil {
			s.logger.Println("Responding:", err)
			typed.RespondError(err)
			return
		}

		// Convert the response.
		fuseResp := &bazilfuse.MkdirResponse{}
		convertChildInodeEntry(s.clock, &resp.Entry, &fuseResp.LookupResponse)

		s.logger.Println("Responding:", fuseResp)
		typed.Respond(fuseResp)

	case *bazilfuse.OpenRequest:
		// Directory or file?
		if typed.Dir {
			// Convert the request.
			req := &OpenDirRequest{
				Inode: InodeID(typed.Header.Node),
				Flags: typed.Flags,
			}

			// Call the file system.
			resp, err := s.fs.OpenDir(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.OpenResponse{
				Handle: bazilfuse.HandleID(resp.Handle),
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		} else {
			// Convert the request.
			req := &OpenFileRequest{
				Inode: InodeID(typed.Header.Node),
				Flags: typed.Flags,
			}

			// Call the file system.
			resp, err := s.fs.OpenFile(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.OpenResponse{
				Handle: bazilfuse.HandleID(resp.Handle),
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		}

	case *bazilfuse.ReadRequest:
		// Directory or file?
		if typed.Dir {
			// Convert the request.
			req := &ReadDirRequest{
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}

			// Call the file system.
			resp, err := s.fs.ReadDir(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.ReadResponse{
				Data: resp.Data,
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		} else {
			// Convert the request.
			req := &ReadFileRequest{
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}

			// Call the file system.
			resp, err := s.fs.ReadFile(ctx, req)
			if err != nil {
				s.logger.Println("Responding:", err)
				typed.RespondError(err)
				return
			}

			// Convert the response.
			fuseResp := &bazilfuse.ReadResponse{
				Data: resp.Data,
			}

			s.logger.Println("Responding:", fuseResp)
			typed.Respond(fuseResp)
		}

	default:
		s.logger.Println("Unhandled type. Returning ENOSYS.")
		typed.RespondError(ENOSYS)
	}
}

func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
	return bazilfuse.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"regexp"
	"strings"

	"github.com/sirupsen/logrus"
)

// Server represents a simple-upload server.
type Server struct {
	DocumentRoot string
	// MaxUploadSize limits the size of the uploaded content, specified with "byte".
	MaxUploadSize int64
	SecureToken   string
	CorsEnable    bool
}

// NewServer creates a new simple-upload server.
func NewServer(documentRoot string, maxUploadSize int64, token string, corsEnable bool) Server {
	return Server{
		DocumentRoot:  documentRoot,
		MaxUploadSize: maxUploadSize,
		SecureToken:   token,
		CorsEnable:    corsEnable,
	}
}

func (s Server) handleGet(w http.ResponseWriter, r *http.Request) {
	re := regexp.MustCompile(`^/files/([^/]+)$`)
	if !re.MatchString(r.URL.Path) {
		w.WriteHeader(http.StatusNotFound)
		writeError(w, fmt.Errorf("\"%s\" is not found", r.URL.Path))
		return
	}
	if s.CorsEnable {
		w.Header().Set("Access-Control-Allow-Origin", "*")
	}
	http.StripPrefix("/files/", http.FileServer(http.Dir(s.DocumentRoot))).ServeHTTP(w, r)
}

func (s Server) handlePost(w http.ResponseWriter, r *http.Request) {
	srcFile, info, err := r.FormFile("file")
	if err != nil {
		logger.WithError(err).Error("failed to acquire the uploaded content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	defer srcFile.Close()
	logger.Debug(info)
	size, err := getSize(srcFile)
	if err != nil {
		logger.WithError(err).Error("failed to get the size of the uploaded content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	if size > s.MaxUploadSize {
		logger.WithField("size", size).Info("file size exceeded")
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		writeError(w, errors.New("uploaded file size exceeds the limit"))
		return
	}

	body, err := ioutil.ReadAll(srcFile)
	if err != nil {
		logger.WithError(err).Error("failed to read the uploaded content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	filename := info.Filename
	if filename == "" {
		filename = fmt.Sprintf("%x", sha1.Sum(body))
	}

	dstPath := path.Join(s.DocumentRoot, filename)
	dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		logger.WithError(err).WithField("path", dstPath).Error("failed to open the file")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	defer dstFile.Close()
	if written, err := dstFile.Write(body); err != nil {
		logger.WithError(err).WithField("path", dstPath).Error("failed to write the content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	} else if int64(written) != size {
		logger.WithFields(logrus.Fields{
			"size":    size,
			"written": written,
		}).Error("uploaded file size and written size differ")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, fmt.Errorf("the size of uploaded content is %d, but %d bytes written", size, written))
	}
	uploadedURL := strings.TrimPrefix(dstPath, s.DocumentRoot)
	if !strings.HasPrefix(uploadedURL, "/") {
		uploadedURL = "/" + uploadedURL
	}
	uploadedURL = "/files" + uploadedURL
	logger.WithFields(logrus.Fields{
		"path": dstPath,
		"url":  uploadedURL,
		"size": size,
	}).Info("file uploaded by POST")
	if s.CorsEnable {
		w.Header().Set("Access-Control-Allow-Origin", "*")
	}
	w.WriteHeader(http.StatusOK)
	writeSuccess(w, uploadedURL)
}

func (s Server) handlePut(w http.ResponseWriter, r *http.Request) {
	re := regexp.MustCompile(`^/files/([^/]+)$`)
	matches := re.FindStringSubmatch(r.URL.Path)
	if matches == nil {
		logger.WithField("path", r.URL.Path).Info("invalid path")
		w.WriteHeader(http.StatusNotFound)
		writeError(w, fmt.Errorf("\"%s\" is not found", r.URL.Path))
		return
	}
	targetPath := path.Join(s.DocumentRoot, matches[1])

	// We have to create a new temporary file in the same device to avoid "invalid cross-device link" on renaming.
	// Here is the easiest solution: create it in the same directory.
	tempFile, err := ioutil.TempFile(s.DocumentRoot, "upload_")
	if err != nil {
		logger.WithError(err).Error("failed to create a temporary file")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	defer r.Body.Close()
	srcFile, info, err := r.FormFile("file")
	if err != nil {
		logger.WithError(err).WithField("path", targetPath).Error("failed to acquire the uploaded content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	defer srcFile.Close()
	// dump headers for the file
	logger.Debug(info.Header)

	size, err := getSize(srcFile)
	if err != nil {
		logger.WithError(err).WithField("path", targetPath).Error("failed to get the size of the uploaded content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	if size > s.MaxUploadSize {
		logger.WithFields(logrus.Fields{
			"path": targetPath,
			"size": size,
		}).Info("file size exceeded")
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		writeError(w, errors.New("uploaded file size exceeds the limit"))
		return
	}

	n, err := io.Copy(tempFile, srcFile)
	if err != nil {
		logger.WithError(err).WithField("path", tempFile.Name()).Error("failed to write body to the file")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	// excplicitly close file to flush, then rename from temp name to actual name in atomic file
	// operation if on linux or other unix-like OS (windows hosts should look into https://github.com/natefinch/atomic
	// package for atomic file write operations)
	tempFile.Close()
	if err := os.Rename(tempFile.Name(), targetPath); err != nil {
		os.Remove(tempFile.Name())
		logger.WithError(err).WithField("path", targetPath).Error("failed to rename temp file to final filename for upload")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}

	logger.WithFields(logrus.Fields{
		"path": r.URL.Path,
		"size": n,
	}).Info("file uploaded by PUT")
	if s.CorsEnable == true {
		w.Header().Set("Access-Control-Allow-Origin", "*")
	}
	w.WriteHeader(http.StatusOK)
	writeSuccess(w, r.URL.Path)
}

func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// first, try to get the token from the query strings
	token := r.URL.Query().Get("token")
	// if token is not found, check the form parameter.
	if token == "" {
		token = r.Form.Get("token")
	}
	if token != s.SecureToken {
		w.WriteHeader(http.StatusUnauthorized)
		writeError(w, fmt.Errorf("authentication required"))
		return
	}

	switch r.Method {
	case http.MethodGet, http.MethodHead:
		s.handleGet(w, r)
	case http.MethodPost:
		s.handlePost(w, r)
	case http.MethodPut:
		s.handlePut(w, r)
	default:
		w.Header().Add("Allow", "GET,HEAD,POST,PUT")
		w.WriteHeader(http.StatusMethodNotAllowed)
		writeError(w, fmt.Errorf("method \"%s\" is not allowed", r.Method))
	}
}
<commit_msg>:shirt: simplify condition expr<commit_after>package main

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"regexp"
	"strings"

	"github.com/sirupsen/logrus"
)

// Server represents a simple-upload server.
type Server struct {
	DocumentRoot string
	// MaxUploadSize limits the size of the uploaded content, specified with "byte".
	MaxUploadSize int64
	SecureToken   string
	CorsEnable    bool
}

// NewServer creates a new simple-upload server.
func NewServer(documentRoot string, maxUploadSize int64, token string, corsEnable bool) Server {
	return Server{
		DocumentRoot:  documentRoot,
		MaxUploadSize: maxUploadSize,
		SecureToken:   token,
		CorsEnable:    corsEnable,
	}
}

func (s Server) handleGet(w http.ResponseWriter, r *http.Request) {
	re := regexp.MustCompile(`^/files/([^/]+)$`)
	if !re.MatchString(r.URL.Path) {
		w.WriteHeader(http.StatusNotFound)
		writeError(w, fmt.Errorf("\"%s\" is not found", r.URL.Path))
		return
	}
	if s.CorsEnable {
		w.Header().Set("Access-Control-Allow-Origin", "*")
	}
	http.StripPrefix("/files/", http.FileServer(http.Dir(s.DocumentRoot))).ServeHTTP(w, r)
}

func (s Server) handlePost(w http.ResponseWriter, r *http.Request) {
	srcFile, info, err := r.FormFile("file")
	if err != nil {
		logger.WithError(err).Error("failed to acquire the uploaded content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	defer srcFile.Close()
	logger.Debug(info)
	size, err := getSize(srcFile)
	if err != nil {
		logger.WithError(err).Error("failed to get the size of the uploaded content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	if size > s.MaxUploadSize {
		logger.WithField("size", size).Info("file size exceeded")
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		writeError(w, errors.New("uploaded file size exceeds the limit"))
		return
	}

	body, err := ioutil.ReadAll(srcFile)
	if err != nil {
		logger.WithError(err).Error("failed to read the uploaded content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	filename := info.Filename
	if filename == "" {
		filename = fmt.Sprintf("%x", sha1.Sum(body))
	}

	dstPath := path.Join(s.DocumentRoot, filename)
	dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		logger.WithError(err).WithField("path", dstPath).Error("failed to open the file")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	defer dstFile.Close()
	if written, err := dstFile.Write(body); err != nil {
		logger.WithError(err).WithField("path", dstPath).Error("failed to write the content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	} else if int64(written) != size {
		logger.WithFields(logrus.Fields{
			"size":    size,
			"written": written,
		}).Error("uploaded file size and written size differ")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, fmt.Errorf("the size of uploaded content is %d, but %d bytes written", size, written))
	}
	uploadedURL := strings.TrimPrefix(dstPath, s.DocumentRoot)
	if !strings.HasPrefix(uploadedURL, "/") {
		uploadedURL = "/" + uploadedURL
	}
	uploadedURL = "/files" + uploadedURL
	logger.WithFields(logrus.Fields{
		"path": dstPath,
		"url":  uploadedURL,
		"size": size,
	}).Info("file uploaded by POST")
	if s.CorsEnable {
		w.Header().Set("Access-Control-Allow-Origin", "*")
	}
	w.WriteHeader(http.StatusOK)
	writeSuccess(w, uploadedURL)
}

func (s Server) handlePut(w http.ResponseWriter, r *http.Request) {
	re := regexp.MustCompile(`^/files/([^/]+)$`)
	matches := re.FindStringSubmatch(r.URL.Path)
	if matches == nil {
		logger.WithField("path", r.URL.Path).Info("invalid path")
		w.WriteHeader(http.StatusNotFound)
		writeError(w, fmt.Errorf("\"%s\" is not found", r.URL.Path))
		return
	}
	targetPath := path.Join(s.DocumentRoot, matches[1])

	// We have to create a new temporary file in the same device to avoid "invalid cross-device link" on renaming.
	// Here is the easiest solution: create it in the same directory.
	tempFile, err := ioutil.TempFile(s.DocumentRoot, "upload_")
	if err != nil {
		logger.WithError(err).Error("failed to create a temporary file")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	defer r.Body.Close()
	srcFile, info, err := r.FormFile("file")
	if err != nil {
		logger.WithError(err).WithField("path", targetPath).Error("failed to acquire the uploaded content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	defer srcFile.Close()
	// dump headers for the file
	logger.Debug(info.Header)

	size, err := getSize(srcFile)
	if err != nil {
		logger.WithError(err).WithField("path", targetPath).Error("failed to get the size of the uploaded content")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	if size > s.MaxUploadSize {
		logger.WithFields(logrus.Fields{
			"path": targetPath,
			"size": size,
		}).Info("file size exceeded")
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		writeError(w, errors.New("uploaded file size exceeds the limit"))
		return
	}

	n, err := io.Copy(tempFile, srcFile)
	if err != nil {
		logger.WithError(err).WithField("path", tempFile.Name()).Error("failed to write body to the file")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}
	// excplicitly close file to flush, then rename from temp name to actual name in atomic file
	// operation if on linux or other unix-like OS (windows hosts should look into https://github.com/natefinch/atomic
	// package for atomic file write operations)
	tempFile.Close()
	if err := os.Rename(tempFile.Name(), targetPath); err != nil {
		os.Remove(tempFile.Name())
		logger.WithError(err).WithField("path", targetPath).Error("failed to rename temp file to final filename for upload")
		w.WriteHeader(http.StatusInternalServerError)
		writeError(w, err)
		return
	}

	logger.WithFields(logrus.Fields{
		"path": r.URL.Path,
		"size": n,
	}).Info("file uploaded by PUT")
	if s.CorsEnable {
		w.Header().Set("Access-Control-Allow-Origin", "*")
	}
	w.WriteHeader(http.StatusOK)
	writeSuccess(w, r.URL.Path)
}

func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// first, try to get the token from the query strings
	token := r.URL.Query().Get("token")
	// if token is not found, check the form parameter.
	if token == "" {
		token = r.Form.Get("token")
	}
	if token != s.SecureToken {
		w.WriteHeader(http.StatusUnauthorized)
		writeError(w, fmt.Errorf("authentication required"))
		return
	}

	switch r.Method {
	case http.MethodGet, http.MethodHead:
		s.handleGet(w, r)
	case http.MethodPost:
		s.handlePost(w, r)
	case http.MethodPut:
		s.handlePut(w, r)
	default:
		w.Header().Add("Allow", "GET,HEAD,POST,PUT")
		w.WriteHeader(http.StatusMethodNotAllowed)
		writeError(w, fmt.Errorf("method \"%s\" is not allowed", r.Method))
	}
}
<|endoftext|>


<commit_before>package main

import (
    "net/http"
    "log"
    "encoding/json"
    "crypto/md5"
    "time"
    "io"
    "fmt"

    "code.google.com/p/go.net/websocket"
)

type Server struct {
    ID      string
    Config  *Configuration
    Store   *Storage

    timeout time.Duration
}

func createServer(conf *Configuration, store *Storage) *Server{
    hash := md5.New()
    io.WriteString(hash, time.Now().String())
    id := string(hash.Sum(nil))

    timeout := time.Duration(conf.GetInt("connection_timeout"))
    return &Server{id, conf, store, timeout}
}

func (this *Server) initSocketListener() {
    Connect := func(ws *websocket.Conn) {
        defer func() { if DEBUG { log.Println("Socket Closed") } }()

        sock := newSocket(ws, nil, this, "")

        if DEBUG { log.Printf("Socket connected via %s\n", ws.RemoteAddr()) }
        if err := sock.Authenticate(""); err != nil {
            if DEBUG { log.Printf("Error: %s\n", err.Error()) }
            return
        }

        go sock.listenForMessages()
        go sock.listenForWrites()

        if this.timeout <= 0 { // if timeout is 0 then wait forever and return when socket is done.
            <-sock.done
            return
        }

        select {
            case <- time.After(this.timeout * time.Second):
                sock.Close()
                return
            case <- sock.done:
                return
        }
    }

    http.Handle("/socket", websocket.Handler(Connect))
}

func (this *Server) initLongPollListener() {
    LpConnect := func(w http.ResponseWriter, r *http.Request) {
        defer func() { if DEBUG { log.Println("Socket Closed") } }()

        sock := newSocket(nil, w, this, "")
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Content-Type", "application/json")
        w.Header().Set("Cache-Control", "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0")
        w.Header().Set("Connection", "keep-alive")
        //w.Header().Set("Content-Encoding", "gzip")
        w.WriteHeader(200)

        if DEBUG { log.Printf("Long poll connected via \n") }

        if err := sock.Authenticate(r.FormValue("user")); err != nil {
            if DEBUG { log.Printf("Error: %s\n", err.Error()) }
            return
        }

        page := r.FormValue("page")
        if page != "" {
            if sock.Page != "" {
                this.Store.UnsetPage(sock)  //remove old page if it exists
            }

            sock.Page = page
            this.Store.SetPage(sock)
        }

        command := r.FormValue("command")
        if command != "" {
            var cmd CommandMsg
            json.Unmarshal([]byte(command), &cmd)

            go cmd.FromSocket(sock)
        }

        go sock.listenForWrites()

        select{
            case <- time.After(30 * time.Second):
                sock.Close()
                return
            case <- sock.done:
                return
        }
    }

    http.HandleFunc("/lp", LpConnect)
}

func (this *Server) initAppListener() {
    rec := make(chan []string)

    consumer, err := this.Store.redis.Subscribe(rec, this.Config.Get("redis_message_channel"))
    if err != nil {
        log.Fatal("Couldn't subscribe to redis channel")
    }
    defer consumer.Quit()

    if DEBUG { log.Println("LISENING FOR REDIS MESSAGE") }
    var ms []string
    for {
        var cmd CommandMsg
        ms = <- rec
        json.Unmarshal([]byte(ms[2]), &cmd)
        go cmd.FromRedis(this)
    }
}

func (this *Server) initPingListener() {
    pingHandler := func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "OK")
    }

    http.HandleFunc("/ping", pingHandler)
}

func (this *Server) sendHeartbeats() {

    for {
        time.Sleep(20 * time.Second)

        clients := this.Store.Clients()

        for _, user := range clients {
            for _, sock := range user {

                if sock.isWebsocket() {
                    websocket.Message.Send(sock.ws, "")
                }

            }
        }
    }
}

<commit_msg>reconnect the subscriber if redis connection is lost<commit_after>package main

import (
    "net/http"
    "log"
    "encoding/json"
    "crypto/md5"
    "time"
    "io"
    "fmt"

    "code.google.com/p/go.net/websocket"
)

type Server struct {
    ID      string
    Config  *Configuration
    Store   *Storage

    timeout time.Duration
}

func createServer(conf *Configuration, store *Storage) *Server{
    hash := md5.New()
    io.WriteString(hash, time.Now().String())
    id := string(hash.Sum(nil))

    timeout := time.Duration(conf.GetInt("connection_timeout"))
    return &Server{id, conf, store, timeout}
}

func (this *Server) initSocketListener() {
    Connect := func(ws *websocket.Conn) {
        defer func() { if DEBUG { log.Println("Socket Closed") } }()

        sock := newSocket(ws, nil, this, "")

        if DEBUG { log.Printf("Socket connected via %s\n", ws.RemoteAddr()) }
        if err := sock.Authenticate(""); err != nil {
            if DEBUG { log.Printf("Error: %s\n", err.Error()) }
            return
        }

        go sock.listenForMessages()
        go sock.listenForWrites()

        if this.timeout <= 0 { // if timeout is 0 then wait forever and return when socket is done.
            <-sock.done
            return
        }

        select {
            case <- time.After(this.timeout * time.Second):
                sock.Close()
                return
            case <- sock.done:
                return
        }
    }

    http.Handle("/socket", websocket.Handler(Connect))
}

func (this *Server) initLongPollListener() {
    LpConnect := func(w http.ResponseWriter, r *http.Request) {
        defer func() { if DEBUG { log.Println("Socket Closed") } }()

        sock := newSocket(nil, w, this, "")
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Content-Type", "application/json")
        w.Header().Set("Cache-Control", "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0")
        w.Header().Set("Connection", "keep-alive")
        //w.Header().Set("Content-Encoding", "gzip")
        w.WriteHeader(200)

        if DEBUG { log.Printf("Long poll connected via \n") }

        if err := sock.Authenticate(r.FormValue("user")); err != nil {
            if DEBUG { log.Printf("Error: %s\n", err.Error()) }
            return
        }

        page := r.FormValue("page")
        if page != "" {
            if sock.Page != "" {
                this.Store.UnsetPage(sock)  //remove old page if it exists
            }

            sock.Page = page
            this.Store.SetPage(sock)
        }

        command := r.FormValue("command")
        if command != "" {
            var cmd CommandMsg
            json.Unmarshal([]byte(command), &cmd)

            go cmd.FromSocket(sock)
        }

        go sock.listenForWrites()

        select{
            case <- time.After(30 * time.Second):
                sock.Close()
                return
            case <- sock.done:
                return
        }
    }

    http.HandleFunc("/lp", LpConnect)
}

func (this *Server) initAppListener() {
    rec := make(chan []string)

    consumer, err := this.Store.redis.Subscribe(rec, this.Config.Get("redis_message_channel"))
    if err != nil {
        log.Fatal("Couldn't subscribe to redis channel")
    }
    defer consumer.Quit()

    if DEBUG { log.Println("LISENING FOR REDIS MESSAGE") }
    var ms []string
    for {
        select{
            case <- time.After(10 * time.Second):
                if _, err := consumer.Ping(); err != nil {
                    consumer.Quit()
                    rec = make(chan []string)
                    consumer, _ = this.Store.redis.Subscribe(rec, this.Config.Get("redis_message_channel"))
                    continue
                }

            case ms = <- rec:
        }

        var cmd CommandMsg
        json.Unmarshal([]byte(ms[2]), &cmd)
        go cmd.FromRedis(this)
    }
}

func (this *Server) initPingListener() {
    pingHandler := func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "OK")
    }

    http.HandleFunc("/ping", pingHandler)
}

func (this *Server) sendHeartbeats() {

    for {
        time.Sleep(20 * time.Second)

        clients := this.Store.Clients()

        for _, user := range clients {
            for _, sock := range user {

                if sock.isWebsocket() {
                    websocket.Message.Send(sock.ws, "")
                }

            }
        }
    }
}

<|endoftext|>


<commit_before>package distnano

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

// addrs is a string slice that will hold all the addrs of our child nodes.
var addrs []string

func handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")

	log.Println("Handling request for url path:", r.URL.Path)

	// Our actual response to the request.
	var response JSONResponse

	// Mutex to protect concurrent modification of response and WaitGroup to
	// wait for all responses.
	var mtx = &sync.Mutex{}
	var wg = &sync.WaitGroup{}

	// Note what kind of request this is.
	schemaRequest := strings.HasPrefix(r.URL.Path, "/schema")
	wg.Add(len(addrs))
	// Send off the request to each server that we know exists.
	for _, e := range addrs {
		go func(addr string) {
			defer wg.Done()

			url := fmt.Sprintf("%v%v", addr, r.URL.Path)

			rawResponse, err := http.Get(url)
			if err != nil {
				log.Fatal("Cannot continue, error getting ", url)
			}

			// Read the response and unmarshal into a NanocubeResponse object.
			defer rawResponse.Body.Close()
			content, _ := ioutil.ReadAll(rawResponse.Body)

			var partitionResponse JSONResponse
			if schemaRequest {
				partitionResponse = new(SchemaResponse)
			} else {
				partitionResponse = new(NanocubeResponse)
			}

			err = partitionResponse.Unmarshal(content)
			if err != nil {
				log.Fatal(err)
			}

			// Update our global response.
			mtx.Lock()
			defer mtx.Unlock()
			if response == nil {
				// First one to respond, we just set the global response to be
				// our response.
				response = partitionResponse
				return
			}

			// TODO(asubiotto): We can use a more intelligent merging strategy
			// when we have a lot of nodes (in a tree-like fashion).

			// Otherwise we merge with what we have already.
			response.Merge(partitionResponse)
		}(e)
	}

	wg.Wait()
	if response == nil {
		w.Write([]byte("error"))
		return
	}

	b, err := response.Marshal()
	if err != nil {
		log.Fatal(err)
	}

	w.Write(b)
}

func Run(port int, addresses []string) {
	addrs = addresses
	http.HandleFunc("/", handler)
	log.Println("Starting server on port", port)
	go func() {
		<-time.After(2 * time.Second)
		log.Println("Server started")
	}()
	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(port), nil))
}
<commit_msg>Handled favicon error<commit_after>package distnano

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

// addrs is a string slice that will hold all the addrs of our child nodes.
var addrs []string

func handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*")

	log.Println("Handling request for url path:", r.URL.Path)

	// Our actual response to the request.
	var response JSONResponse

	// This global error variable will indicate whether our goroutines
	// encountered any errors.
	var glerr error
	var glerrMtx = &sync.Mutex{}

	// Mutex to protect concurrent modification of response and WaitGroup to
	// wait for all responses.
	var mtx = &sync.Mutex{}
	var wg = &sync.WaitGroup{}

	// Note what kind of request this is.
	schemaRequest := strings.HasPrefix(r.URL.Path, "/schema")
	wg.Add(len(addrs))
	// Send off the request to each server that we know exists.
	for _, e := range addrs {
		go func(addr string) {
			defer wg.Done()

			url := fmt.Sprintf("%v%v", addr, r.URL.Path)

			rawResponse, err := http.Get(url)
			if err != nil {
				glerrMtx.Lock()
				defer glerrMtx.Unlock()
				glerr = err
				return
			}

			// Read the response and unmarshal into a NanocubeResponse object.
			defer rawResponse.Body.Close()
			content, _ := ioutil.ReadAll(rawResponse.Body)

			var partitionResponse JSONResponse
			if schemaRequest {
				partitionResponse = new(SchemaResponse)
			} else {
				partitionResponse = new(NanocubeResponse)
			}

			err = partitionResponse.Unmarshal(content)
			if err != nil {
				glerrMtx.Lock()
				defer glerrMtx.Unlock()
				glerr = err
				return
			}

			// Update our global response.
			mtx.Lock()
			defer mtx.Unlock()
			if response == nil {
				// First one to respond, we just set the global response to be
				// our response.
				response = partitionResponse
				return
			}

			// TODO(asubiotto): We can use a more intelligent merging strategy
			// when we have a lot of nodes (in a tree-like fashion).

			// Otherwise we merge with what we have already.
			response.Merge(partitionResponse)
		}(e)
	}

	wg.Wait()
	if response == nil || glerr != nil {
		w.Write([]byte("error"))
		return
	}

	b, err := response.Marshal()
	if err != nil {
		log.Fatal(err)
	}

	w.Write(b)
}

func Run(port int, addresses []string) {
	addrs = addresses
	http.HandleFunc("/", handler)
	log.Println("Starting server on port", port)
	go func() {
		<-time.After(2 * time.Second)
		log.Println("Server started")
	}()
	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(port), nil))
}
<|endoftext|>