<commit_before>package httpfstream

import (
	"bytes"
	"errors"
	"github.com/garyburd/go-websocket/websocket"
	"io"
	"log"
	"net/http"
	"os"
	pathpkg "path"
	"path/filepath"
	"sync"
	"time"
)

// New returns a new http.Handler for httpfstream.
func New(root string) Handler {
	return Handler{
		Root:      root,
		httpFS:    http.Dir(root),
		writers:   make(map[string]struct{}),
		followers: make(map[string]map[*http.Request]chan []byte),
	}
}

type Handler struct {
	Root string
	Log  *log.Logger

	httpFS http.FileSystem

	writers   map[string]struct{}
	writersMu sync.Mutex

	followers   map[string]map[*http.Request]chan []byte
	followersMu sync.Mutex
}

const xVerb = "X-Verb"

// ServeHTTP implements net/http.Handler.
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	verb := r.Header.Get(xVerb)
	if verb == "" {
		verb = r.URL.Query().Get("verb")
	}

	switch r.Method {
	case "GET":
		switch verb {
		case "APPEND":
			h.Append(w, r)
		default:
			h.Follow(w, r)
		}
	default:
		http.Error(w, "method not supported", http.StatusMethodNotAllowed)
	}
}

func (h Handler) logf(msg string, v ...interface{}) {
	if h.Log != nil {
		h.Log.Printf(msg, v...)
	}
}

const (
	readBufSize  = 10 * 1024 // 10 kb
	writeBufSize = 10 * 1024 // 10 kb

	writeChanSize = 50
)

var (
	followKeepaliveInterval = 3 * time.Second
	readWait                = 5 * time.Second
	writeWait               = 5 * time.Second
)

func (h Handler) resolve(path string) string {
	path = pathpkg.Clean("/" + path)
	return filepath.Join(string(h.Root), path)
}

// ErrWriterConflict indicates that the requested path is currently being
// written by another writer. A path may have at most one active writer.
var ErrWriterConflict = errors.New("path already has an active writer")

func (h Handler) addWriter(path string) error {
	h.writersMu.Lock()
	defer h.writersMu.Unlock()
	if _, present := h.writers[path]; !present {
		h.writers[path] = struct{}{}
		return nil
	}
	return ErrWriterConflict
}

func (h Handler) removeWriter(path string) {
	h.writersMu.Lock()
	defer h.writersMu.Unlock()
	delete(h.writers, path)
}

func (h Handler) addFollower(path string, r *http.Request, c chan []byte) {
	h.followersMu.Lock()
	defer h.followersMu.Unlock()
	if _, present := h.followers[path]; !present {
		h.followers[path] = make(map[*http.Request]chan []byte)
	}
	h.followers[path][r] = c
}

func (h Handler) getFollowers(path string) []chan []byte {
	h.followersMu.Lock()
	defer h.followersMu.Unlock()
	fs := make([]chan []byte, len(h.followers[path]))
	i := 0
	for _, f := range h.followers[path] {
		fs[i] = f
		i++
	}
	return fs
}

func (h Handler) removeFollower(path string, r *http.Request) {
	h.followersMu.Lock()
	defer h.followersMu.Unlock()
	delete(h.followers[path], r)
	if len(h.followers[path]) == 0 {
		delete(h.followers, path)
	}
}

func (h Handler) isWriting(path string) bool {
	h.writersMu.Lock()
	defer h.writersMu.Unlock()
	_, present := h.writers[path]
	return present
}

func (h Handler) serveFile(w http.ResponseWriter, r *http.Request) {
	http.FileServer(h.httpFS).ServeHTTP(w, r)
}

// Follow handles FOLLOW requests to retrieve the contents of a file and a
// real-time stream of data that is appended to the file.
func (h Handler) Follow(w http.ResponseWriter, r *http.Request) {
	path := h.resolve(r.URL.Path)
	h.logf("FOLLOW %s", path)

	// If this file isn't currently being written to, we don't need to update to
	// a WebSocket; we can just return the static file.
	if !h.isWriting(path) {
		h.serveFile(w, r)
		return
	}

	c := make(chan []byte)
	h.addFollower(path, r, c)
	defer h.removeFollower(path, r)

	// TODO(sqs): race conditions galore

	f, err := os.Open(path)
	if err != nil {
		http.Error(w, "failed to open file: "+err.Error(), http.StatusInternalServerError)
		return
	}
	defer f.Close()

	// Open WebSocket.
	ws, err := websocket.Upgrade(w, r.Header, nil, readBufSize, writeBufSize)
	if err != nil {
		if _, ok := err.(websocket.HandshakeError); ok {
			// Serve file via HTTP (not WebSocket).
			h.serveFile(w, r)
			return
		}
		h.logf("failed to upgrade to WebSocket: %s", err)
		return
	}
	defer ws.Close()

	// Send persisted file contents.
	for {
		sw, err := ws.NextWriter(websocket.OpText)
		if err != nil {
			h.logf("NextWriter for file failed: %s", err)
			return
		}

		n, err := io.Copy(sw, f)
		if err != nil {
			h.logf("File write to WebSocket failed: %s", err)
			sw.Close()
			return
		}

		err = sw.Close()
		if err != nil {
			h.logf("Failed to close WebSocket file writer: %s", err)
			return
		}

		// Finished reading file.
		if n == 0 {
			break
		}
	}

	// Follow new writes to file.
	var lastPing time.Time
	for {
		tick := time.NewTicker(50 * time.Millisecond)
		select {
		case <-tick.C:
			if !h.isWriting(path) {
				goto done
			}
			if time.Since(lastPing) > followKeepaliveInterval {
				ws.WriteMessage(websocket.OpPing, []byte{})
				lastPing = time.Now()
			}
		case data := <-c:
			sw, err := ws.NextWriter(websocket.OpText)
			if err != nil {
				h.logf("NextWriter failed: %s", err)
				return
			}

			_, err = sw.Write(data)
			if err != nil {
				h.logf("Write to WebSocket failed: %s", err)
				sw.Close()
				return
			}

			err = sw.Close()
			if err != nil {
				h.logf("Failed to close WebSocket writer: %s", err)
				return
			}
		}
	}

done:
	err = ws.WriteControl(websocket.OpClose, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Time{})
	if err != nil {
		h.logf("Failed to close WebSocket: %s", err)
		return
	}

	err = f.Close()
	if err != nil {
		h.logf("Failed to close destination file: %s", err)
		return
	}
}

// Append handles APPEND requests and appends data to a file.
func (h Handler) Append(w http.ResponseWriter, r *http.Request) {
	path := h.resolve(r.URL.Path)
	h.logf("APPEND %s", path)

	defer r.Body.Close()

	err := h.addWriter(path)
	if err != nil {
		h.logf("addWriter %s: %s", err)
		http.Error(w, "addWriter: "+err.Error(), http.StatusForbidden)
		return
	}
	defer h.removeWriter(path)

	err = os.MkdirAll(filepath.Dir(path), 0755)
	if err != nil {
		http.Error(w, "failed to create dir: "+err.Error(), http.StatusInternalServerError)
		return
	}

	f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
	if err != nil {
		http.Error(w, "failed to open destination file for writing: "+err.Error(), http.StatusInternalServerError)
		return
	}
	defer f.Close()

	ws, err := websocket.Upgrade(w, r.Header, nil, readBufSize, writeBufSize)
	if err != nil {
		if _, ok := err.(websocket.HandshakeError); ok {
			h.logf("not a WebSocket handshake: %s", err)
			return
		}
		h.logf("failed to upgrade to WebSocket: %s", err)
		return
	}
	defer ws.Close()

	ws.SetReadDeadline(time.Now().Add(readWait))
	for {
		op, rd, err := ws.NextReader()
		if err != nil {
			if err != io.ErrUnexpectedEOF {
				h.logf("NextReader failed: %s", err)
			}
			break
		}
		switch op {
		case websocket.OpPong:
			ws.SetReadDeadline(time.Now().Add(readWait))
		case websocket.OpText:
			var buf bytes.Buffer
			mw := io.MultiWriter(f, &buf)

			// Persist to file.
			_, err := io.Copy(mw, rd)
			if err != nil {
				h.logf("Read from WebSocket failed: %s", err)
				return
			}

			// Broadcast to followers.
			followers := h.getFollowers(path)
			for _, fc := range followers {
				fc <- buf.Bytes()
			}
			ws.SetReadDeadline(time.Now().Add(readWait))
		}
	}

	err = r.Body.Close()
	if err != nil {
		h.logf("failed to close upload stream: %s", err)
		return
	}

	err = f.Close()
	if err != nil {
		h.logf("failed to close destination file: %s", err)
		return
	}
}
<commit_msg>Increase readWait as temporary workaround until we properly ping clients<commit_after>package httpfstream

import (
	"bytes"
	"errors"
	"github.com/garyburd/go-websocket/websocket"
	"io"
	"log"
	"net/http"
	"os"
	pathpkg "path"
	"path/filepath"
	"sync"
	"time"
)

// New returns a new http.Handler for httpfstream.
func New(root string) Handler {
	return Handler{
		Root:      root,
		httpFS:    http.Dir(root),
		writers:   make(map[string]struct{}),
		followers: make(map[string]map[*http.Request]chan []byte),
	}
}

type Handler struct {
	Root string
	Log  *log.Logger

	httpFS http.FileSystem

	writers   map[string]struct{}
	writersMu sync.Mutex

	followers   map[string]map[*http.Request]chan []byte
	followersMu sync.Mutex
}

const xVerb = "X-Verb"

// ServeHTTP implements net/http.Handler.
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	verb := r.Header.Get(xVerb)
	if verb == "" {
		verb = r.URL.Query().Get("verb")
	}

	switch r.Method {
	case "GET":
		switch verb {
		case "APPEND":
			h.Append(w, r)
		default:
			h.Follow(w, r)
		}
	default:
		http.Error(w, "method not supported", http.StatusMethodNotAllowed)
	}
}

func (h Handler) logf(msg string, v ...interface{}) {
	if h.Log != nil {
		h.Log.Printf(msg, v...)
	}
}

const (
	readBufSize  = 10 * 1024 // 10 kb
	writeBufSize = 10 * 1024 // 10 kb

	writeChanSize = 50
)

var (
	followKeepaliveInterval = 3 * time.Second
	readWait                = 25 * time.Second
	writeWait               = 5 * time.Second
)

func (h Handler) resolve(path string) string {
	path = pathpkg.Clean("/" + path)
	return filepath.Join(string(h.Root), path)
}

// ErrWriterConflict indicates that the requested path is currently being
// written by another writer. A path may have at most one active writer.
var ErrWriterConflict = errors.New("path already has an active writer")

func (h Handler) addWriter(path string) error {
	h.writersMu.Lock()
	defer h.writersMu.Unlock()
	if _, present := h.writers[path]; !present {
		h.writers[path] = struct{}{}
		return nil
	}
	return ErrWriterConflict
}

func (h Handler) removeWriter(path string) {
	h.writersMu.Lock()
	defer h.writersMu.Unlock()
	delete(h.writers, path)
}

func (h Handler) addFollower(path string, r *http.Request, c chan []byte) {
	h.followersMu.Lock()
	defer h.followersMu.Unlock()
	if _, present := h.followers[path]; !present {
		h.followers[path] = make(map[*http.Request]chan []byte)
	}
	h.followers[path][r] = c
}

func (h Handler) getFollowers(path string) []chan []byte {
	h.followersMu.Lock()
	defer h.followersMu.Unlock()
	fs := make([]chan []byte, len(h.followers[path]))
	i := 0
	for _, f := range h.followers[path] {
		fs[i] = f
		i++
	}
	return fs
}

func (h Handler) removeFollower(path string, r *http.Request) {
	h.followersMu.Lock()
	defer h.followersMu.Unlock()
	delete(h.followers[path], r)
	if len(h.followers[path]) == 0 {
		delete(h.followers, path)
	}
}

func (h Handler) isWriting(path string) bool {
	h.writersMu.Lock()
	defer h.writersMu.Unlock()
	_, present := h.writers[path]
	return present
}

func (h Handler) serveFile(w http.ResponseWriter, r *http.Request) {
	http.FileServer(h.httpFS).ServeHTTP(w, r)
}

// Follow handles FOLLOW requests to retrieve the contents of a file and a
// real-time stream of data that is appended to the file.
func (h Handler) Follow(w http.ResponseWriter, r *http.Request) {
	path := h.resolve(r.URL.Path)
	h.logf("FOLLOW %s", path)

	// If this file isn't currently being written to, we don't need to update to
	// a WebSocket; we can just return the static file.
	if !h.isWriting(path) {
		h.serveFile(w, r)
		return
	}

	c := make(chan []byte)
	h.addFollower(path, r, c)
	defer h.removeFollower(path, r)

	// TODO(sqs): race conditions galore

	f, err := os.Open(path)
	if err != nil {
		http.Error(w, "failed to open file: "+err.Error(), http.StatusInternalServerError)
		return
	}
	defer f.Close()

	// Open WebSocket.
	ws, err := websocket.Upgrade(w, r.Header, nil, readBufSize, writeBufSize)
	if err != nil {
		if _, ok := err.(websocket.HandshakeError); ok {
			// Serve file via HTTP (not WebSocket).
			h.serveFile(w, r)
			return
		}
		h.logf("failed to upgrade to WebSocket: %s", err)
		return
	}
	defer ws.Close()

	// Send persisted file contents.
	for {
		sw, err := ws.NextWriter(websocket.OpText)
		if err != nil {
			h.logf("NextWriter for file failed: %s", err)
			return
		}

		n, err := io.Copy(sw, f)
		if err != nil {
			h.logf("File write to WebSocket failed: %s", err)
			sw.Close()
			return
		}

		err = sw.Close()
		if err != nil {
			h.logf("Failed to close WebSocket file writer: %s", err)
			return
		}

		// Finished reading file.
		if n == 0 {
			break
		}
	}

	// Follow new writes to file.
	var lastPing time.Time
	for {
		tick := time.NewTicker(50 * time.Millisecond)
		select {
		case <-tick.C:
			if !h.isWriting(path) {
				goto done
			}
			if time.Since(lastPing) > followKeepaliveInterval {
				ws.WriteMessage(websocket.OpPing, []byte{})
				lastPing = time.Now()
			}
		case data := <-c:
			sw, err := ws.NextWriter(websocket.OpText)
			if err != nil {
				h.logf("NextWriter failed: %s", err)
				return
			}

			_, err = sw.Write(data)
			if err != nil {
				h.logf("Write to WebSocket failed: %s", err)
				sw.Close()
				return
			}

			err = sw.Close()
			if err != nil {
				h.logf("Failed to close WebSocket writer: %s", err)
				return
			}
		}
	}

done:
	err = ws.WriteControl(websocket.OpClose, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), time.Time{})
	if err != nil {
		h.logf("Failed to close WebSocket: %s", err)
		return
	}

	err = f.Close()
	if err != nil {
		h.logf("Failed to close destination file: %s", err)
		return
	}
}

// Append handles APPEND requests and appends data to a file.
func (h Handler) Append(w http.ResponseWriter, r *http.Request) {
	path := h.resolve(r.URL.Path)
	h.logf("APPEND %s", path)

	defer r.Body.Close()

	err := h.addWriter(path)
	if err != nil {
		h.logf("addWriter %s: %s", err)
		http.Error(w, "addWriter: "+err.Error(), http.StatusForbidden)
		return
	}
	defer h.removeWriter(path)

	err = os.MkdirAll(filepath.Dir(path), 0755)
	if err != nil {
		http.Error(w, "failed to create dir: "+err.Error(), http.StatusInternalServerError)
		return
	}

	f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
	if err != nil {
		http.Error(w, "failed to open destination file for writing: "+err.Error(), http.StatusInternalServerError)
		return
	}
	defer f.Close()

	ws, err := websocket.Upgrade(w, r.Header, nil, readBufSize, writeBufSize)
	if err != nil {
		if _, ok := err.(websocket.HandshakeError); ok {
			h.logf("not a WebSocket handshake: %s", err)
			return
		}
		h.logf("failed to upgrade to WebSocket: %s", err)
		return
	}
	defer ws.Close()

	ws.SetReadDeadline(time.Now().Add(readWait))
	for {
		op, rd, err := ws.NextReader()
		if err != nil {
			if err != io.ErrUnexpectedEOF {
				h.logf("NextReader failed: %s", err)
			}
			break
		}
		switch op {
		case websocket.OpPong:
			ws.SetReadDeadline(time.Now().Add(readWait))
		case websocket.OpText:
			var buf bytes.Buffer
			mw := io.MultiWriter(f, &buf)

			// Persist to file.
			_, err := io.Copy(mw, rd)
			if err != nil {
				h.logf("Read from WebSocket failed: %s", err)
				return
			}

			// Broadcast to followers.
			followers := h.getFollowers(path)
			for _, fc := range followers {
				fc <- buf.Bytes()
			}
			ws.SetReadDeadline(time.Now().Add(readWait))
		}
	}

	err = r.Body.Close()
	if err != nil {
		h.logf("failed to close upload stream: %s", err)
		return
	}

	err = f.Close()
	if err != nil {
		h.logf("failed to close destination file: %s", err)
		return
	}
}
<|endoftext|>


<commit_before>package skeleton

import (
	"github.com/coraldane/logger"
	"github.com/coraldane/skeleton/proto"
	"net"
	"time"
)

func StartServer(addr string, connListener proto.Connection, tcpServer *TcpServer) error {
	tcpAddr, _ := net.ResolveTCPAddr("tcp", addr)
	listener, err := net.ListenTCP("tcp", tcpAddr)
	if nil != err {
		return err
	}
	defer listener.Close()

	logger.Info("listen tcp server at", addr)

	if nil == tcpServer {
		tcpServer = NewTcpServer()
	}

	for {
		conn, err := listener.AcceptTCP()
		if nil != err {
			logger.Error("accept new client error", err)
			continue
		}

		conn.SetKeepAlive(true)
		conn.SetKeepAlivePeriod(time.Second)

		session := proto.NewTcpSession(conn, newServerConnection(connListener, tcpServer))
		go handleConnection(session)
	}
}

func handleConnection(session *proto.TcpSession) {
	defer session.Conn.Close()

	for {
		sbyte, err := session.BR.ReadByte()
		if nil == err {
			if proto.BYTE_PREFIX != sbyte {
				continue
			}
		} else {
			logger.Error("read data from conn error", err)
			session.Close()
			break
		}

		msg := session.Read()
		if nil == msg {
			continue
		}

		session.ConnectionListener.HandleMessage(session, msg)
	}
}

type serverConnection struct {
	connListener proto.Connection
	tcpServer    *TcpServer
}

func newServerConnection(listener proto.Connection, tcpServer *TcpServer) *serverConnection {
	inst := &serverConnection{}
	inst.connListener = listener
	inst.tcpServer = tcpServer
	return inst
}

func (this *serverConnection) OnConnected(session *proto.TcpSession) {
	this.tcpServer.PutSession(session.UniqueKey, session)
	logger.Info("accept conn from ", session.RemoteAddr, ", conn num:", this.tcpServer.SessionCount())

	this.connListener.OnConnected(session)
}

func (this *serverConnection) OnDisconnected(session *proto.TcpSession) {
	this.tcpServer.DeleteSession(session.UniqueKey)
	logger.Info("connection closed ", session.RemoteAddr, session.GetUserId(), ", conn num:", this.tcpServer.SessionCount())

	this.connListener.OnDisconnected(session)
}

func (this *serverConnection) OnAck(msg *proto.Message) {
	if authConnListener, ok := this.connListener.(proto.AuthConnection); ok {
		authConnListener.OnAck(msg)
	}
}

func (this *serverConnection) HandleMessage(session *proto.TcpSession, msg *proto.Message) {
	this.connListener.HandleMessage(session, msg)

	go func() {
		if err := recover(); nil != err {
			logger.Error("handle message error", err)
		}
	}()

	switch msg.Cmd {
	case proto.PING:
		session.Write(msg)
	case proto.ENTER:
		logger.Debug("Enter:", string(msg.Body))
		doCheckAuth(session, msg)
	case proto.ACK:
		this.OnAck(msg)
	case proto.REQUEST:
		tcpRequest := &proto.TcpRequest{}
		tcpRequest.Decode(msg.Body)
		logger.Debug("Request:", msg.MsgId, msg.Flag, tcpRequest.Method, string(tcpRequest.Data))

		if "" == tcpRequest.Method {
			session.WriteError(msg, "method name empty")
			return
		}
		handleRequest(session, msg, tcpRequest)
	default:
	}
}

func (this *serverConnection) DoAuth(session *proto.TcpSession, auth *proto.LoginAuth) *proto.Response {
	resp := proto.NewResponse()
	if authConnListener, ok := this.connListener.(proto.AuthConnection); ok {
		resp = authConnListener.DoAuth(session, auth)
	} else {
		resp.Code = "ok"
	}
	return resp
}
<commit_msg>add request logger information<commit_after>package skeleton

import (
	"github.com/coraldane/logger"
	"github.com/coraldane/skeleton/proto"
	"net"
	"time"
)

func StartServer(addr string, connListener proto.Connection, tcpServer *TcpServer) error {
	tcpAddr, _ := net.ResolveTCPAddr("tcp", addr)
	listener, err := net.ListenTCP("tcp", tcpAddr)
	if nil != err {
		return err
	}
	defer listener.Close()

	logger.Info("listen tcp server at", addr)

	if nil == tcpServer {
		tcpServer = NewTcpServer()
	}

	for {
		conn, err := listener.AcceptTCP()
		if nil != err {
			logger.Error("accept new client error", err)
			continue
		}

		conn.SetKeepAlive(true)
		conn.SetKeepAlivePeriod(time.Second)

		session := proto.NewTcpSession(conn, newServerConnection(connListener, tcpServer))
		go handleConnection(session)
	}
}

func handleConnection(session *proto.TcpSession) {
	defer session.Conn.Close()

	for {
		sbyte, err := session.BR.ReadByte()
		if nil == err {
			if proto.BYTE_PREFIX != sbyte {
				continue
			}
		} else {
			logger.Error("read data from conn error", err)
			session.Close()
			break
		}

		msg := session.Read()
		if nil == msg {
			continue
		}

		session.ConnectionListener.HandleMessage(session, msg)
	}
}

type serverConnection struct {
	connListener proto.Connection
	tcpServer    *TcpServer
}

func newServerConnection(listener proto.Connection, tcpServer *TcpServer) *serverConnection {
	inst := &serverConnection{}
	inst.connListener = listener
	inst.tcpServer = tcpServer
	return inst
}

func (this *serverConnection) OnConnected(session *proto.TcpSession) {
	this.tcpServer.PutSession(session.UniqueKey, session)
	logger.Info("accept conn from ", session.RemoteAddr, ", conn num:", this.tcpServer.SessionCount())

	this.connListener.OnConnected(session)
}

func (this *serverConnection) OnDisconnected(session *proto.TcpSession) {
	this.tcpServer.DeleteSession(session.UniqueKey)
	logger.Info("connection closed ", session.RemoteAddr, session.GetUserId(), ", conn num:", this.tcpServer.SessionCount())

	this.connListener.OnDisconnected(session)
}

func (this *serverConnection) OnAck(msg *proto.Message) {
	if authConnListener, ok := this.connListener.(proto.AuthConnection); ok {
		authConnListener.OnAck(msg)
	}
}

func (this *serverConnection) HandleMessage(session *proto.TcpSession, msg *proto.Message) {
	this.connListener.HandleMessage(session, msg)

	go func() {
		if err := recover(); nil != err {
			logger.Error("handle message error", err)
		}
	}()

	switch msg.Cmd {
	case proto.PING:
		session.Write(msg)
	case proto.ENTER:
		logger.Debug("Enter:", string(msg.Body))
		doCheckAuth(session, msg)
	case proto.ACK:
		this.OnAck(msg)
	case proto.REQUEST:
		tcpRequest := &proto.TcpRequest{}
		tcpRequest.Decode(msg.Body)
		logger.Debug("Request:", session.GetUserId(), msg.MsgId, msg.Flag, tcpRequest.Method, string(tcpRequest.Data))

		if "" == tcpRequest.Method {
			session.WriteError(msg, "method name empty")
			return
		}
		handleRequest(session, msg, tcpRequest)
	default:
	}
}

func (this *serverConnection) DoAuth(session *proto.TcpSession, auth *proto.LoginAuth) *proto.Response {
	resp := proto.NewResponse()
	if authConnListener, ok := this.connListener.(proto.AuthConnection); ok {
		resp = authConnListener.DoAuth(session, auth)
	} else {
		resp.Code = "ok"
	}
	return resp
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0
// Details: https://raw.githubusercontent.com/maniksurtani/quotaservice/master/LICENSE

package quotaservice

import (
	"fmt"
	"net/http"
	"time"

	"github.com/maniksurtani/quotaservice/admin"
	"github.com/maniksurtani/quotaservice/lifecycle"
	"github.com/maniksurtani/quotaservice/logging"
)

// Implements the quotaservice.Server interface
type server struct {
	cfgs            *ServiceConfig
	currentStatus   lifecycle.Status
	stopper         *chan int
	bucketContainer *bucketContainer
	bucketFactory   BucketFactory
	rpcEndpoints    []RpcEndpoint
	listener        Listener
	producer        *EventProducer
}

func (s *server) String() string {
	return fmt.Sprintf("Quota Server running with status %v", s.currentStatus)
}

func (s *server) Start() (bool, error) {
	// Set up listeners
	if s.listener != nil {
		s.producer = registerListener(s.listener)
	}

	// Initialize buckets
	s.bucketFactory.Init(s.cfgs)
	s.bucketContainer = NewBucketContainer(s.cfgs, s.bucketFactory, s)

	// Start the RPC servers
	for _, rpcServer := range s.rpcEndpoints {
		rpcServer.Init(s)
		rpcServer.Start()
	}

	s.currentStatus = lifecycle.Started
	return true, nil
}

func (s *server) Stop() (bool, error) {
	s.currentStatus = lifecycle.Stopped

	// Stop the RPC servers
	for _, rpcServer := range s.rpcEndpoints {
		rpcServer.Stop()
	}

	return true, nil
}

func (s *server) Allow(namespace string, name string, tokensRequested int64, maxWaitMillisOverride int64) (granted int64, waitTime time.Duration, err error) {
	b, e := s.bucketContainer.FindBucket(namespace, name)
	if e != nil {
		// Attempted to create a dynamic bucket and failed.
		err = newError(fmt.Sprintf("Cannot create dynamic bucket %v:%v.", namespace, name),
			ER_TOO_MANY_BUCKETS)
		s.Emit(emitBucketMissed(namespace, name, true))
		return

	}

	if b == nil {
		err = newError(fmt.Sprintf("No such bucket %v:%v.", namespace, name), ER_NO_BUCKET)
		s.Emit(emitBucketMissed(namespace, name, true))
		return
	}

	if b.Config().MaxTokensPerRequest < tokensRequested {
		err = newError(fmt.Sprintf("Too many tokens requested. Bucket %v:%v, tokensRequested=%v, maxTokensPerRequest=%v",
			namespace, name, tokensRequested, b.Config().MaxTokensPerRequest),
			ER_TOO_MANY_TOKENS_REQUESTED)
		s.Emit(emitTooManyTokensRequested(namespace, name, true, tokensRequested))
		return
	}

	// Timeout
	maxWaitTime := time.Millisecond
	if maxWaitMillisOverride > -1 && maxWaitMillisOverride < b.Config().WaitTimeoutMillis {
		maxWaitTime *= time.Duration(maxWaitMillisOverride)
	} else {
		maxWaitTime *= time.Duration(b.Config().WaitTimeoutMillis)
	}

	waitTime = b.Take(tokensRequested, maxWaitTime)

	if waitTime < 0 && maxWaitTime > 0 {
		waitTime = 0
		err = newError(fmt.Sprintf("Timed out waiting on %v:%v", namespace, name), ER_TIMEOUT)
		s.Emit(emitTimedOut(namespace, name, true, tokensRequested))
	} else {
		granted = tokensRequested
		s.Emit(emitTokensServed(namespace, name, true, tokensRequested, waitTime))
	}

	return
}

func (s *server) ServeAdminConsole(mux *http.ServeMux) {
	admin.ServeAdminConsole(s, mux)
}

func (s *server) SetLogger(logger logging.Logger) {
	if s.currentStatus == lifecycle.Started {
		panic("Cannot set logger after server has started!")
	}
	logging.SetLogger(logger)
}

func (s *server) SetListener(listener Listener) {
	if s.currentStatus == lifecycle.Started {
		panic("Cannot add listener after server has started!")
	}
	s.listener = listener
}

// Implements admin.Administrable
func (s *server) Configs() fmt.Stringer {
	return s.cfgs
}

func (s *server) BucketContainer() fmt.Stringer {
	return s.bucketContainer
}

func (s *server) Emit(e Event) {
	if s.producer != nil {
		s.producer.Emit(e)
	}
}
<commit_msg>Only check max tokens if value is set<commit_after>// Licensed under the Apache License, Version 2.0
// Details: https://raw.githubusercontent.com/maniksurtani/quotaservice/master/LICENSE

package quotaservice

import (
	"fmt"
	"net/http"
	"time"

	"github.com/maniksurtani/quotaservice/admin"
	"github.com/maniksurtani/quotaservice/lifecycle"
	"github.com/maniksurtani/quotaservice/logging"
)

// Implements the quotaservice.Server interface
type server struct {
	cfgs            *ServiceConfig
	currentStatus   lifecycle.Status
	stopper         *chan int
	bucketContainer *bucketContainer
	bucketFactory   BucketFactory
	rpcEndpoints    []RpcEndpoint
	listener        Listener
	producer        *EventProducer
}

func (s *server) String() string {
	return fmt.Sprintf("Quota Server running with status %v", s.currentStatus)
}

func (s *server) Start() (bool, error) {
	// Set up listeners
	if s.listener != nil {
		s.producer = registerListener(s.listener)
	}

	// Initialize buckets
	s.bucketFactory.Init(s.cfgs)
	s.bucketContainer = NewBucketContainer(s.cfgs, s.bucketFactory, s)

	// Start the RPC servers
	for _, rpcServer := range s.rpcEndpoints {
		rpcServer.Init(s)
		rpcServer.Start()
	}

	s.currentStatus = lifecycle.Started
	return true, nil
}

func (s *server) Stop() (bool, error) {
	s.currentStatus = lifecycle.Stopped

	// Stop the RPC servers
	for _, rpcServer := range s.rpcEndpoints {
		rpcServer.Stop()
	}

	return true, nil
}

func (s *server) Allow(namespace string, name string, tokensRequested int64, maxWaitMillisOverride int64) (granted int64, waitTime time.Duration, err error) {
	b, e := s.bucketContainer.FindBucket(namespace, name)
	if e != nil {
		// Attempted to create a dynamic bucket and failed.
		err = newError(fmt.Sprintf("Cannot create dynamic bucket %v:%v.", namespace, name),
			ER_TOO_MANY_BUCKETS)
		s.Emit(emitBucketMissed(namespace, name, true))
		return

	}

	if b == nil {
		err = newError(fmt.Sprintf("No such bucket %v:%v.", namespace, name), ER_NO_BUCKET)
		s.Emit(emitBucketMissed(namespace, name, true))
		return
	}

	if b.Config().MaxTokensPerRequest < tokensRequested && b.Config().MaxTokensPerRequest > 0 {
		err = newError(fmt.Sprintf("Too many tokens requested. Bucket %v:%v, tokensRequested=%v, maxTokensPerRequest=%v",
			namespace, name, tokensRequested, b.Config().MaxTokensPerRequest),
			ER_TOO_MANY_TOKENS_REQUESTED)
		s.Emit(emitTooManyTokensRequested(namespace, name, true, tokensRequested))
		return
	}

	// Timeout
	maxWaitTime := time.Millisecond
	if maxWaitMillisOverride > -1 && maxWaitMillisOverride < b.Config().WaitTimeoutMillis {
		maxWaitTime *= time.Duration(maxWaitMillisOverride)
	} else {
		maxWaitTime *= time.Duration(b.Config().WaitTimeoutMillis)
	}

	waitTime = b.Take(tokensRequested, maxWaitTime)

	if waitTime < 0 && maxWaitTime > 0 {
		waitTime = 0
		err = newError(fmt.Sprintf("Timed out waiting on %v:%v", namespace, name), ER_TIMEOUT)
		s.Emit(emitTimedOut(namespace, name, true, tokensRequested))
	} else {
		granted = tokensRequested
		s.Emit(emitTokensServed(namespace, name, true, tokensRequested, waitTime))
	}

	return
}

func (s *server) ServeAdminConsole(mux *http.ServeMux) {
	admin.ServeAdminConsole(s, mux)
}

func (s *server) SetLogger(logger logging.Logger) {
	if s.currentStatus == lifecycle.Started {
		panic("Cannot set logger after server has started!")
	}
	logging.SetLogger(logger)
}

func (s *server) SetListener(listener Listener) {
	if s.currentStatus == lifecycle.Started {
		panic("Cannot add listener after server has started!")
	}
	s.listener = listener
}

// Implements admin.Administrable
func (s *server) Configs() fmt.Stringer {
	return s.cfgs
}

func (s *server) BucketContainer() fmt.Stringer {
	return s.bucketContainer
}

func (s *server) Emit(e Event) {
	if s.producer != nil {
		s.producer.Emit(e)
	}
}
<|endoftext|>


<commit_before>// spdy/server.go

package spdy

import (
	"bytes"
	"encoding/binary"
	"http"
	"io"
	"log"
	"net"
	"os"
	"strconv"
	"time"
)

// ListenAndServe creates a new Server that serves on the given address.  If
// the handler is nil, then http.DefaultServeMux is used.
func ListenAndServe(addr string, handler http.Handler) os.Error {
	srv := &Server{addr, handler}
	return srv.ListenAndServe()
}

// A Server handles incoming SPDY connections with HTTP handlers.
type Server struct {
	Addr    string
	Handler http.Handler
}

func (srv *Server) ListenAndServe() os.Error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	l, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(l)
}

func (srv *Server) Serve(l net.Listener) os.Error {
	defer l.Close()
	handler := srv.Handler
	if handler == nil {
		handler = http.DefaultServeMux
	}
	for {
		c, err := l.Accept()
		if err != nil {
			return err
		}
		s, err := newSession(c, handler)
		if err != nil {
			return err
		}
		go s.serve()
	}
	return nil
}

// A session manages a single TCP connection to a client.
type session struct {
	c       net.Conn
	handler http.Handler
	in, out chan Frame
	streams map[uint32]*serverStream // all access is done synchronously

	headerReader *HeaderReader
	headerWriter *HeaderWriter
}

func newSession(c net.Conn, h http.Handler) (s *session, err os.Error) {
	s = &session{
		c:            c,
		handler:      h,
		headerReader: NewHeaderReader(),
		headerWriter: NewHeaderWriter(-1),
		in:           make(chan Frame),
		out:          make(chan Frame),
		streams:      make(map[uint32]*serverStream),
	}
	return
}

func (sess *session) serve() {
	defer sess.c.Close()
	go sess.sendFrames()
	go sess.receiveFrames()

	for {
		select {
		case f := <-sess.in:
			switch frame := f.(type) {
			case ControlFrame:
				sess.handleControl(frame)
			case DataFrame:
				sess.handleData(frame)
			}
		}
	}
}

func (sess *session) handleControl(frame ControlFrame) {
	log.Printf("CONTROL <-\n")
	log.Printf("  Type:  %v\n", frame.Type)
	log.Printf("  Flags: %#04x\n", frame.Flags)

	switch frame.Type {
	case TypeSynStream:
		if stream, err := newServerStream(sess, frame); err == nil {
			if _, exists := sess.streams[stream.id]; !exists {
				sess.streams[stream.id] = stream
				go func() {
					sess.handler.ServeHTTP(stream, stream.Request())
					stream.finish()
				}()
			}
		} else {
			log.Println("Stream error:", err)
		}
	case TypeRstStream:
		d := bytes.NewBuffer(frame.Data)
		var streamId, statusCode uint32
		readBinary(d, &streamId, &statusCode)
		log.Println("  Reset")
		log.Println("    ID:", streamId)
		log.Println("    Code:", statusCode)
	}
}

func (sess *session) handleData(frame DataFrame) {
	log.Printf("DATA <-\n")
	log.Printf("  Flags: %#04x\n", frame.Flags)

	st, found := sess.streams[frame.StreamID]
	if !found {
		// TODO: Error?
		return
	}
	if st.dataPipe != nil {
		st.dataPipe.write(frame.Data)
		if frame.Flags&FlagFin != 0 {
			st.dataPipe.wclose(nil)
		}
	}
}

func (sess *session) sendFrames() {
	for frame := range sess.out {
		// TODO: Check for errors
		switch f := frame.(type) {
		case DataFrame:
			log.Println("DATA ->")
			log.Printf("  Flags: %#04x", f.Flags)
		case ControlFrame:
			log.Println("CONTROL ->")
			log.Printf("  Type:  %v", f.Type)
			log.Printf("  Flags: %#04x\n", f.Flags)
		}
		_, err := frame.WriteTo(sess.c)
		if err != nil {
			log.Println("Error", err)
		}
	}
}

func (sess *session) receiveFrames() {
	defer close(sess.in)
	for {
		frame, err := ReadFrame(sess.c)
		if err != nil {
			return
		}
		sess.in <- frame
	}
}

// A serverStream is a logical data stream inside a session.  A serverStream
// services a single request.
type serverStream struct {
	id      uint32
	session *session
	closed  bool

	requestHeaders  http.Header
	responseHeaders http.Header
	wroteHeader     bool

	dataPipe *asyncPipe
}

func newServerStream(sess *session, frame ControlFrame) (st *serverStream, err os.Error) {
	if frame.Type != TypeSynStream {
		err = os.NewError("Server stream must be created from a SynStream frame")
		return
	}
	st = &serverStream{
		session:         sess,
		responseHeaders: make(http.Header),
	}
	if frame.Flags&FlagFin == 0 {
		// Request body will follow
		st.dataPipe = apipe()
	}
	// Read frame data
	data := bytes.NewBuffer(frame.Data)
	err = binary.Read(data, binary.BigEndian, &st.id)
	if err != nil {
		return
	}
	_, err = io.ReadFull(data, make([]byte, 6)) // skip associated stream ID and priority
	if err != nil {
		return
	}
	st.requestHeaders, err = sess.headerReader.Decode(data.Bytes())
	if err == nil {
		log.Println("HEADERS")
		for name, values := range st.requestHeaders {
			log.Printf("  %s:\n", name)
			for _, v := range values {
				log.Println("    " + v)
			}
		}
	}
	return
}

// Request returns the request data associated with the serverStream.
func (st *serverStream) Request() (req *http.Request) {
	// TODO
	req = &http.Request{
		Method:     st.requestHeaders.Get("method"),
		RawURL:     st.requestHeaders.Get("url"),
		Proto:      st.requestHeaders.Get("version"),
		Header:     st.requestHeaders,
		Body:       st,
		RemoteAddr: st.session.c.RemoteAddr().String(),
	}
	req.URL, _ = http.ParseRequestURL(req.RawURL)
	return
}

func (st *serverStream) Read(p []byte) (n int, err os.Error) {
	return st.dataPipe.read(p)
}

// Header returns the current response headers.
func (st *serverStream) Header() http.Header { return st.responseHeaders }

func (st *serverStream) Write(p []byte) (n int, err os.Error) {
	if st.closed {
		err = os.NewError("Write on closed serverStream")
		return
	}
	if !st.wroteHeader {
		st.WriteHeader(http.StatusOK)
	}
	for len(p) > 0 {
		frame := DataFrame{
			StreamID: st.id,
		}
		if len(p) < MaxDataLength {
			frame.Data = make([]byte, len(p))
		} else {
			frame.Data = make([]byte, MaxDataLength)
		}
		copy(frame.Data, p)
		p = p[len(frame.Data):]
		st.session.out <- frame
		n += len(frame.Data)
	}
	return
}

type synReplyFrame struct {
	stream *serverStream
	header http.Header
	flags  FrameFlags
}

func (frame synReplyFrame) GetFlags() FrameFlags {
	return frame.flags
}

func (frame synReplyFrame) GetData() []byte {
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.BigEndian, frame.stream.id&0x7fffffff)
	buf.Write([2]byte{}[:])
	frame.stream.session.headerWriter.WriteHeader(buf, frame.stream.responseHeaders)
	return buf.Bytes()
}

func (frame synReplyFrame) WriteTo(w io.Writer) (n int64, err os.Error) {
	cf := ControlFrame{Type: TypeSynReply, Data: frame.GetData()}
	return cf.WriteTo(w)
}

func (st *serverStream) WriteHeader(code int) {
	if st.wroteHeader {
		return
	}
	st.responseHeaders.Set("status", strconv.Itoa(code)+" "+http.StatusText(code))
	st.responseHeaders.Set("version", "HTTP/1.1")
	if st.responseHeaders.Get("Content-Type") == "" {
		st.responseHeaders.Set("Content-Type", "text/html; charset=utf-8")
	}
	if st.responseHeaders.Get("Date") == "" {
		st.responseHeaders.Set("Date", time.UTC().Format(http.TimeFormat))
	}
	// Write the frame
	// TODO: Copy headers
	st.session.out <- synReplyFrame{stream: st, header: st.responseHeaders}
	st.wroteHeader = true
	// Display response headers
	log.Println("RESPONSE HEADERS")
	for name, values := range st.responseHeaders {
		log.Printf("  %s:\n", name)
		for _, v := range values {
			log.Println("    " + v)
		}
	}
}

func (st *serverStream) Close() (err os.Error) {
	if st.closed {
		return
	}
	st.session.out <- DataFrame{
		StreamID: st.id,
		Flags:    FlagFin,
		Data:     []byte{},
	}
	st.closed = true
	return nil
}

func (st *serverStream) finish() (err os.Error) {
	if !st.wroteHeader {
		st.WriteHeader(http.StatusOK)
	}
	return st.Close()
}
<commit_msg>Support ping operation<commit_after>// spdy/server.go

package spdy

import (
	"bytes"
	"encoding/binary"
	"http"
	"io"
	"log"
	"net"
	"os"
	"strconv"
	"time"
)

// ListenAndServe creates a new Server that serves on the given address.  If
// the handler is nil, then http.DefaultServeMux is used.
func ListenAndServe(addr string, handler http.Handler) os.Error {
	srv := &Server{addr, handler}
	return srv.ListenAndServe()
}

// A Server handles incoming SPDY connections with HTTP handlers.
type Server struct {
	Addr    string
	Handler http.Handler
}

func (srv *Server) ListenAndServe() os.Error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	l, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(l)
}

func (srv *Server) Serve(l net.Listener) os.Error {
	defer l.Close()
	handler := srv.Handler
	if handler == nil {
		handler = http.DefaultServeMux
	}
	for {
		c, err := l.Accept()
		if err != nil {
			return err
		}
		s, err := newSession(c, handler)
		if err != nil {
			return err
		}
		go s.serve()
	}
	return nil
}

// A session manages a single TCP connection to a client.
type session struct {
	c       net.Conn
	handler http.Handler
	in, out chan Frame
	streams map[uint32]*serverStream // all access is done synchronously

	headerReader *HeaderReader
	headerWriter *HeaderWriter
}

func newSession(c net.Conn, h http.Handler) (s *session, err os.Error) {
	s = &session{
		c:            c,
		handler:      h,
		headerReader: NewHeaderReader(),
		headerWriter: NewHeaderWriter(-1),
		in:           make(chan Frame),
		out:          make(chan Frame),
		streams:      make(map[uint32]*serverStream),
	}
	return
}

func (sess *session) serve() {
	defer sess.c.Close()
	go sess.sendFrames()
	go sess.receiveFrames()

	for {
		select {
		case f := <-sess.in:
			switch frame := f.(type) {
			case ControlFrame:
				sess.handleControl(frame)
			case DataFrame:
				sess.handleData(frame)
			}
		}
	}
}

func (sess *session) handleControl(frame ControlFrame) {
	log.Printf("CONTROL <-\n")
	log.Printf("  Type:  %v\n", frame.Type)
	log.Printf("  Flags: %#04x\n", frame.Flags)

	switch frame.Type {
	case TypeSynStream:
		if stream, err := newServerStream(sess, frame); err == nil {
			if _, exists := sess.streams[stream.id]; !exists {
				sess.streams[stream.id] = stream
				go func() {
					sess.handler.ServeHTTP(stream, stream.Request())
					stream.finish()
				}()
			}
		} else {
			log.Println("Stream error:", err)
		}
	case TypeRstStream:
		d := bytes.NewBuffer(frame.Data)
		var streamId, statusCode uint32
		readBinary(d, &streamId, &statusCode)
		log.Println("  Reset")
		log.Println("    ID:", streamId)
		log.Println("    Code:", statusCode)
	case TypePing:
		d := bytes.NewBuffer(frame.Data)
		var pingId uint32
		readBinary(d, &pingId)
		log.Println("  Ping")
		sess.out <- ControlFrame{
			Type: TypePing,
			Data: []byte{
				byte(pingId & 0xff000000 >> 24),
				byte(pingId & 0x00ff0000 >> 16),
				byte(pingId & 0x0000ff00 >> 8),
				byte(pingId & 0x000000ff >> 0),
			},
		}
	}
}

func (sess *session) handleData(frame DataFrame) {
	log.Printf("DATA <-\n")
	log.Printf("  Flags: %#04x\n", frame.Flags)

	st, found := sess.streams[frame.StreamID]
	if !found {
		// TODO: Error?
		return
	}
	if st.dataPipe != nil {
		st.dataPipe.write(frame.Data)
		if frame.Flags&FlagFin != 0 {
			st.dataPipe.wclose(nil)
		}
	}
}

func (sess *session) sendFrames() {
	for frame := range sess.out {
		// TODO: Check for errors
		switch f := frame.(type) {
		case DataFrame:
			log.Println("DATA ->")
			log.Printf("  Flags: %#04x", f.Flags)
		case ControlFrame:
			log.Println("CONTROL ->")
			log.Printf("  Type:  %v", f.Type)
			log.Printf("  Flags: %#04x\n", f.Flags)
		}
		_, err := frame.WriteTo(sess.c)
		if err != nil {
			log.Println("Error", err)
		}
	}
}

func (sess *session) receiveFrames() {
	defer close(sess.in)
	for {
		frame, err := ReadFrame(sess.c)
		if err != nil {
			return
		}
		sess.in <- frame
	}
}

// A serverStream is a logical data stream inside a session.  A serverStream
// services a single request.
type serverStream struct {
	id      uint32
	session *session
	closed  bool

	requestHeaders  http.Header
	responseHeaders http.Header
	wroteHeader     bool

	dataPipe *asyncPipe
}

func newServerStream(sess *session, frame ControlFrame) (st *serverStream, err os.Error) {
	if frame.Type != TypeSynStream {
		err = os.NewError("Server stream must be created from a SynStream frame")
		return
	}
	st = &serverStream{
		session:         sess,
		responseHeaders: make(http.Header),
	}
	if frame.Flags&FlagFin == 0 {
		// Request body will follow
		st.dataPipe = apipe()
	}
	// Read frame data
	data := bytes.NewBuffer(frame.Data)
	err = binary.Read(data, binary.BigEndian, &st.id)
	if err != nil {
		return
	}
	_, err = io.ReadFull(data, make([]byte, 6)) // skip associated stream ID and priority
	if err != nil {
		return
	}
	st.requestHeaders, err = sess.headerReader.Decode(data.Bytes())
	if err == nil {
		log.Println("HEADERS")
		for name, values := range st.requestHeaders {
			log.Printf("  %s:\n", name)
			for _, v := range values {
				log.Println("    " + v)
			}
		}
	}
	return
}

// Request returns the request data associated with the serverStream.
func (st *serverStream) Request() (req *http.Request) {
	// TODO
	req = &http.Request{
		Method:     st.requestHeaders.Get("method"),
		RawURL:     st.requestHeaders.Get("url"),
		Proto:      st.requestHeaders.Get("version"),
		Header:     st.requestHeaders,
		Body:       st,
		RemoteAddr: st.session.c.RemoteAddr().String(),
	}
	req.URL, _ = http.ParseRequestURL(req.RawURL)
	return
}

func (st *serverStream) Read(p []byte) (n int, err os.Error) {
	return st.dataPipe.read(p)
}

// Header returns the current response headers.
func (st *serverStream) Header() http.Header { return st.responseHeaders }

func (st *serverStream) Write(p []byte) (n int, err os.Error) {
	if st.closed {
		err = os.NewError("Write on closed serverStream")
		return
	}
	if !st.wroteHeader {
		st.WriteHeader(http.StatusOK)
	}
	for len(p) > 0 {
		frame := DataFrame{
			StreamID: st.id,
		}
		if len(p) < MaxDataLength {
			frame.Data = make([]byte, len(p))
		} else {
			frame.Data = make([]byte, MaxDataLength)
		}
		copy(frame.Data, p)
		p = p[len(frame.Data):]
		st.session.out <- frame
		n += len(frame.Data)
	}
	return
}

type synReplyFrame struct {
	stream *serverStream
	header http.Header
	flags  FrameFlags
}

func (frame synReplyFrame) GetFlags() FrameFlags {
	return frame.flags
}

func (frame synReplyFrame) GetData() []byte {
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.BigEndian, frame.stream.id&0x7fffffff)
	buf.Write([2]byte{}[:])
	frame.stream.session.headerWriter.WriteHeader(buf, frame.stream.responseHeaders)
	return buf.Bytes()
}

func (frame synReplyFrame) WriteTo(w io.Writer) (n int64, err os.Error) {
	cf := ControlFrame{Type: TypeSynReply, Data: frame.GetData()}
	return cf.WriteTo(w)
}

func (st *serverStream) WriteHeader(code int) {
	if st.wroteHeader {
		return
	}
	st.responseHeaders.Set("status", strconv.Itoa(code)+" "+http.StatusText(code))
	st.responseHeaders.Set("version", "HTTP/1.1")
	if st.responseHeaders.Get("Content-Type") == "" {
		st.responseHeaders.Set("Content-Type", "text/html; charset=utf-8")
	}
	if st.responseHeaders.Get("Date") == "" {
		st.responseHeaders.Set("Date", time.UTC().Format(http.TimeFormat))
	}
	// Write the frame
	// TODO: Copy headers
	st.session.out <- synReplyFrame{stream: st, header: st.responseHeaders}
	st.wroteHeader = true
	// Display response headers
	log.Println("RESPONSE HEADERS")
	for name, values := range st.responseHeaders {
		log.Printf("  %s:\n", name)
		for _, v := range values {
			log.Println("    " + v)
		}
	}
}

func (st *serverStream) Close() (err os.Error) {
	if st.closed {
		return
	}
	st.session.out <- DataFrame{
		StreamID: st.id,
		Flags:    FlagFin,
		Data:     []byte{},
	}
	st.closed = true
	return nil
}

func (st *serverStream) finish() (err os.Error) {
	if !st.wroteHeader {
		st.WriteHeader(http.StatusOK)
	}
	return st.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-martini/martini"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	m := martini.Classic()
	m.Get("/", func(res http.ResponseWriter, req *http.Request) string {
		contents, err := ioutil.ReadFile("public/index.html")
		if err != nil {
			log.Fatal(err)
		}
		res.Write(contents)
		return ""
	})
	m.Run()
}
<commit_msg>thats some really bad go<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"github.com/codegangsta/martini-contrib/binding"
	"github.com/go-martini/martini"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
)

type Message struct {
	Text string `json:"text" binding:"required"`
	Nick string `json:"nick" binding:"required"`
}

func escape(str string) string {
	esc := url.QueryEscape(str)
	esc = strings.Replace(esc, "+", " ", -1)
	return esc
}

func bytesToString(input []byte) string {
	buf := new(bytes.Buffer)
	buf.ReadFrom(bytes.NewReader(input))
	return buf.String()
}

func publish(message []byte) {
	str := bytesToString(message)
	rd := strings.NewReader(escape(str))
	// seems like this would block? goroutine?
	// TODO un-hardcode channel
	url := "http://localhost:9080/pub?id=example"
	_, err := http.Post(url, "application/json", rd)
	if err != nil {
		log.Fatal(err)
	}
	// do we want to return something here?
}

func main() {
	m := martini.Classic()
	m.Get("/", func(req *http.Request, res http.ResponseWriter) string {
		contents, err := ioutil.ReadFile("public/index.html")
		if err != nil {
			log.Fatal(err)
		}
		res.Write(contents)
		return ""
	})
	m.Post("/pub", binding.Bind(Message{}), func(message Message) string {
		m, err := json.Marshal(message)
		if err != nil {
			log.Fatal(err)
		}
		publish(m)
		return "" //actually return something
	})
	m.Run()
}
<|endoftext|>


<commit_before>package kuja

import (
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"reflect"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/golang/snappy"
	"github.com/kr/pretty"
	"github.com/plimble/errors"
	"github.com/plimble/kuja/broker"
	"github.com/plimble/kuja/encoder"
	"github.com/plimble/kuja/encoder/json"
	"github.com/plimble/kuja/registry"
	"github.com/satori/go.uuid"
	"golang.org/x/net/netutil"
	"gopkg.in/tylerb/graceful.v1"
)

const (
	DEVELOPMENT = "development"
	STAGING     = "staging"
	PRODUCTION  = "production"
)

type Handler func(ctx *Context, w http.ResponseWriter, r *http.Request) error

type Server struct {
	stage           string
	id              string
	pool            sync.Pool
	middleware      []Handler
	mu              sync.Mutex // protects the serviceMap
	serviceMap      map[string]*service
	subscriberMap   map[string]*subscriber
	broker          broker.Broker
	encoder         encoder.Encoder
	snappy          bool
	serviceError    ServiceErrorFunc
	subscriberError SubscriberErrorFunc
	registry        registry.Registry
	srv             *graceful.Server
}

func NewServer() *Server {
	server := &Server{
		stage:           DEVELOPMENT,
		id:              uuid.NewV1().String(),
		serviceMap:      make(map[string]*service),
		encoder:         json.NewEncoder(),
		serviceError:    defaulServiceErr,
		subscriberError: defaulSubscriberErr,
		subscriberMap:   make(map[string]*subscriber),
	}

	server.pool.New = func() interface{} {
		return &Context{
			ReqMetadata:  make(Metadata),
			RespMetadata: make(Metadata),
			returnValues: make([]reflect.Value, 1),
			serviceError: server.serviceError,
			isResp:       false,
		}
	}

	return server
}

func (server *Server) Id() string {
	return server.id
}

func (server *Server) Use(h ...Handler) {
	server.middleware = append(server.middleware, h...)
}

func (server *Server) Stage(stage string) {
	server.stage = stage
}

func (server *Server) Snappy(enable bool) {
	server.snappy = enable
}

func (server *Server) Registry(r registry.Registry) {
	server.registry = r
}

func (server *Server) Encoder(enc encoder.Encoder) {
	server.encoder = enc
}

func (server *Server) Broker(b broker.Broker) {
	server.broker = b
}

func (server *Server) Run(addr string, timeout time.Duration) {
	server.srv = &graceful.Server{
		Timeout: timeout,
		Server: &http.Server{
			Addr:    addr,
			Handler: server,
		},
	}

	l, err := net.Listen("tcp", addr)
	if err != nil {
		log.Error(err)
		return
	}

	if server.srv.ListenLimit != 0 {
		l = netutil.LimitListener(l, server.srv.ListenLimit)
	}

	if err := server.startRegistry("http://", addr); err != nil {
		log.Error(err)
		return
	}

	if err := server.startBroker(); err != nil {
		log.Error(err)
		return
	}

	if err := server.startSubscribe(); err != nil {
		log.Error(err)
		return
	}

	log.Infof("Start server id %s on %s stage %s", server.id, addr, server.stage)
	server.srv.Serve(l)
	log.Info("Stop server")
	server.stopRegistry()
	server.stopBroker()
}

func (server *Server) Close() {
	server.srv.Stop(0)
}

func (server *Server) RunTLS(addr string, timeout time.Duration, certFile, keyFile string) {
	server.srv = &graceful.Server{
		Timeout: timeout,
		Server: &http.Server{
			Addr:    addr,
			Handler: server,
		},
	}

	config := &tls.Config{}
	if server.srv.TLSConfig != nil {
		*config = *server.srv.TLSConfig
	}

	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		log.Error(err)
		return
	}

	conn, err := net.Listen("tcp", addr)
	if err != nil {
		log.Error(err)
		return
	}

	tlsListener := tls.NewListener(conn, config)

	if err := server.startRegistry("https://", addr); err != nil {
		log.Error(err)
		return
	}

	if err := server.startBroker(); err != nil {
		log.Error(err)
		return
	}

	if err := server.startSubscribe(); err != nil {
		log.Error(err)
		return
	}

	log.Infof("Start server id %s on %s stage %s", server.id, addr, server.stage)
	server.srv.Serve(tlsListener)
	log.Info("Stop server")
	server.stopRegistry()
	server.stopBroker()
}

func (server *Server) startRegistry(scheme, addr string) error {
	if server.registry == nil {
		return nil
	}

	var host string
	var port string
	parts := strings.Split(addr, ":")
	if len(parts) > 1 {
		host = strings.Join(parts[:len(parts)-1], ":")
		port = parts[len(parts)-1]
	} else {
		host = parts[0]
	}

	for _, service := range server.serviceMap {
		service.node = &registry.Node{
			Id:      service.id,
			Name:    service.name,
			Host:    host,
			Port:    port,
			Address: addr,
			URL:     scheme + addr,
		}
		err := server.registry.Register(service.node)
		log.Infof("Registerd %s %s %s", service.name, service.id, addr)
		if err != nil {
			return err
		}
	}

	return nil
}

func (server *Server) stopRegistry() {
	if server.registry != nil {
		for _, service := range server.serviceMap {
			if service.node == nil {
				continue
			}
			err := server.registry.Deregister(service.node)
			if err != nil {
				log.WithFields(log.Fields{
					"error":      err,
					"service":    service.name,
					"service_id": service.id,
				}).Error("Unable to deregister")
				continue
			}
			log.Infof("Deregisterd %s %s", service.name, service.id)
		}
		server.registry.Close()
	}
}

func (server *Server) startBroker() error {
	if server.broker == nil {
		return nil
	}

	if err := server.broker.Connect(); err != nil {
		return err
	}

	log.Infof("Connected to broker")

	return nil
}

func (server *Server) stopBroker() {
	if server.broker == nil {
		return
	}

	server.broker.Close()
	log.Infof("Close broker connection")
}

func (server *Server) startSubscribe() error {
	if server.broker == nil {
		return nil
	}

	for _, s := range server.subscriberMap {
		server.broker.Subscribe(s.topic, s.queue, server.id, s.size, s.handler)
		log.Infof("Subscribe topic: %s queue: %s size: %d", s.topic, s.queue, s.size)
	}

	return nil
}

func getServiceMethod(s string) (string, string) {
	if strings.HasPrefix(s, "/") {
		s = s[1:]
	}

	if strings.HasSuffix(s, "/") {
		s = s[:len(s)-1]
	}

	for i := 0; i < len(s); i++ {
		if s[i] == '/' {
			return s[:i], s[i+1:]
		}
	}

	return "", ""
}

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method == "GET" && req.URL.Path == "/health" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusOK)
		io.WriteString(w, "ok\n")
		return
	}

	if req.Method != "POST" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusMethodNotAllowed)
		io.WriteString(w, "405 must POST\n")
		return
	}

	serviceName, methodName := getServiceMethod(req.URL.Path)

	if serviceName == "" || methodName == "" {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find service or method"))
		return
	}

	ctx := server.pool.Get().(*Context)

	for name, vals := range req.Header {
		ctx.ReqMetadata[name] = vals[0]
	}

	// server.mu.Lock()
	s := server.serviceMap[serviceName]
	// server.mu.Unlock()
	if s == nil {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find service " + serviceName))
		return
	}
	mt := s.method[methodName]
	if mt == nil {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find method " + methodName))
		return
	}

	ctx.handlers = s.handlers
	ctx.mt = mt
	ctx.req = req
	ctx.w = w
	ctx.rcvr = s.rcvr
	ctx.encoder = server.encoder
	ctx.snappy = server.snappy
	ctx.ServiceID = s.id
	ctx.ServiceName = serviceName
	ctx.MethodName = methodName

	if len(s.handlers) > 0 {
		if err := s.handlers[0](ctx, w, req); err != nil && !ctx.isResp {
			respError(err, ctx)
		}
	} else {
		if err := serve(ctx); err != nil && !ctx.isResp {
			respError(err, ctx)
		}
	}
	server.pool.Put(ctx)
}

func respError(err error, ctx *Context) {
	if errs, ok := err.(errors.Error); ok {
		ctx.isResp = true
		go ctx.serviceError(ctx.ServiceID, ctx.ServiceName, ctx.MethodName, errs.Code(), err)
		ctx.w.WriteHeader(errs.Code())
		ctx.w.Write([]byte(errs.Error()))
	} else {
		ctx.isResp = true
		go ctx.serviceError(ctx.ServiceID, ctx.ServiceName, ctx.MethodName, 500, err)
		ctx.w.WriteHeader(500)
		ctx.w.Write([]byte(err.Error()))
	}
}

func serve(ctx *Context) error {
	argv := reflect.New(ctx.mt.ArgType.Elem())
	var replyv interface{}

	argvInter := argv.Interface()
	err := ctx.encoder.Decode(ctx.req.Body, argvInter)
	ctx.req.Body.Close()
	if err != nil {
		return errors.InternalError("unable to encode response")
	}

	function := ctx.mt.method.Func
	if ctx.mt.ContextType == nil {
		ctx.returnValues = function.Call([]reflect.Value{ctx.rcvr, argv})
	} else {
		ctx.returnValues = function.Call([]reflect.Value{ctx.rcvr, ctx.mt.prepareContext(ctx), argv})
	}

	if len(ctx.returnValues) == 1 {
		if ctx.returnValues[0].Interface() != nil {
			return ctx.returnValues[0].Interface().(error)
		}
	} else if len(ctx.returnValues) == 2 {
		if ctx.returnValues[1].Interface() != nil {
			return ctx.returnValues[1].Interface().(error)
		}
		replyv = ctx.returnValues[0].Interface()
	}

	pretty.Println(replyv)

	for name, val := range ctx.RespMetadata {
		ctx.w.Header().Set(name, val)
	}

	if replyv != nil {
		if ctx.snappy {
			data, err := ctx.encoder.Marshal(replyv)
			if err != nil {
				return err
			}
			data, err = snappy.Encode(nil, data)
			if err != nil {
				return err
			}
			ctx.isResp = true
			ctx.w.Header().Set("Snappy", "true")
			ctx.w.WriteHeader(200)
			ctx.w.Write(data)
		} else {
			ctx.isResp = true
			ctx.w.WriteHeader(200)
			ctx.encoder.Encode(ctx.w, replyv)
		}
	} else {
		ctx.isResp = true
		ctx.w.WriteHeader(200)
		ctx.w.Write(nil)
	}

	return nil
}
<commit_msg>fix test<commit_after>package kuja

import (
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"reflect"
	"strings"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/golang/snappy"
	"github.com/kr/pretty"
	"github.com/plimble/errors"
	"github.com/plimble/kuja/broker"
	"github.com/plimble/kuja/encoder"
	"github.com/plimble/kuja/encoder/json"
	"github.com/plimble/kuja/registry"
	"github.com/satori/go.uuid"
	"golang.org/x/net/netutil"
	"gopkg.in/tylerb/graceful.v1"
)

const (
	DEVELOPMENT = "development"
	STAGING     = "staging"
	PRODUCTION  = "production"
)

type Handler func(ctx *Context, w http.ResponseWriter, r *http.Request) error

type Server struct {
	stage           string
	id              string
	pool            sync.Pool
	middleware      []Handler
	mu              sync.Mutex // protects the serviceMap
	serviceMap      map[string]*service
	subscriberMap   map[string]*subscriber
	broker          broker.Broker
	encoder         encoder.Encoder
	snappy          bool
	serviceError    ServiceErrorFunc
	subscriberError SubscriberErrorFunc
	registry        registry.Registry
	srv             *graceful.Server
}

func NewServer() *Server {
	server := &Server{
		stage:           DEVELOPMENT,
		id:              uuid.NewV1().String(),
		serviceMap:      make(map[string]*service),
		encoder:         json.NewEncoder(),
		serviceError:    defaulServiceErr,
		subscriberError: defaulSubscriberErr,
		subscriberMap:   make(map[string]*subscriber),
	}

	server.pool.New = func() interface{} {
		return &Context{
			ReqMetadata:  make(Metadata),
			RespMetadata: make(Metadata),
			returnValues: make([]reflect.Value, 1),
			serviceError: server.serviceError,
			isResp:       false,
		}
	}

	return server
}

func (server *Server) Id() string {
	return server.id
}

func (server *Server) Use(h ...Handler) {
	server.middleware = append(server.middleware, h...)
}

func (server *Server) Stage(stage string) {
	server.stage = stage
}

func (server *Server) Snappy(enable bool) {
	server.snappy = enable
}

func (server *Server) Registry(r registry.Registry) {
	server.registry = r
}

func (server *Server) Encoder(enc encoder.Encoder) {
	server.encoder = enc
}

func (server *Server) Broker(b broker.Broker) {
	server.broker = b
}

func (server *Server) Run(addr string, timeout time.Duration) {
	server.srv = &graceful.Server{
		Timeout: timeout,
		Server: &http.Server{
			Addr:    addr,
			Handler: server,
		},
	}

	l, err := net.Listen("tcp", addr)
	if err != nil {
		log.Error(err)
		return
	}

	if server.srv.ListenLimit != 0 {
		l = netutil.LimitListener(l, server.srv.ListenLimit)
	}

	if err := server.startRegistry("http://", addr); err != nil {
		log.Error(err)
		return
	}

	if err := server.startBroker(); err != nil {
		log.Error(err)
		return
	}

	if err := server.startSubscribe(); err != nil {
		log.Error(err)
		return
	}

	log.Infof("Start server id %s on %s stage %s", server.id, addr, server.stage)
	server.srv.Serve(l)
	log.Info("Stop server")
	server.stopRegistry()
	server.stopBroker()
}

func (server *Server) Close() {
	server.srv.Stop(0)
}

func (server *Server) RunTLS(addr string, timeout time.Duration, certFile, keyFile string) {
	server.srv = &graceful.Server{
		Timeout: timeout,
		Server: &http.Server{
			Addr:    addr,
			Handler: server,
		},
	}

	config := &tls.Config{}
	if server.srv.TLSConfig != nil {
		*config = *server.srv.TLSConfig
	}

	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		log.Error(err)
		return
	}

	conn, err := net.Listen("tcp", addr)
	if err != nil {
		log.Error(err)
		return
	}

	tlsListener := tls.NewListener(conn, config)

	if err := server.startRegistry("https://", addr); err != nil {
		log.Error(err)
		return
	}

	if err := server.startBroker(); err != nil {
		log.Error(err)
		return
	}

	if err := server.startSubscribe(); err != nil {
		log.Error(err)
		return
	}

	log.Infof("Start server id %s on %s stage %s", server.id, addr, server.stage)
	server.srv.Serve(tlsListener)
	log.Info("Stop server")
	server.stopRegistry()
	server.stopBroker()
}

func (server *Server) startRegistry(scheme, addr string) error {
	if server.registry == nil {
		return nil
	}

	var host string
	var port string
	parts := strings.Split(addr, ":")
	if len(parts) > 1 {
		host = strings.Join(parts[:len(parts)-1], ":")
		port = parts[len(parts)-1]
	} else {
		host = parts[0]
	}

	for _, service := range server.serviceMap {
		service.node = &registry.Node{
			Id:      service.id,
			Name:    service.name,
			Host:    host,
			Port:    port,
			Address: addr,
			URL:     scheme + addr,
		}
		err := server.registry.Register(service.node)
		log.Infof("Registerd %s %s %s", service.name, service.id, addr)
		if err != nil {
			return err
		}
	}

	return nil
}

func (server *Server) stopRegistry() {
	if server.registry != nil {
		for _, service := range server.serviceMap {
			if service.node == nil {
				continue
			}
			err := server.registry.Deregister(service.node)
			if err != nil {
				log.WithFields(log.Fields{
					"error":      err,
					"service":    service.name,
					"service_id": service.id,
				}).Error("Unable to deregister")
				continue
			}
			log.Infof("Deregisterd %s %s", service.name, service.id)
		}
		server.registry.Close()
	}
}

func (server *Server) startBroker() error {
	if server.broker == nil {
		return nil
	}

	if err := server.broker.Connect(); err != nil {
		return err
	}

	log.Infof("Connected to broker")

	return nil
}

func (server *Server) stopBroker() {
	if server.broker == nil {
		return
	}

	server.broker.Close()
	log.Infof("Close broker connection")
}

func (server *Server) startSubscribe() error {
	if server.broker == nil {
		return nil
	}

	for _, s := range server.subscriberMap {
		server.broker.Subscribe(s.topic, s.queue, server.id, s.size, s.handler)
		log.Infof("Subscribe topic: %s queue: %s size: %d", s.topic, s.queue, s.size)
	}

	return nil
}

func getServiceMethod(s string) (string, string) {
	if strings.HasPrefix(s, "/") {
		s = s[1:]
	}

	if strings.HasSuffix(s, "/") {
		s = s[:len(s)-1]
	}

	for i := 0; i < len(s); i++ {
		if s[i] == '/' {
			return s[:i], s[i+1:]
		}
	}

	return "", ""
}

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method == "GET" && req.URL.Path == "/health" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusOK)
		io.WriteString(w, "ok\n")
		return
	}

	if req.Method != "POST" {
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(http.StatusMethodNotAllowed)
		io.WriteString(w, "405 must POST\n")
		return
	}

	serviceName, methodName := getServiceMethod(req.URL.Path)

	if serviceName == "" || methodName == "" {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find service or method"))
		return
	}

	ctx := server.pool.Get().(*Context)

	for name, vals := range req.Header {
		ctx.ReqMetadata[name] = vals[0]
	}

	// server.mu.Lock()
	s := server.serviceMap[serviceName]
	// server.mu.Unlock()
	if s == nil {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find service " + serviceName))
		return
	}
	mt := s.method[methodName]
	if mt == nil {
		w.WriteHeader(404)
		w.Write([]byte("rpc: can't find method " + methodName))
		return
	}

	ctx.handlers = s.handlers
	ctx.mt = mt
	ctx.req = req
	ctx.w = w
	ctx.rcvr = s.rcvr
	ctx.encoder = server.encoder
	ctx.snappy = server.snappy
	ctx.ServiceID = s.id
	ctx.ServiceName = serviceName
	ctx.MethodName = methodName

	if len(s.handlers) > 0 {
		if err := s.handlers[0](ctx, w, req); err != nil && !ctx.isResp {
			respError(err, ctx)
		}
	} else {
		if err := serve(ctx); err != nil && !ctx.isResp {
			respError(err, ctx)
		}
	}
	server.pool.Put(ctx)
}

func respError(err error, ctx *Context) {
	if errs, ok := err.(errors.Error); ok {
		ctx.isResp = true
		go ctx.serviceError(ctx.ServiceID, ctx.ServiceName, ctx.MethodName, errs.Code(), err)
		ctx.w.WriteHeader(errs.Code())
		ctx.w.Write([]byte(errs.Error()))
	} else {
		ctx.isResp = true
		go ctx.serviceError(ctx.ServiceID, ctx.ServiceName, ctx.MethodName, 500, err)
		ctx.w.WriteHeader(500)
		ctx.w.Write([]byte(err.Error()))
	}
}

func serve(ctx *Context) error {
	argv := reflect.New(ctx.mt.ArgType.Elem())
	var replyv interface{}

	argvInter := argv.Interface()
	err := ctx.encoder.Decode(ctx.req.Body, argvInter)
	ctx.req.Body.Close()
	if err != nil {
		return errors.InternalError("unable to encode response")
	}

	function := ctx.mt.method.Func
	if ctx.mt.ContextType == nil {
		ctx.returnValues = function.Call([]reflect.Value{ctx.rcvr, argv})
	} else {
		ctx.returnValues = function.Call([]reflect.Value{ctx.rcvr, ctx.mt.prepareContext(ctx), argv})
	}

	if len(ctx.returnValues) == 1 {
		if ctx.returnValues[0].Interface() != nil {
			return ctx.returnValues[0].Interface().(error)
		}
	} else if len(ctx.returnValues) == 2 {
		if ctx.returnValues[1].Interface() != nil {
			return ctx.returnValues[1].Interface().(error)
		}
		replyv = ctx.returnValues[0].Interface()
	}

	pretty.Println(replyv)

	for name, val := range ctx.RespMetadata {
		ctx.w.Header().Set(name, val)
	}

	if replyv != nil {
		if ctx.snappy {
			data, err := ctx.encoder.Marshal(replyv)
			if err != nil {
				return err
			}
			data = snappy.Encode(nil, data)
			ctx.isResp = true
			ctx.w.Header().Set("Snappy", "true")
			ctx.w.WriteHeader(200)
			ctx.w.Write(data)
		} else {
			ctx.isResp = true
			ctx.w.WriteHeader(200)
			ctx.encoder.Encode(ctx.w, replyv)
		}
	} else {
		ctx.isResp = true
		ctx.w.WriteHeader(200)
		ctx.w.Write(nil)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"path/filepath"
	"strings"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/miekg/dns"
	"github.com/spf13/cobra"
)

type (
	server struct {
		etcd   *etcd.Client
		domain string
		prefix string
		ttl    uint32
	}
)

func nameError(w dns.ResponseWriter, req *dns.Msg) {
	m := &dns.Msg{}
	m.SetReply(req)
	m.SetRcode(req, dns.RcodeNameError)
	_ = w.WriteMsg(m)
}

func (s *server) GetNode(name string) (*node, error) {
	name = strings.ToLower(name)
	name = strings.TrimSuffix(name, ".nodes.")
	key := filepath.Join("/", s.prefix, "nodes", name)

	resp, err := s.etcd.Get(key, false, false)

	if err != nil {
		return nil, err
	}

	var n node
	err = json.Unmarshal([]byte(resp.Node.Value), &n)
	if err != nil {
		return nil, err
	}

	if n.IP == nil {
		log.Printf("node %s has no address", name)
		return nil, nil
	}
	return &n, nil
}

func (s *server) GetService(name string) ([]*record, error) {
	name = strings.TrimSuffix(name, ".services.")

	key := filepath.Join("/", s.prefix, "services", name)

	resp, err := s.etcd.Get(key, false, true)

	if err != nil {
		return nil, err
	}

	records := make([]*record, 0, len(resp.Node.Nodes))

	for _, n := range resp.Node.Nodes {
		var rec record
		err := json.Unmarshal([]byte(n.Value), &rec)
		if err != nil {
			log.Printf("json.Unmarshal failed for %s: %s", n.Key, err)
			continue
		}

		// should match against a regex?
		if rec.Target == "" {
			continue
		}

		records = append(records, &rec)
	}

	return records, nil
}

func (s *server) ServicesA(w dns.ResponseWriter, r *dns.Msg, name string) (*dns.Msg, error) {
	records, err := s.GetService(name)

	if err != nil {
		return nil, err
	}

	if len(records) == 0 {
		return nil, nil
	}
	m := &dns.Msg{}
	m.SetReply(r)

	header := dns.RR_Header{
		Name:   r.Question[0].Name,
		Rrtype: r.Question[0].Qtype,
		Class:  r.Question[0].Qclass,
		Ttl:    s.ttl,
	}

	m.Answer = make([]dns.RR, 0, len(records))
	for _, record := range records {
		// any target that contains a "." is assumed to not be a node
		if strings.Contains(record.Target, ".") {
			continue
		}

		node, err := s.GetNode(record.Target)
		if err != nil {
			continue
		}

		answer := &dns.A{
			Hdr: header,
			A:   node.IP,
		}

		m.Answer = append(m.Answer, answer)
	}

	return m, nil

}

func (s *server) ServicesSRV(w dns.ResponseWriter, r *dns.Msg, name string) (*dns.Msg, error) {
	records, err := s.GetService(name)
	if err != nil {
		return nil, err
	}

	if len(records) == 0 {
		return nil, nil
	}

	m := &dns.Msg{}
	m.SetReply(r)

	header := dns.RR_Header{
		Name:   r.Question[0].Name,
		Rrtype: r.Question[0].Qtype,
		Class:  r.Question[0].Qclass,
		Ttl:    s.ttl,
	}

	m.Answer = make([]dns.RR, 0, len(records))
	m.Extra = make([]dns.RR, 0, len(records))
	for _, record := range records {

		isNode := false

		target := strings.ToLower(record.Target)
		if !strings.Contains(target, ".") {
			target = strings.Join([]string{target, s.domain}, ".")
			isNode = true
			// should we make sure it exists before we add it?
		}

		answer := &dns.SRV{
			Hdr:      header,
			Priority: record.Priority,
			Weight:   record.Weight,
			Port:     record.Port,
			Target:   target,
		}

		m.Answer = append(m.Answer, answer)

		if !isNode {
			continue
		}

		node, err := s.GetNode(record.Target)
		if err != nil {
			// just skip it
			continue
		}

		extra := &dns.A{
			Hdr: dns.RR_Header{
				Name:   target,
				Rrtype: dns.TypeA,
				Class:  r.Question[0].Qclass,
				Ttl:    s.ttl,
			},
			A: node.IP,
		}

		m.Extra = append(m.Extra, extra)
	}

	return m, nil

}

func (s *server) NodesA(w dns.ResponseWriter, r *dns.Msg, name string) (*dns.Msg, error) {
	node, err := s.GetNode(name)

	if err != nil {
		return nil, err
	}

	m := &dns.Msg{}
	m.SetReply(r)

	header := dns.RR_Header{
		Name:   r.Question[0].Name,
		Rrtype: r.Question[0].Qtype,
		Class:  r.Question[0].Qclass,
		Ttl:    s.ttl,
	}

	m.Answer = []dns.RR{
		&dns.A{
			Hdr: header,
			A:   node.IP,
		},
	}

	return m, nil

}
func (s *server) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {

	question := strings.ToLower(r.Question[0].Name)
	name := strings.TrimSuffix(question, s.domain)
	parts := strings.Split(name, ".")
	parts = parts[:len(parts)-1]

	if len(parts) != 2 {
		log.Printf("invalid query: %s", question)
		nameError(w, r)
		return
	}

	rType := parts[1]
	qType := r.Question[0].Qtype

	var m *dns.Msg
	var err error
	switch rType {
	case "services":
		switch qType {
		case dns.TypeA:
			m, err = s.ServicesA(w, r, name)
		case dns.TypeSRV:
			m, err = s.ServicesSRV(w, r, name)
		}
	case "nodes":
		switch qType {
		case dns.TypeA:
			m, err = s.NodesA(w, r, name)
		}
	}

	if err != nil {
		log.Println(err)
		if isKeyNotFound(err) {
			nameError(w, r)
		} else {
			dns.HandleFailed(w, r)
		}
		return
	}

	if m == nil {
		log.Printf("%s: not found", question)
		nameError(w, r)
		return
	}

	_ = w.WriteMsg(m)
}

// TODO: start a simple http status interface.  should be a different service?

func runServer(cmd *cobra.Command, args []string) {
	e := etcd.NewClient(([]string{etcdAddress}))

	parts := strings.Split(dnsDomain, ".")
	dom := make([]string, 0, len(parts))
	for _, p := range parts {
		if len(p) > 0 {
			dom = append(dom, p)
		}
	}

	s := &server{
		etcd:   e,
		domain: strings.ToLower(strings.Join(dom, ".") + "."),
		prefix: etcdPrefix,
		ttl:    uint32(dnsTTL),
	}

	dns.Handle(s.domain, s)

	server := &dns.Server{
		Addr:         fmt.Sprintf(":%d", dnsPort),
		Net:          "udp",
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	log.Fatal(server.ListenAndServe())
}
<commit_msg>Very rough initial take at supporting _service._protocol.services.<domain> style SRV queries<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"path/filepath"
	"strings"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/miekg/dns"
	"github.com/spf13/cobra"
)

type (
	server struct {
		etcd   *etcd.Client
		domain string
		prefix string
		ttl    uint32
	}
)

const (
	UnknownType = iota
	NodeType
	ServiceType
)

func nameError(w dns.ResponseWriter, req *dns.Msg) {
	m := &dns.Msg{}
	m.SetReply(req)
	m.SetRcode(req, dns.RcodeNameError)
	_ = w.WriteMsg(m)
}

func (s *server) GetNode(name string) (*node, error) {
	name = strings.ToLower(name)
	name = strings.TrimSuffix(name, ".nodes.")
	key := filepath.Join("/", s.prefix, "nodes", name)

	resp, err := s.etcd.Get(key, false, false)

	if err != nil {
		return nil, err
	}

	var n node
	err = json.Unmarshal([]byte(resp.Node.Value), &n)
	if err != nil {
		return nil, err
	}

	if n.IP == nil {
		log.Printf("node %s has no address", name)
		return nil, nil
	}
	return &n, nil
}

func (s *server) GetService(name string) ([]*record, error) {
	name = strings.TrimSuffix(name, ".services.")

	key := filepath.Join("/", s.prefix, "services", name)

	resp, err := s.etcd.Get(key, false, true)

	if err != nil {
		return nil, err
	}

	records := make([]*record, 0, len(resp.Node.Nodes))

	for _, n := range resp.Node.Nodes {
		var rec record
		err := json.Unmarshal([]byte(n.Value), &rec)
		if err != nil {
			log.Printf("json.Unmarshal failed for %s: %s", n.Key, err)
			continue
		}

		// should match against a regex?
		if rec.Target == "" {
			continue
		}

		records = append(records, &rec)
	}

	return records, nil
}

func (s *server) ServicesA(w dns.ResponseWriter, r *dns.Msg, name string) (*dns.Msg, error) {
	records, err := s.GetService(name)

	if err != nil {
		return nil, err
	}

	if len(records) == 0 {
		return nil, nil
	}
	m := &dns.Msg{}
	m.SetReply(r)

	header := dns.RR_Header{
		Name:   r.Question[0].Name,
		Rrtype: r.Question[0].Qtype,
		Class:  r.Question[0].Qclass,
		Ttl:    s.ttl,
	}

	m.Answer = make([]dns.RR, 0, len(records))
	for _, record := range records {
		// any target that contains a "." is assumed to not be a node
		if strings.Contains(record.Target, ".") {
			continue
		}

		node, err := s.GetNode(record.Target)
		if err != nil {
			continue
		}

		answer := &dns.A{
			Hdr: header,
			A:   node.IP,
		}

		m.Answer = append(m.Answer, answer)
	}

	return m, nil

}

func (s *server) ServicesSRV(w dns.ResponseWriter, r *dns.Msg, name string) (*dns.Msg, error) {
	records, err := s.GetService(name)
	if err != nil {
		return nil, err
	}

	if len(records) == 0 {
		return nil, nil
	}

	m := &dns.Msg{}
	m.SetReply(r)

	header := dns.RR_Header{
		Name:   r.Question[0].Name,
		Rrtype: r.Question[0].Qtype,
		Class:  r.Question[0].Qclass,
		Ttl:    s.ttl,
	}

	m.Answer = make([]dns.RR, 0, len(records))
	m.Extra = make([]dns.RR, 0, len(records))
	for _, record := range records {

		isNode := false

		target := strings.ToLower(record.Target)
		if !strings.Contains(target, ".") {
			target = strings.Join([]string{target, s.domain}, ".")
			isNode = true
			// should we make sure it exists before we add it?
		}

		answer := &dns.SRV{
			Hdr:      header,
			Priority: record.Priority,
			Weight:   record.Weight,
			Port:     record.Port,
			Target:   target,
		}

		m.Answer = append(m.Answer, answer)

		if !isNode {
			continue
		}

		node, err := s.GetNode(record.Target)
		if err != nil {
			// just skip it
			continue
		}

		extra := &dns.A{
			Hdr: dns.RR_Header{
				Name:   target,
				Rrtype: dns.TypeA,
				Class:  r.Question[0].Qclass,
				Ttl:    s.ttl,
			},
			A: node.IP,
		}

		m.Extra = append(m.Extra, extra)
	}

	return m, nil

}

func (s *server) NodesA(w dns.ResponseWriter, r *dns.Msg, name string) (*dns.Msg, error) {
	node, err := s.GetNode(name)

	if err != nil {
		return nil, err
	}

	m := &dns.Msg{}
	m.SetReply(r)

	header := dns.RR_Header{
		Name:   r.Question[0].Name,
		Rrtype: r.Question[0].Qtype,
		Class:  r.Question[0].Qclass,
		Ttl:    s.ttl,
	}

	m.Answer = []dns.RR{
		&dns.A{
			Hdr: header,
			A:   node.IP,
		},
	}

	return m, nil
}

func toType(s string) int {
	switch s {
	case "services":
		return ServiceType
	case "nodes":
		return NodeType
	}
	return UnknownType
}

func checkName(qType uint16, name string) (string, int) {
	parts := strings.Split(name, ".")
	parts = parts[:len(parts)-1]

	switch len(parts) {
	case 2:
		return parts[0], toType(parts[1])
	case 3:
		// horrible hack to munge _service._protocol.services.<domain> queries

		if qType == dns.TypeSRV && parts[2] == "services" && (parts[1] == "_tcp" || parts[1] == "_udp") {
			n := parts[0]
			if len(n) > 1 && (string([]rune(n)[0]) == "_") {
				return strings.TrimPrefix(n, "_"), ServiceType
			}
		}
	}
	// just horrible...
	return "", UnknownType
}

func (s *server) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {

	question := strings.ToLower(r.Question[0].Name)
	name := strings.TrimSuffix(question, s.domain)

	qType := r.Question[0].Qtype

	name, rType := checkName(qType, name)

	if name == "" || rType == UnknownType {
		log.Printf("invalid query: %s", question)
		nameError(w, r)
		return
	}

	var m *dns.Msg
	var err error
	switch rType {
	case ServiceType:
		switch qType {
		case dns.TypeA:
			m, err = s.ServicesA(w, r, name)
		case dns.TypeSRV:
			m, err = s.ServicesSRV(w, r, name)
		}
	case NodeType:
		switch qType {
		case dns.TypeA:
			m, err = s.NodesA(w, r, name)
		}
	}

	if err != nil {
		log.Println(err)
		if isKeyNotFound(err) {
			nameError(w, r)
		} else {
			dns.HandleFailed(w, r)
		}
		return
	}

	if m == nil {
		log.Printf("%s: not found", question)
		nameError(w, r)
		return
	}

	_ = w.WriteMsg(m)
}

// TODO: start a simple http status interface.  should be a different service?

func runServer(cmd *cobra.Command, args []string) {
	e := etcd.NewClient(([]string{etcdAddress}))

	parts := strings.Split(dnsDomain, ".")
	dom := make([]string, 0, len(parts))
	for _, p := range parts {
		if len(p) > 0 {
			dom = append(dom, p)
		}
	}

	s := &server{
		etcd:   e,
		domain: strings.ToLower(strings.Join(dom, ".") + "."),
		prefix: etcdPrefix,
		ttl:    uint32(dnsTTL),
	}

	dns.Handle(s.domain, s)

	server := &dns.Server{
		Addr:         fmt.Sprintf(":%d", dnsPort),
		Net:          "udp",
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	log.Fatal(server.ListenAndServe())
}
<|endoftext|>


<commit_before>package main

import (
  "bytes"
  "fmt"
  "io"
  "log"
  "mime"
  "net/http"
  "os"
  "strconv"
  "strings"
  "time"
)

func handleRequest(res http.ResponseWriter, req *http.Request) {
  var delay time.Duration = 0
  var reader io.Reader = new(bytes.Buffer)
  charset := "utf-8"
  mimetype := "application/octet-stream"
  status := 200

  // Delay response, when requested
  if val := req.Header.Get("X-Stub-Delay"); val != "" {
    amount, err := strconv.ParseInt(val, 10, 64)
    if err != nil {
      log.Printf("[%s]: Cannot interpret 'X-Stub-Delay' header value (%s) as integer\n", req.URL, val)
    } else {
      delay = time.Duration(amount) * time.Millisecond
    }
  }

  // Determine appropriate response status
  if val := req.Header.Get("X-Stub-Status"); val != "" {
    parsed, err := strconv.ParseInt(val, 10, 0)
    if err != nil || parsed < 100 || parsed >= 600 {
      log.Printf("[%s]: Invalid 'X-Stub-Status' value (%s)\n", req.URL, val)
    } else {
      status = int(parsed)
    }
  }

  // Determine appropriate MIME type
  if val := req.Header.Get("X-Stub-Content-Type"); val != "" {
    mediatype, _, err := mime.ParseMediaType(val)
    if err != nil {
      log.Printf("[%s]: Invalid MIME type specified (%s)\n", req.URL, val)
    } else {
      mimetype = mediatype
    }
  }

  // Determine character set (no validation here)
  if val := req.Header.Get("X-Stub-Charset"); val != "" {
    charset = val
  }

  // Use stub content for response body, when possible
  if val := req.Header.Get("X-Stub-Content"); val != "" {
    val = strings.Replace(val, "..", "", 0)
    fd, err := os.Open(config["content"] + "/" + val)
    if err != nil {
      fmt.Printf("Failed to read stub content: %s\n", config["content"] + "/" + val)
    } else {
      reader = fd
    }
    defer fd.Close()
  }

  // Respond
  time.Sleep(delay)
  res.Header().Set("Content-Type", mimetype + "; charset=" + charset)
  res.WriteHeader(status)
  if _, err := io.Copy(res, reader); err != nil {
    log.Printf("[%s]: Failed to write response\n", req.URL)
  }
}

func main() {
  config, _ := getConfig()
  fmt.Printf("Starting HTTP server on port [%s]\n", config["port"])
  http.HandleFunc("/", handleRequest)
  http.ListenAndServe(":" + config["port"], nil)
}
<commit_msg>Add annotation to main server package<commit_after>package main // import "github.com/mikattack/golang-rest-stub"

import (
  "bytes"
  "fmt"
  "io"
  "log"
  "mime"
  "net/http"
  "os"
  "strconv"
  "strings"
  "time"
)

func handleRequest(res http.ResponseWriter, req *http.Request) {
  var delay time.Duration = 0
  var reader io.Reader = new(bytes.Buffer)
  charset := "utf-8"
  mimetype := "application/octet-stream"
  status := 200

  // Delay response, when requested
  if val := req.Header.Get("X-Stub-Delay"); val != "" {
    amount, err := strconv.ParseInt(val, 10, 64)
    if err != nil {
      log.Printf("[%s]: Cannot interpret 'X-Stub-Delay' header value (%s) as integer\n", req.URL, val)
    } else {
      delay = time.Duration(amount) * time.Millisecond
    }
  }

  // Determine appropriate response status
  if val := req.Header.Get("X-Stub-Status"); val != "" {
    parsed, err := strconv.ParseInt(val, 10, 0)
    if err != nil || parsed < 100 || parsed >= 600 {
      log.Printf("[%s]: Invalid 'X-Stub-Status' value (%s)\n", req.URL, val)
    } else {
      status = int(parsed)
    }
  }

  // Determine appropriate MIME type
  if val := req.Header.Get("X-Stub-Content-Type"); val != "" {
    mediatype, _, err := mime.ParseMediaType(val)
    if err != nil {
      log.Printf("[%s]: Invalid MIME type specified (%s)\n", req.URL, val)
    } else {
      mimetype = mediatype
    }
  }

  // Determine character set (no validation here)
  if val := req.Header.Get("X-Stub-Charset"); val != "" {
    charset = val
  }

  // Use stub content for response body, when possible
  if val := req.Header.Get("X-Stub-Content"); val != "" {
    val = strings.Replace(val, "..", "", 0)
    fd, err := os.Open(config["content"] + "/" + val)
    if err != nil {
      fmt.Printf("Failed to read stub content: %s\n", config["content"] + "/" + val)
    } else {
      reader = fd
    }
    defer fd.Close()
  }

  // Respond
  time.Sleep(delay)
  res.Header().Set("Content-Type", mimetype + "; charset=" + charset)
  res.WriteHeader(status)
  if _, err := io.Copy(res, reader); err != nil {
    log.Printf("[%s]: Failed to write response\n", req.URL)
  }
}

func main() {
  config, _ := getConfig()
  fmt.Printf("Starting HTTP server on port [%s]\n", config["port"])
  http.HandleFunc("/", handleRequest)
  http.ListenAndServe(":" + config["port"], nil)
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"os"
	"time"

	"github.com/joho/godotenv"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/fasthttp"
	"github.com/labstack/echo/middleware"

	"github.com/shamsher31/stay-motivated-server/db"
	"github.com/shamsher31/stay-motivated-server/utils"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type Status int

const (
	PENDING Status = iota
	APPROVED
	CANCELED
)

// Quote defines structure of quote
type Quote struct {
	ID        bson.ObjectId `json:"-" bson:"_id,omitempty"`
	Title     string        `json:"title" bson:"title"`
	Author    string        `json:"author" bson:"author,omitempty"`
	Status    string        `json:"status" bson:"status"`
	Tag       []string      `json:"tag" bson:"tag"`
	Timestamp time.Time     `json:"timestamp" bson:"timestamp"`
}

// Server provides common mongo session
type Server struct {
	session *mgo.Session
}

// Global variable to hold mongo session
var gServer Server

func init() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error loading .env file")
	}
}

func main() {

	session := db.ConnectDB()
	defer session.Close()

	gServer = Server{session}

	e := echo.New()

	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	e.POST("/qoutes/", createQoute)
	e.GET("/qoutes/", getAllQoutes)
	e.GET("/qoutes/:id", getQoute)
	// e.PUT("/qoutes/:id", updateQoute)
	e.DELETE("/qoutes/:id", deleteQoute)

	e.Run(fasthttp.New(os.Getenv("PORT")))

}

func createQoute(c echo.Context) error {

	session := gServer.session.Copy()
	defer session.Close()

	title := c.FormValue("title")
	author := c.FormValue("author")

	id := db.GenerateID()
	qoutes := db.GetCollection(session, "qoutes")
	err := qoutes.Insert(&Quote{
		ID:        id,
		Title:     title,
		Author:    author,
		Timestamp: time.Now(),
	})

	utils.CheckError(err)

	return c.JSON(http.StatusOK, qoutes)
}

func getAllQoutes(c echo.Context) error {

	var results []Quote

	session := gServer.session.Copy()
	defer session.Close()

	qoutes := db.GetCollection(session, "qoutes")
	err := qoutes.Find(bson.M{}).One(&results)

	utils.CheckError(err)
	return c.JSON(http.StatusOK, results)
}

func getQoute(c echo.Context) error {

	session := gServer.session.Copy()
	defer session.Close()

	result := Quote{}
	qoutes := db.GetCollection(session, "qoutes")
	err := qoutes.Find(bson.M{}).One(&result)

	utils.CheckError(err)

	return c.JSON(http.StatusOK, result)
}

func deleteQoute(c echo.Context) error {

	session := gServer.session.Copy()
	defer session.Close()

	id := db.GetHexID(c.Param("id"))

	qoutes := db.GetCollection(session, "qoutes")
	err := qoutes.Remove(bson.M{"_id": id})

	utils.CheckError(err)

	return c.JSON(http.StatusOK, qoutes)

}
<commit_msg>Add qoute update method<commit_after>package main

import (
	"log"
	"net/http"
	"os"
	"time"

	"github.com/joho/godotenv"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/fasthttp"
	"github.com/labstack/echo/middleware"

	"github.com/shamsher31/stay-motivated-server/db"
	"github.com/shamsher31/stay-motivated-server/utils"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type Status int

const (
	PENDING Status = iota
	APPROVED
	CANCELED
)

// Quote defines structure of quote
type Quote struct {
	ID        bson.ObjectId `json:"-" bson:"_id,omitempty"`
	Title     string        `json:"title" bson:"title"`
	Author    string        `json:"author" bson:"author,omitempty"`
	Status    string        `json:"status" bson:"status"`
	Tag       []string      `json:"tag" bson:"tag"`
	Timestamp time.Time     `json:"timestamp" bson:"timestamp"`
}

// Server provides common mongo session
type Server struct {
	session *mgo.Session
}

// Global variable to hold mongo session
var gServer Server

func init() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error loading .env file")
	}
}

func main() {

	session := db.ConnectDB()
	defer session.Close()

	gServer = Server{session}

	e := echo.New()

	// Middleware
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	e.POST("/qoutes/", createQoute)
	e.GET("/qoutes/", getAllQoutes)
	e.GET("/qoutes/:id", getQoute)
	e.PUT("/qoutes/:id", updateQoute)
	e.DELETE("/qoutes/:id", deleteQoute)

	e.Run(fasthttp.New(os.Getenv("PORT")))

}

func createQoute(c echo.Context) error {

	session := gServer.session.Copy()
	defer session.Close()

	title := c.FormValue("title")
	author := c.FormValue("author")

	id := db.GenerateID()
	qoutes := db.GetCollection(session, "qoutes")
	err := qoutes.Insert(&Quote{
		ID:        id,
		Title:     title,
		Author:    author,
		Timestamp: time.Now(),
	})

	utils.CheckError(err)

	return c.JSON(http.StatusOK, qoutes)
}

func getAllQoutes(c echo.Context) error {

	var results []Quote

	session := gServer.session.Copy()
	defer session.Close()

	qoutes := db.GetCollection(session, "qoutes")
	err := qoutes.Find(bson.M{}).One(&results)

	utils.CheckError(err)
	return c.JSON(http.StatusOK, results)
}

func getQoute(c echo.Context) error {

	session := gServer.session.Copy()
	defer session.Close()

	result := Quote{}
	qoutes := db.GetCollection(session, "qoutes")
	err := qoutes.Find(bson.M{}).One(&result)

	utils.CheckError(err)

	return c.JSON(http.StatusOK, result)
}

func updateQoute(c echo.Context) error {
	session := gServer.session.Copy()
	defer session.Close()

	id := db.GetHexID(c.Param("id"))
	author := c.FormValue("author")

	qoutes := db.GetCollection(session, "qoutes")
	err := qoutes.Update(bson.M{"_id": id}, bson.M{"$set": bson.M{"author": author}})

	utils.CheckError(err)

	return c.JSON(http.StatusOK, qoutes)
}

func deleteQoute(c echo.Context) error {

	session := gServer.session.Copy()
	defer session.Close()

	id := db.GetHexID(c.Param("id"))

	qoutes := db.GetCollection(session, "qoutes")
	err := qoutes.Remove(bson.M{"_id": id})

	utils.CheckError(err)

	return c.JSON(http.StatusOK, qoutes)

}
<|endoftext|>


<commit_before><commit_msg>Fix typo.<commit_after><|endoftext|>


<commit_before>// This example would minify standalone Javascript files (identified by their content type)
// using the command line utility YUI compressor http://yui.github.io/yuicompressor/
// Example usage:
// ./yui -java /usr/local/bin/java -yuicompressor ~/Downloads/yuicompressor-2.4.8.jar
// $ curl -vx localhost:8080  http://golang.org/lib/godoc/godocs.js
// (function(){function g(){var u=$("#search");if(u.length===0){return}function t(){if(....
// $ curl http://golang.org/lib/godoc/godocs.js | head -n 3
// // Copyright 2012 The Go Authors. All rights reserved.
// // Use of this source code is governed by a BSD-style
// // license that can be found in the LICENSE file.
package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"os/exec"
	"path"
	"strings"

	"github.com/elazarl/goproxy"
)

func main() {
	verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
	addr := flag.String("addr", ":8080", "proxy listen address")
	java := flag.String("javapath", "java", "where the Java executable is located")
	yuicompressor := flag.String("yuicompressor", "", "where the yuicompressor is located, assumed to be in CWD")
	yuicompressordir := flag.String("yuicompressordir", ".", "a folder to search yuicompressor in, will be ignored if yuicompressor is set")
	flag.Parse()
	if *yuicompressor == "" {
		files, err := ioutil.ReadDir(*yuicompressordir)
		if err != nil {
			log.Fatal("Cannot find yuicompressor jar")
		}
		for _, file := range files {
			if strings.HasPrefix(file.Name(), "yuicompressor") && strings.HasSuffix(file.Name(), ".jar") {
				c := path.Join(*yuicompressordir, file.Name())
				yuicompressor = &c
				break
			}
		}
	}
	proxy := goproxy.NewProxyHttpServer()
	proxy.Verbose = *verbose
	proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
		contentType := resp.Header.Get("Content-Type")
		if contentType == "application/javascript" || contentType == "application/x-javascript" {
			// in real code, response should be streamed as well
			var err error
			cmd := exec.Command(*java, "-jar", *yuicompressor, "--type", "js")
			cmd.Stdin = resp.Body
			resp.Body, err = cmd.StdoutPipe()
			if err != nil {
				ctx.Warnf("Cannot minify content in %v: %v", ctx.Req.URL, err)
				return goproxy.TextResponse(ctx.Req, "Error getting stdout pipe")
			}
			if err := cmd.Start(); err != nil {
				ctx.Warnf("Cannot minify content in %v: %v", ctx.Req.URL, err)
			}
		}
		return resp
	})
	log.Fatal(http.ListenAndServe(*addr, proxy))
}
<commit_msg>ident doc, better checking for problems<commit_after>// This example would minify standalone Javascript files (identified by their content type)
// using the command line utility YUI compressor http://yui.github.io/yuicompressor/
// Example usage:
//
//    ./yui -java /usr/local/bin/java -yuicompressor ~/Downloads/yuicompressor-2.4.8.jar
//    $ curl -vx localhost:8080  http://golang.org/lib/godoc/godocs.js
//    (function(){function g(){var u=$("#search");if(u.length===0){return}function t(){if(....
//    $ curl http://golang.org/lib/godoc/godocs.js | head -n 3
//    // Copyright 2012 The Go Authors. All rights reserved.
//    // Use of this source code is governed by a BSD-style
//    // license that can be found in the LICENSE file.
package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/elazarl/goproxy"
)

func main() {
	verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
	addr := flag.String("addr", ":8080", "proxy listen address")
	java := flag.String("javapath", "java", "where the Java executable is located")
	yuicompressor := flag.String("yuicompressor", "", "where the yuicompressor is located, assumed to be in CWD")
	yuicompressordir := flag.String("yuicompressordir", ".", "a folder to search yuicompressor in, will be ignored if yuicompressor is set")
	flag.Parse()
	if *yuicompressor == "" {
		files, err := ioutil.ReadDir(*yuicompressordir)
		if err != nil {
			log.Fatal("Cannot find yuicompressor jar")
		}
		for _, file := range files {
			if strings.HasPrefix(file.Name(), "yuicompressor") && strings.HasSuffix(file.Name(), ".jar") {
				c := path.Join(*yuicompressordir, file.Name())
				yuicompressor = &c
				break
			}
		}
	}
	if *yuicompressor == "" {
		log.Fatal("Can't find yuicompressor jar, searched yuicompressor*.jar in dir ", *yuicompressordir)
	}
	if _, err := os.Stat(*yuicompressor); os.IsNotExist(err) {
		log.Fatal("Can't find yuicompressor jar specified ", *yuicompressor)
	}
	proxy := goproxy.NewProxyHttpServer()
	proxy.Verbose = *verbose
	proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
		contentType := resp.Header.Get("Content-Type")
		if contentType == "application/javascript" || contentType == "application/x-javascript" {
			// in real code, response should be streamed as well
			var err error
			cmd := exec.Command(*java, "-jar", *yuicompressor, "--type", "js")
			cmd.Stdin = resp.Body
			resp.Body, err = cmd.StdoutPipe()
			if err != nil {
				ctx.Warnf("Cannot minify content in %v: %v", ctx.Req.URL, err)
				return goproxy.TextResponse(ctx.Req, "Error getting stdout pipe")
			}
			if err := cmd.Start(); err != nil {
				ctx.Warnf("Cannot minify content in %v: %v", ctx.Req.URL, err)
			}
		}
		return resp
	})
	log.Fatal(http.ListenAndServe(*addr, proxy))
}
<|endoftext|>