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

// DNS server implementation.

package dns

import (
	"io"
	"os"
	"net"
)

type Handler interface {
	ServeDNS(w ResponseWriter, r *Msg)
	// IP based ACL mapping. The contains the string representation
	// of the IP address and a boolean saying it may connect (true) or not.
}

// TODO(mg): fit axfr responses in here too???
// A ResponseWriter interface is used by an DNS handler to
// construct an DNS response.
type ResponseWriter interface {
	// RemoteAddr returns the address of the client that sent the current request
	RemoteAddr() string

	Write([]byte) (int, os.Error)
}

type conn struct {
	remoteAddr net.Addr     // address of remote side (sans port)
	port       int          // port of the remote side, needed TODO(mg)
	handler    Handler      // request handler
	request    []byte       // bytes read
	_UDP       *net.UDPConn // i/o connection if UDP was used
	_TCP       *net.TCPConn // i/o connection if TCP was used
	hijacked   bool         // connection has been hijacked by hander TODO(mg)
}

type response struct {
	conn *conn
	req  *Msg
}

// ServeMux is an DNS request multiplexer. It matches the
// zone name of each incoming request against a list of
// registered patterns add calls the handler for the pattern
// that most closely matches the zone name.
type ServeMux struct {
	m map[string]Handler
}

// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return &ServeMux{make(map[string]Handler)} }

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = NewServeMux()

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as DNS handlers.  If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler object that calls f.
type HandlerFunc func(ResponseWriter, *Msg)

// ServerDNS calls f(w, reg)
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
	f(w, r)
}

// Helper handlers

func Refused(w ResponseWriter, r *Msg) {
        m := new(Msg)
        m.SetReply(r)
        m.MsgHdr.Rcode = RcodeRefused
        m.MsgHdr.Authoritative = false
        buf, _ := m.Pack()
        w.Write(buf)
}

// RefusedHandler return a REFUSED answer
func RefusedHandler() Handler { return HandlerFunc(Refused) }

func ListenAndServe(addr string, network string, handler Handler) os.Error {
	server := &Server{Addr: addr, Network: network, Handler: handler}
	return server.ListenAndServe()
}

func zoneMatch(pattern, zone string) bool {
	if len(pattern) == 0 {
		return false
	}
	n := len(pattern)
        var _ = n
        // better matching from the right TODO(mg)
	return true
}

func (mux *ServeMux) match(zone string) Handler {
	var h Handler
	var n = 0
	for k, v := range mux.m {
		if !zoneMatch(k, zone) {
			continue
		}
		if h == nil || len(k) > n {
			n = len(k)
			h = v
		}
	}
	return h
}

func (mux *ServeMux) Handle(pattern string, handler Handler) {
	if pattern == "" {
		panic("dns: invalid pattern " + pattern)
	}
	mux.m[pattern] = handler
}

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
	mux.Handle(pattern, HandlerFunc(handler))
}

// ServeDNS dispatches the request to the handler whose
// pattern most closely matches the request message.
func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) {
	h := mux.match(request.Question[0].Name)
	if h == nil {
		h = RefusedHandler()
	}
	h.ServeDNS(w, request)
}

// Handle register the handler the given pattern
// in the DefaultServeMux. The documentation for
// ServeMux explains how patters are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

// Serve accepts incoming DNS request on the TCP listener l,
// creating a new service thread for each.  The service threads
// read requests and then call handler to reply to them.
// Handler is typically nil, in which case the DefaultServeMux is used.
func ServeTCP(l *net.TCPListener, handler Handler) os.Error {
	srv := &Server{Handler: handler, Network: "tcp"}
	return srv.ServeTCP(l)
}

// Serve accepts incoming DNS request on the UDP Conn l,
// creating a new service thread for each.  The service threads
// read requests and then call handler to reply to them.
// Handler is typically nil, in which case the DefaultServeMux is used.
func ServeUDP(l *net.UDPConn, handler Handler) os.Error {
	srv := &Server{Handler: handler, Network: "udp"}
	return srv.ServeUDP(l)
}

// A Server defines parameters for running an HTTP server.
type Server struct {
	Addr         string  // address to listen on, ":dns" if empty
	Network      string  // If "tcp" it will invoke a TCP listener, otherwise an UDP one
	Handler      Handler // handler to invoke, http.DefaultServeMux if nil
	ReadTimeout  int64   // the net.Conn.SetReadTimeout value for new connections
	WriteTimeout int64   // the net.Conn.SetWriteTimeout value for new connections
}

// Fixes for udp/tcp
func (srv *Server) ListenAndServe() os.Error {
	addr := srv.Addr
	if addr == "" {
		addr = ":domain"
	}
	switch srv.Network {
	case "tcp":
		a, e := net.ResolveTCPAddr(addr)
		if e != nil {
			return e
		}
		l, e := net.ListenTCP("tcp", a)
		if e != nil {
			return e
		}
		return srv.ServeTCP(l)
	case "udp":
		a, e := net.ResolveUDPAddr(addr)
		if e != nil {
			return e
		}
		l, e := net.ListenUDP("udp", a)
		if e != nil {
			return e
		}
		return srv.ServeUDP(l)
	}
	return nil // os.Error with wrong network
}

func (srv *Server) ServeTCP(l *net.TCPListener) os.Error {
	defer l.Close()
	handler := srv.Handler
	if handler == nil {
		handler = DefaultServeMux
	}
forever:
	for {
		rw, e := l.AcceptTCP()
		if e != nil {
			return e
		}
		if srv.ReadTimeout != 0 {
			rw.SetReadTimeout(srv.ReadTimeout)
		}
		if srv.WriteTimeout != 0 {
			rw.SetWriteTimeout(srv.WriteTimeout)
		}
		l := make([]byte, 2)
		n, err := rw.Read(l)
		if err != nil || n != 2 {
			continue
		}
		length, _ := unpackUint16(l, 0)
		if length == 0 {
			continue
		}
		m := make([]byte, int(length))
		n, err = rw.Read(m[:int(length)])
		if err != nil {
			continue
		}
		i := n
		for i < int(length) {
			j, err := rw.Read(m[i:int(length)])
			if err != nil {
				continue forever
			}
			i += j
		}
		n = i
		d, err := newConn(rw, nil, rw.RemoteAddr(), m, handler)
		if err != nil {
			continue
		}
		go d.serve()
	}
	panic("not reached")
}

func (srv *Server) ServeUDP(l *net.UDPConn) os.Error {
	defer l.Close()
	handler := srv.Handler
	if handler == nil {
		handler = DefaultServeMux
	}
	for {
		m := make([]byte, DefaultMsgSize)
		n, a, e := l.ReadFromUDP(m)
		if e != nil {
			return e
		}
		m = m[:n]

		if srv.ReadTimeout != 0 {
			l.SetReadTimeout(srv.ReadTimeout)
		}
		if srv.WriteTimeout != 0 {
			l.SetWriteTimeout(srv.WriteTimeout)
		}
		d, err := newConn(nil, l, a, m, handler)
		if err != nil {
			continue
		}
		go d.serve()
	}
	panic("not reached")
}

func newConn(t *net.TCPConn, u *net.UDPConn, a net.Addr, buf []byte, handler Handler) (*conn, os.Error) {
	c := new(conn)
	c.handler = handler
	c._TCP = t
	c._UDP = u
	c.remoteAddr = a
	c.request = buf
	if t != nil {
		c.port = a.(*net.TCPAddr).Port
	}
	if u != nil {
		c.port = a.(*net.UDPAddr).Port
	}
	return c, nil
}

// Close the connection.
func (c *conn) close() {
	switch {
	case c._UDP != nil:
		c._UDP.Close()
		c._UDP = nil
	case c._TCP != nil:
		c._TCP.Close()
		c._TCP = nil
	}
}

// Serve a new connection.
func (c *conn) serve() {
        for {
                // Request has been read in ServeUDP or ServeTCP
                w := new(response)
                w.conn = c
                req := new(Msg)
                if !req.Unpack(c.request) {
                        break
                }
                w.req = req
                c.handler.ServeDNS(w, w.req) // this does the writing back to the client
                if c.hijacked {
                        return
                }
                break           // TODO(mg) Why is this a loop anyway
        }
        if c._TCP != nil {
                c.close() // Listen and Serve is closed then
        }
}

func (w *response) Write(data []byte) (n int, err os.Error) {
        switch {
        case w.conn._UDP != nil:
                n, err = w.conn._UDP.WriteTo(data, w.conn.remoteAddr)
                if err != nil {
                        return 0, err
                }
        case w.conn._TCP != nil:
                // TODO(mg) len(data) > 64K
                l := make([]byte, 2)
                l[0], l[1] = packUint16(uint16(len(data)))
                n, err = w.conn._TCP.Write(l)
                if err != nil {
                        return n, err
                }
                if n != 2 {
                        return n, io.ErrShortWrite
                }
                n, err = w.conn._TCP.Write(data)
                if err != nil {
                        return n, err
                }
                i := n
                if i < len(data) {
                        j, err := w.conn._TCP.Write(data[i:len(data)])
                        if err != nil {
                                return i, err
                        }
                        i += j
                }
                n = i
        }
        return n, nil
}

// RemoteAddr implements the ResponseWriter.RemoteAddr method
func (w *response) RemoteAddr() string { return w.conn.remoteAddr.String() }
<commit_msg>Fix the matching function<commit_after>// Copyright 2011 Miek Gieben. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// DNS server implementation.

package dns

import (
	"io"
	"os"
	"net"
)

type Handler interface {
	ServeDNS(w ResponseWriter, r *Msg)
	// IP based ACL mapping. The contains the string representation
	// of the IP address and a boolean saying it may connect (true) or not.
}

// TODO(mg): fit axfr responses in here too???
// A ResponseWriter interface is used by an DNS handler to
// construct an DNS response.
type ResponseWriter interface {
	// RemoteAddr returns the address of the client that sent the current request
	RemoteAddr() string

	Write([]byte) (int, os.Error)
}

type conn struct {
	remoteAddr net.Addr     // address of remote side (sans port)
	port       int          // port of the remote side, needed TODO(mg)
	handler    Handler      // request handler
	request    []byte       // bytes read
	_UDP       *net.UDPConn // i/o connection if UDP was used
	_TCP       *net.TCPConn // i/o connection if TCP was used
	hijacked   bool         // connection has been hijacked by hander TODO(mg)
}

type response struct {
	conn *conn
	req  *Msg
}

// ServeMux is an DNS request multiplexer. It matches the
// zone name of each incoming request against a list of
// registered patterns add calls the handler for the pattern
// that most closely matches the zone name.
type ServeMux struct {
	m map[string]Handler
}

// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return &ServeMux{make(map[string]Handler)} }

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = NewServeMux()

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as DNS handlers.  If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler object that calls f.
type HandlerFunc func(ResponseWriter, *Msg)

// ServerDNS calls f(w, reg)
func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) {
	f(w, r)
}

// Helper handlers

func Refused(w ResponseWriter, r *Msg) {
        m := new(Msg)
        m.SetReply(r)
        m.MsgHdr.Rcode = RcodeRefused
        m.MsgHdr.Authoritative = false
        buf, _ := m.Pack()
        w.Write(buf)
}

// RefusedHandler return a REFUSED answer
func RefusedHandler() Handler { return HandlerFunc(Refused) }

func ListenAndServe(addr string, network string, handler Handler) os.Error {
	server := &Server{Addr: addr, Network: network, Handler: handler}
	return server.ListenAndServe()
}

func zoneMatch(pattern, zone string) (ok bool) {
	if len(pattern) == 0 {
		return
	}
        i:=0
        for {
                ok = pattern[len(pattern)-1-i] == zone[len(zone)-1-i]
                i++

                if !ok {
                        break
                }
                if len(pattern)-1-i < 0 || len(zone)-1-i < 0{
                        break
                }

        }
	return
}

func (mux *ServeMux) match(zone string) Handler {
	var h Handler
	var n = 0
	for k, v := range mux.m {
		if !zoneMatch(k, zone) {
			continue
		}
		if h == nil || len(k) > n {
			n = len(k)
			h = v
		}
	}
	return h
}

func (mux *ServeMux) Handle(pattern string, handler Handler) {
	if pattern == "" {
		panic("dns: invalid pattern " + pattern)
	}
        if pattern[len(pattern)-1] != '.' {             // no ending .
	        mux.m[pattern + "."] = handler
        } else {
	        mux.m[pattern]= handler
        }
}

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
	mux.Handle(pattern, HandlerFunc(handler))
}

// ServeDNS dispatches the request to the handler whose
// pattern most closely matches the request message.
func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) {
	h := mux.match(request.Question[0].Name)
	if h == nil {
		h = RefusedHandler()
	}
	h.ServeDNS(w, request)
}

// Handle register the handler the given pattern
// in the DefaultServeMux. The documentation for
// ServeMux explains how patters are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

// Serve accepts incoming DNS request on the TCP listener l,
// creating a new service thread for each.  The service threads
// read requests and then call handler to reply to them.
// Handler is typically nil, in which case the DefaultServeMux is used.
func ServeTCP(l *net.TCPListener, handler Handler) os.Error {
	srv := &Server{Handler: handler, Network: "tcp"}
	return srv.ServeTCP(l)
}

// Serve accepts incoming DNS request on the UDP Conn l,
// creating a new service thread for each.  The service threads
// read requests and then call handler to reply to them.
// Handler is typically nil, in which case the DefaultServeMux is used.
func ServeUDP(l *net.UDPConn, handler Handler) os.Error {
	srv := &Server{Handler: handler, Network: "udp"}
	return srv.ServeUDP(l)
}

// A Server defines parameters for running an HTTP server.
type Server struct {
	Addr         string  // address to listen on, ":dns" if empty
	Network      string  // If "tcp" it will invoke a TCP listener, otherwise an UDP one
	Handler      Handler // handler to invoke, http.DefaultServeMux if nil
	ReadTimeout  int64   // the net.Conn.SetReadTimeout value for new connections
	WriteTimeout int64   // the net.Conn.SetWriteTimeout value for new connections
}

// Fixes for udp/tcp
func (srv *Server) ListenAndServe() os.Error {
	addr := srv.Addr
	if addr == "" {
		addr = ":domain"
	}
	switch srv.Network {
	case "tcp":
		a, e := net.ResolveTCPAddr(addr)
		if e != nil {
			return e
		}
		l, e := net.ListenTCP("tcp", a)
		if e != nil {
			return e
		}
		return srv.ServeTCP(l)
	case "udp":
		a, e := net.ResolveUDPAddr(addr)
		if e != nil {
			return e
		}
		l, e := net.ListenUDP("udp", a)
		if e != nil {
			return e
		}
		return srv.ServeUDP(l)
	}
	return nil // os.Error with wrong network
}

func (srv *Server) ServeTCP(l *net.TCPListener) os.Error {
	defer l.Close()
	handler := srv.Handler
	if handler == nil {
		handler = DefaultServeMux
	}
forever:
	for {
		rw, e := l.AcceptTCP()
		if e != nil {
			return e
		}
		if srv.ReadTimeout != 0 {
			rw.SetReadTimeout(srv.ReadTimeout)
		}
		if srv.WriteTimeout != 0 {
			rw.SetWriteTimeout(srv.WriteTimeout)
		}
		l := make([]byte, 2)
		n, err := rw.Read(l)
		if err != nil || n != 2 {
			continue
		}
		length, _ := unpackUint16(l, 0)
		if length == 0 {
			continue
		}
		m := make([]byte, int(length))
		n, err = rw.Read(m[:int(length)])
		if err != nil {
			continue
		}
		i := n
		for i < int(length) {
			j, err := rw.Read(m[i:int(length)])
			if err != nil {
				continue forever
			}
			i += j
		}
		n = i
		d, err := newConn(rw, nil, rw.RemoteAddr(), m, handler)
		if err != nil {
			continue
		}
		go d.serve()
	}
	panic("not reached")
}

func (srv *Server) ServeUDP(l *net.UDPConn) os.Error {
	defer l.Close()
	handler := srv.Handler
	if handler == nil {
		handler = DefaultServeMux
	}
	for {
		m := make([]byte, DefaultMsgSize)
		n, a, e := l.ReadFromUDP(m)
		if e != nil {
			return e
		}
		m = m[:n]

		if srv.ReadTimeout != 0 {
			l.SetReadTimeout(srv.ReadTimeout)
		}
		if srv.WriteTimeout != 0 {
			l.SetWriteTimeout(srv.WriteTimeout)
		}
		d, err := newConn(nil, l, a, m, handler)
		if err != nil {
			continue
		}
		go d.serve()
	}
	panic("not reached")
}

func newConn(t *net.TCPConn, u *net.UDPConn, a net.Addr, buf []byte, handler Handler) (*conn, os.Error) {
	c := new(conn)
	c.handler = handler
	c._TCP = t
	c._UDP = u
	c.remoteAddr = a
	c.request = buf
	if t != nil {
		c.port = a.(*net.TCPAddr).Port
	}
	if u != nil {
		c.port = a.(*net.UDPAddr).Port
	}
	return c, nil
}

// Close the connection.
func (c *conn) close() {
	switch {
	case c._UDP != nil:
		c._UDP.Close()
		c._UDP = nil
	case c._TCP != nil:
		c._TCP.Close()
		c._TCP = nil
	}
}

// Serve a new connection.
func (c *conn) serve() {
        for {
                // Request has been read in ServeUDP or ServeTCP
                w := new(response)
                w.conn = c
                req := new(Msg)
                if !req.Unpack(c.request) {
                        break
                }
                w.req = req
                c.handler.ServeDNS(w, w.req) // this does the writing back to the client
                if c.hijacked {
                        return
                }
                break           // TODO(mg) Why is this a loop anyway
        }
        if c._TCP != nil {
                c.close() // Listen and Serve is closed then
        }
}

func (w *response) Write(data []byte) (n int, err os.Error) {
        switch {
        case w.conn._UDP != nil:
                n, err = w.conn._UDP.WriteTo(data, w.conn.remoteAddr)
                if err != nil {
                        return 0, err
                }
        case w.conn._TCP != nil:
                // TODO(mg) len(data) > 64K
                l := make([]byte, 2)
                l[0], l[1] = packUint16(uint16(len(data)))
                n, err = w.conn._TCP.Write(l)
                if err != nil {
                        return n, err
                }
                if n != 2 {
                        return n, io.ErrShortWrite
                }
                n, err = w.conn._TCP.Write(data)
                if err != nil {
                        return n, err
                }
                i := n
                if i < len(data) {
                        j, err := w.conn._TCP.Write(data[i:len(data)])
                        if err != nil {
                                return i, err
                        }
                        i += j
                }
                n = i
        }
        return n, nil
}

// RemoteAddr implements the ResponseWriter.RemoteAddr method
func (w *response) RemoteAddr() string { return w.conn.remoteAddr.String() }
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	flags "github.com/jessevdk/go-flags"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
)

type ResponseData struct {
	StatusCode  int      `json:"-"`
	Key         string   `json:"key"`
	IsNamespace bool     `json:"namespace"`
	Value       string   `json:"value"`
	Keys        []string `json:"keys,omitempty"`  // need better decision here
	Error       string   `json:"error,omitempty"` // need better decision here
}

var validKey = regexp.MustCompile(`^[a-zA-Z0-9_\-/]+$`)

func handler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	var responseData ResponseData
	key := r.URL.Path[1:]
	if validKey.MatchString(key) {
		key_path := expandPath(key)
		value := r.PostForm.Get("value")
		var keys []string
		var err error

		switch r.Method {
		case "GET":
			var values []string
			var fileInfo os.FileInfo
			values, fileInfo, err = readKey(key_path)
			if fileInfo.IsDir() {
				keys = values
			} else {
				value = values[0]
			}
		case "DELETE":
			err = deleteKey(key_path)
		case "PUT", "POST":
			err = putKey(key_path, value)
		}

		if err == nil {
			responseData = ResponseData{StatusCode: http.StatusOK, Key: key, Value: value, Keys: keys}
			if keys == nil {
				responseData.IsNamespace = false
			} else {
				responseData.IsNamespace = true
			}
		} else {
			responseData = ResponseData{StatusCode: http.StatusNotFound, Key: key, Error: err.Error()}
		}
	} else {
		responseData = ResponseData{StatusCode: http.StatusBadRequest, Key: key, Error: "Invalid key. Only " + validKey.String() + " allowed!"}
	}

	content, err := json.Marshal(responseData)
	if err == nil && responseData.StatusCode != 0 {
		callHook(key, r.Method)
		w.WriteHeader(responseData.StatusCode)
		w.Write(append(content, '\n'))
	} else {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(http.StatusText(http.StatusInternalServerError)))
		fmt.Println(err)
	}
}

func readKey(path string) ([]string, os.FileInfo, error) {
	var result []string
	var err error

	var fileInfo os.FileInfo
	fileInfo, err = os.Stat(path)
	if err == nil {
		if fileInfo.IsDir() {
			var files []os.FileInfo
			if files, err = ioutil.ReadDir(path); err == nil {
				for _, f := range files {
					result = append(result, f.Name())
				}
			}
		} else {
			var content []byte
			if content, err = ioutil.ReadFile(path); err == nil {
				result = append(result, string(content))
			}
		}
	}
	return result, fileInfo, err
}

func putKey(path string, value string) error {
	os.MkdirAll(filepath.Dir(path), os.ModePerm)
	return ioutil.WriteFile(path, []byte(value), os.ModePerm)
}

func deleteKey(path string) error {
	return os.Remove(path)
}

func expandPath(key string) string {
	return filepath.Join(opts.DataPath, key)
}

// Return nil if File exists, else non-nil value
func fileExists(filename string) error {
	_, err := os.Stat(filename)
	return err
}

// Return nil if filename is a directory, else non-nil value
func isDirectory(filename string) error {
	stat, err := os.Stat(filename)
	if err == nil && !stat.IsDir() {
		return errors.New("'" + filename + "' is not a directory!")
	}
	return nil
}

// ignore errors, just print them and continue
func callHook(key, action string) {
	for _, hookUrl := range opts.WebHookUrls {
		if hookUrl[:4] != "http" {
			hookUrl = "http://" + hookUrl
		}
		go func(hookUrl string, hookData url.Values) {
			if _, err := http.PostForm(hookUrl, hookData); err != nil {
				fmt.Printf("WebHook Post failed: %s", err)
			}
		}(hookUrl, url.Values{"key": {key}, "action": {action}})
	}
}

var opts struct {
	DataPath    string   `short:"d" long:"data-path" default:"./data" description:"Directory where files will be stored."`
	Port        int      `short:"p" long:"port" default:"8080" description:"Port where server is listening for requests."`
	WebHookUrls []string `short:"w" long:"webhook-url" description:"WebHook-Urls."`
}

func main() {
	flags.Parse(&opts)
	opts.DataPath, _ = filepath.Abs(opts.DataPath)
	fmt.Println("DATA_PATH:", opts.DataPath)
	fmt.Println("PORT:", opts.Port)

	http.HandleFunc("/", handler)
	http.ListenAndServe(":"+strconv.Itoa(opts.Port), nil)
}
<commit_msg>fix file not found bug<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	flags "github.com/jessevdk/go-flags"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
)

type ResponseData struct {
	StatusCode  int      `json:"-"`
	Key         string   `json:"key"`
	IsNamespace bool     `json:"namespace"`
	Value       string   `json:"value"`
	Keys        []string `json:"keys,omitempty"`  // need better decision here
	Error       string   `json:"error,omitempty"` // need better decision here
}

var validKey = regexp.MustCompile(`^[a-zA-Z0-9_\-/]+$`)

func handler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	var responseData ResponseData
	key := r.URL.Path[1:]
	if validKey.MatchString(key) {
		key_path := expandPath(key)
		value := r.PostForm.Get("value")
		var keys []string
		var err error

		switch r.Method {
		case "GET":
			var values []string
			values, err = readKey(key_path)
			if len(values) == 1 {
				value = values[0]
			} else {
				keys = values
			}
		case "DELETE":
			err = deleteKey(key_path)
		case "PUT", "POST":
			err = putKey(key_path, value)
		}

		if err == nil {
			responseData = ResponseData{StatusCode: http.StatusOK, Key: key, Value: value, Keys: keys}
			if keys == nil {
				responseData.IsNamespace = false
			} else {
				responseData.IsNamespace = true
			}
		} else {
			responseData = ResponseData{StatusCode: http.StatusNotFound, Key: key, Error: err.Error()}
		}
	} else {
		responseData = ResponseData{StatusCode: http.StatusBadRequest, Key: key, Error: "Invalid key. Only " + validKey.String() + " allowed!"}
	}

	content, err := json.Marshal(responseData)
	if err == nil && responseData.StatusCode != 0 {
		callHook(key, r.Method)
		w.WriteHeader(responseData.StatusCode)
		w.Write(append(content, '\n'))
	} else {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(http.StatusText(http.StatusInternalServerError)))
		fmt.Println(err)
	}
}

func readKey(path string) ([]string, error) {
	var result []string
	var err error

	var fileInfo os.FileInfo
	fileInfo, err = os.Stat(path)
	if err == nil {
		if fileInfo.IsDir() {
			var files []os.FileInfo
			if files, err = ioutil.ReadDir(path); err == nil {
				for _, f := range files {
					result = append(result, f.Name())
				}
			}
		} else {
			var content []byte
			if content, err = ioutil.ReadFile(path); err == nil {
				result = append(result, string(content))
			}
		}
	}
	return result, err
}

func putKey(path string, value string) error {
	os.MkdirAll(filepath.Dir(path), os.ModePerm)
	return ioutil.WriteFile(path, []byte(value), os.ModePerm)
}

func deleteKey(path string) error {
	return os.Remove(path)
}

func expandPath(key string) string {
	return filepath.Join(opts.DataPath, key)
}

// Return nil if File exists, else non-nil value
func fileExists(filename string) error {
	_, err := os.Stat(filename)
	return err
}

// Return nil if filename is a directory, else non-nil value
func isDirectory(filename string) error {
	stat, err := os.Stat(filename)
	if err == nil && !stat.IsDir() {
		return errors.New("'" + filename + "' is not a directory!")
	}
	return nil
}

// ignore errors, just print them and continue
func callHook(key, action string) {
	for _, hookUrl := range opts.WebHookUrls {
		if hookUrl[:4] != "http" {
			hookUrl = "http://" + hookUrl
		}
		go func(hookUrl string, hookData url.Values) {
			if _, err := http.PostForm(hookUrl, hookData); err != nil {
				fmt.Printf("WebHook Post failed: %s", err)
			}
		}(hookUrl, url.Values{"key": {key}, "action": {action}})
	}
}

var opts struct {
	DataPath    string   `short:"d" long:"data-path" default:"./data" description:"Directory where files will be stored."`
	Port        int      `short:"p" long:"port" default:"8080" description:"Port where server is listening for requests."`
	WebHookUrls []string `short:"w" long:"webhook-url" description:"WebHook-Urls."`
}

func main() {
	flags.Parse(&opts)
	opts.DataPath, _ = filepath.Abs(opts.DataPath)
	fmt.Println("DATA_PATH:", opts.DataPath)
	fmt.Println("PORT:", opts.Port)

	http.HandleFunc("/", handler)
	http.ListenAndServe(":"+strconv.Itoa(opts.Port), nil)
}
<|endoftext|>


<commit_before>package webapp

import (
	"encoding/json"
	"net/http"
	"path/filepath"
	"strconv"
	"time"

	"github.com/tag1consulting/pipeviz/Godeps/_workspace/src/github.com/Sirupsen/logrus"
	"github.com/tag1consulting/pipeviz/Godeps/_workspace/src/github.com/gorilla/websocket"
	"github.com/tag1consulting/pipeviz/Godeps/_workspace/src/github.com/zenazn/goji/web"
	"github.com/tag1consulting/pipeviz/broker"
	"github.com/tag1consulting/pipeviz/log"
	"github.com/tag1consulting/pipeviz/mlog"
	"github.com/tag1consulting/pipeviz/represent"
	"github.com/tag1consulting/pipeviz/represent/q"
	"github.com/tag1consulting/pipeviz/types/system"
)

var (
	publicDir = filepath.Join(defaultBase("github.com/tag1consulting/pipeviz/webapp/"), "public")
)

var (
	// Subscribe to the master broker and store latest locally as it comes
	brokerListen = broker.Get().Subscribe()
	// Initially set the latestGraph to a new, empty one to avoid nil pointer
	latestGraph = represent.NewGraph()
	// Count of active websocket clients (for expvars)
	clientCount int64
)

const (
	// Time allowed to write data to the client.
	writeWait = 10 * time.Second
	// Time allowed to read the next pong message from the client.
	pongWait = 60 * time.Second
	// Send pings to client with this period; less than pongWait.
	pingPeriod = (pongWait * 9) / 10
)

var (
	// gorilla websocket upgrader
	upgrader = websocket.Upgrader{ReadBufferSize: 256, WriteBufferSize: 8192}
)

func init() {
	// Kick off goroutine to listen on the graph broker and keep our local pointer up to date
	go func() {
		for g := range brokerListen {
			latestGraph = g
		}
	}()
}

// Creates a Goji *web.Mux that can act as the http muxer for the frontend app.
func NewMux() *web.Mux {
	m := web.New()

	m.Use(log.NewHTTPLogger("webapp"))
	m.Get("/sock", openSocket)
	m.Get("/message/:mid", getMessage)
	m.Get("/*", http.StripPrefix("/", http.FileServer(http.Dir(publicDir))))

	return m
}

// RegisterToMux adds all necessary pieces to an injected mux.
func RegisterToMux(m *web.Mux) {
	m.Use(log.NewHTTPLogger("webapp"))
	m.Get("/sock", openSocket)
	m.Get("/message/:mid", getMessage)
	m.Get("/*", http.StripPrefix("/", http.FileServer(http.Dir(publicDir))))
}

func graphToJSON(g system.CoreGraph) ([]byte, error) {
	var vertices []interface{}
	for _, v := range g.VerticesWith(q.Qbv(system.VTypeNone)) {
		vertices = append(vertices, v.Flat())
	}

	// TODO use something that lets us write to a reusable byte buffer instead
	return json.Marshal(struct {
		Id       uint64        `json:"id"`
		Vertices []interface{} `json:"vertices"`
	}{
		Id:       g.MsgID(),
		Vertices: vertices,
	})
}

func getMessage(c web.C, w http.ResponseWriter, r *http.Request) {
	id, err := strconv.ParseUint(c.URLParams["mid"], 10, 64)
	if err != nil {
		http.Error(w, http.StatusText(400), 400)
		return
	}

	var getter mlog.RecordGetter
	var ok bool
	if fun, exists := c.Env["mlogGet"]; !exists {
		http.Error(w, "Could not access mlog storage", 500)
		return
	} else if getter, ok = fun.(mlog.RecordGetter); !ok {
		http.Error(w, "Could not access mlog storage", 500)
		return
	}

	rec, err := getter(id)
	if err != nil {
		// TODO Might be something other than not found, but oh well for now
		http.Error(w, http.StatusText(404), 404)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	w.Write(rec.Message)
}

func openSocket(w http.ResponseWriter, r *http.Request) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		entry := logrus.WithFields(logrus.Fields{
			"system": "webapp",
			"err":    err,
		})

		if _, ok := err.(websocket.HandshakeError); !ok {
			entry.Error("Error on attempting upgrade to websocket")
		} else {
			entry.Warn("Handshake error on websocket upgrade")
		}
		return
	}

	clientCount++
	go wsWriter(ws)
	wsReader(ws)
	clientCount--
}

func wsReader(ws *websocket.Conn) {
	defer ws.Close()
	ws.SetReadLimit(512)
	ws.SetReadDeadline(time.Now().Add(pongWait))
	ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	// for now, we ignore all messages from the frontend
	for {
		_, _, err := ws.ReadMessage()
		if err != nil {
			break
		}
	}
}

func wsWriter(ws *websocket.Conn) {
	graphIn := broker.Get().Subscribe()
	pingTicker := time.NewTicker(pingPeriod)
	defer func() {
		pingTicker.Stop()
		broker.Get().Unsubscribe(graphIn)
		ws.Close()
	}()

	// write the current graph state first, before entering loop
	graphToSock(ws, latestGraph)
	var g system.CoreGraph
	for {
		select {
		case <-pingTicker.C:
			// ensure client connection is healthy
			ws.SetWriteDeadline(time.Now().Add(writeWait))
			if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
				return
			}
		case g = <-graphIn:
			graphToSock(ws, g)
		}
	}
}

func graphToSock(ws *websocket.Conn, g system.CoreGraph) {
	j, err := graphToJSON(g)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"system": "webapp",
			"err":    err,
		}).Error("Error while marshaling graph into JSON for transmission over websocket")
	}

	if j != nil {
		ws.SetWriteDeadline(time.Now().Add(writeWait))
		if err := ws.WriteMessage(websocket.TextMessage, j); err != nil {
			logrus.WithFields(logrus.Fields{
				"system": "webapp",
				"err":    err,
			}).Error("Error while writing graph data to websocket")
			return
		}
	}
}
<commit_msg>Rewrite import paths<commit_after>package webapp

import (
	"encoding/json"
	"net/http"
	"path/filepath"
	"strconv"
	"time"

	"github.com/pipeviz/pipeviz/Godeps/_workspace/src/github.com/Sirupsen/logrus"
	"github.com/pipeviz/pipeviz/Godeps/_workspace/src/github.com/gorilla/websocket"
	"github.com/pipeviz/pipeviz/Godeps/_workspace/src/github.com/zenazn/goji/web"
	"github.com/pipeviz/pipeviz/broker"
	"github.com/pipeviz/pipeviz/log"
	"github.com/pipeviz/pipeviz/mlog"
	"github.com/pipeviz/pipeviz/represent"
	"github.com/pipeviz/pipeviz/represent/q"
	"github.com/pipeviz/pipeviz/types/system"
)

var (
	publicDir = filepath.Join(defaultBase("github.com/pipeviz/pipeviz/webapp/"), "public")
)

var (
	// Subscribe to the master broker and store latest locally as it comes
	brokerListen = broker.Get().Subscribe()
	// Initially set the latestGraph to a new, empty one to avoid nil pointer
	latestGraph = represent.NewGraph()
	// Count of active websocket clients (for expvars)
	clientCount int64
)

const (
	// Time allowed to write data to the client.
	writeWait = 10 * time.Second
	// Time allowed to read the next pong message from the client.
	pongWait = 60 * time.Second
	// Send pings to client with this period; less than pongWait.
	pingPeriod = (pongWait * 9) / 10
)

var (
	// gorilla websocket upgrader
	upgrader = websocket.Upgrader{ReadBufferSize: 256, WriteBufferSize: 8192}
)

func init() {
	// Kick off goroutine to listen on the graph broker and keep our local pointer up to date
	go func() {
		for g := range brokerListen {
			latestGraph = g
		}
	}()
}

// Creates a Goji *web.Mux that can act as the http muxer for the frontend app.
func NewMux() *web.Mux {
	m := web.New()

	m.Use(log.NewHTTPLogger("webapp"))
	m.Get("/sock", openSocket)
	m.Get("/message/:mid", getMessage)
	m.Get("/*", http.StripPrefix("/", http.FileServer(http.Dir(publicDir))))

	return m
}

// RegisterToMux adds all necessary pieces to an injected mux.
func RegisterToMux(m *web.Mux) {
	m.Use(log.NewHTTPLogger("webapp"))
	m.Get("/sock", openSocket)
	m.Get("/message/:mid", getMessage)
	m.Get("/*", http.StripPrefix("/", http.FileServer(http.Dir(publicDir))))
}

func graphToJSON(g system.CoreGraph) ([]byte, error) {
	var vertices []interface{}
	for _, v := range g.VerticesWith(q.Qbv(system.VTypeNone)) {
		vertices = append(vertices, v.Flat())
	}

	// TODO use something that lets us write to a reusable byte buffer instead
	return json.Marshal(struct {
		Id       uint64        `json:"id"`
		Vertices []interface{} `json:"vertices"`
	}{
		Id:       g.MsgID(),
		Vertices: vertices,
	})
}

func getMessage(c web.C, w http.ResponseWriter, r *http.Request) {
	id, err := strconv.ParseUint(c.URLParams["mid"], 10, 64)
	if err != nil {
		http.Error(w, http.StatusText(400), 400)
		return
	}

	var getter mlog.RecordGetter
	var ok bool
	if fun, exists := c.Env["mlogGet"]; !exists {
		http.Error(w, "Could not access mlog storage", 500)
		return
	} else if getter, ok = fun.(mlog.RecordGetter); !ok {
		http.Error(w, "Could not access mlog storage", 500)
		return
	}

	rec, err := getter(id)
	if err != nil {
		// TODO Might be something other than not found, but oh well for now
		http.Error(w, http.StatusText(404), 404)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	w.Write(rec.Message)
}

func openSocket(w http.ResponseWriter, r *http.Request) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		entry := logrus.WithFields(logrus.Fields{
			"system": "webapp",
			"err":    err,
		})

		if _, ok := err.(websocket.HandshakeError); !ok {
			entry.Error("Error on attempting upgrade to websocket")
		} else {
			entry.Warn("Handshake error on websocket upgrade")
		}
		return
	}

	clientCount++
	go wsWriter(ws)
	wsReader(ws)
	clientCount--
}

func wsReader(ws *websocket.Conn) {
	defer ws.Close()
	ws.SetReadLimit(512)
	ws.SetReadDeadline(time.Now().Add(pongWait))
	ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	// for now, we ignore all messages from the frontend
	for {
		_, _, err := ws.ReadMessage()
		if err != nil {
			break
		}
	}
}

func wsWriter(ws *websocket.Conn) {
	graphIn := broker.Get().Subscribe()
	pingTicker := time.NewTicker(pingPeriod)
	defer func() {
		pingTicker.Stop()
		broker.Get().Unsubscribe(graphIn)
		ws.Close()
	}()

	// write the current graph state first, before entering loop
	graphToSock(ws, latestGraph)
	var g system.CoreGraph
	for {
		select {
		case <-pingTicker.C:
			// ensure client connection is healthy
			ws.SetWriteDeadline(time.Now().Add(writeWait))
			if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
				return
			}
		case g = <-graphIn:
			graphToSock(ws, g)
		}
	}
}

func graphToSock(ws *websocket.Conn, g system.CoreGraph) {
	j, err := graphToJSON(g)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"system": "webapp",
			"err":    err,
		}).Error("Error while marshaling graph into JSON for transmission over websocket")
	}

	if j != nil {
		ws.SetWriteDeadline(time.Now().Add(writeWait))
		if err := ws.WriteMessage(websocket.TextMessage, j); err != nil {
			logrus.WithFields(logrus.Fields{
				"system": "webapp",
				"err":    err,
			}).Error("Error while writing graph data to websocket")
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/Shopify/sarama"
	rfc3164 "github.com/jeromer/syslogparser/rfc3164"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"
)

var (
	cfg       *config
	logger    = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile)
	port      = flag.Int("port", 514, "port on which to listen")
	verbose   = flag.Bool("verbose", false, "print all messages to stdout")
	publish   = flag.Bool("publish", false, "publish messages to kafka")
	topic     = flag.String("topic", "syslog", "kafka topic to publish on")
	zkstring  = flag.String("zkstring", "localhost:2181", "ZooKeeper broker connection string")
	flushTime = flag.String("flushTime", "1s", "max time before flushing messages to kafka, e.g. 1s, 2m")
)

type config struct {
	port      int
	verbose   bool
	publish   bool
	topic     string
	zkstring  string
	flushTime time.Duration
}

func (c *config) String() string {
	return fmt.Sprintf("port: %d, verbose: %t, publish: %t, topic: %s, zkstring: %s, flushTime: %s",
		c.port, c.verbose, c.publish, c.topic, c.zkstring, c.flushTime)
}

type server struct {
	listener    net.Listener
	connections []net.Conn
	client      *sarama.Client
	producer    *sarama.Producer
}

func (s *server) process(line []byte) {
	p := rfc3164.NewParser(line)
	if err := p.Parse(); err != nil {
		logger.Println("failed to parse:", err)
		return
	}

	parts := p.Dump()
	content := parts["content"].(string)

	if cfg.publish {
		if cfg.verbose {
			logger.Println("enqueuing", content)
		}

		err := s.producer.QueueMessage(cfg.topic,
			nil, sarama.StringEncoder(content))
		if err != nil {
			logger.Println("failed publishing", err)
		}
	}

	if cfg.verbose {
		logger.Println(content)
	}
}

func (s *server) handleConnection(conn net.Conn) {
	logger.Println("got connection from:", conn.RemoteAddr())
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		b := []byte(scanner.Text())
		logger.Printf("received %d bytes\n", len(b))
		s.process(b)
	}

	if err := scanner.Err(); err != nil {
		logger.Println("error reading from connection:", err)
	}
}

func (s *server) stop() {
	s.listener.Close()

	for _, conn := range s.connections {
		logger.Println("closing connection to", conn.RemoteAddr())
		conn.Close()
	}

	if cfg.publish {
		s.client.Close()
		s.producer.Close()
	}
}

func (s *server) start() error {
	logger.Printf("starting to listen on %d\n", cfg.port)

	if cfg.verbose {
		logger.Println("verbose is enabled, all messages will be printed to stderr")
	}

	// listen for inbound syslog messages over tcp
	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.port))
	if err != nil {
		return err
	}
	s.listener = listener

	if cfg.publish {
		// client for kafka
		brokers, err := LookupBrokers(cfg.zkstring)
		if err != nil {
			return err
		}

		brokerStr := make([]string, len(brokers))
		for i, b := range brokers {
			brokerStr[i] = fmt.Sprintf("%s:%d", b.Host, b.Port)
		}

		logger.Println("connecting to Kafka, using brokers from ZooKeeper:", brokerStr)
		client, err := sarama.NewClient("syslog", brokerStr, sarama.NewClientConfig())
		if err != nil {
			return err
		}
		s.client = client

		// producer for kafka
		producerConfig := sarama.NewProducerConfig()
		producerConfig.MaxBufferTime = cfg.flushTime

		producer, err := sarama.NewProducer(client, producerConfig)
		if err != nil {
			return err
		}
		s.producer = producer

		go func() {
			for err := range s.producer.Errors() {
				if err != nil {
					logger.Println(err)
				}
			}
		}()
	}

	logger.Println("waiting for syslog connection")
	for {
		// TODO
		// We get errors here when killing the app with Ctrl-C:
		// server.go:134: failed to accept connection: use of closed network connection
		conn, err := listener.Accept()
		if err != nil {
			logger.Println("failed to accept connection:", err)
			return err
		}
		s.connections = append(s.connections, conn)
		go s.handleConnection(conn)
	}
}

func handleInterrupt(s *server) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGUSR1)

	go func() {
		for sig := range signals {
			switch sig {
			case syscall.SIGINT:
				logger.Println("got kill signal, closing", len(s.connections), "connections")
				s.stop()
				os.Exit(0)
				break
			case syscall.SIGUSR1:
				// TODO: refresh/reload things?
				logger.Println("refreshing kafka endpoints?")
				break
			}
		}
	}()
}

func main() {
	flag.Parse()

	ft, err := time.ParseDuration(*flushTime)
	if err != nil {
		logger.Fatalln(err)
	}

	cfg = &config{
		port:      *port,
		verbose:   *verbose,
		publish:   *publish,
		topic:     *topic,
		zkstring:  *zkstring,
		flushTime: ft,
	}

	logger.Println("starting with config:", cfg)

	server := &server{
		connections: []net.Conn{},
	}

	handleInterrupt(server)

	if err := server.start(); err != nil {
		logger.Println("failed to listen:", err)
		os.Exit(1)
	}
}
<commit_msg>flush time<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/Shopify/sarama"
	rfc3164 "github.com/jeromer/syslogparser/rfc3164"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"
)

var (
	cfg       *config
	logger    = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile)
	port      = flag.Int("port", 514, "port on which to listen")
	verbose   = flag.Bool("verbose", false, "print all messages to stdout")
	publish   = flag.Bool("publish", false, "publish messages to kafka")
	topic     = flag.String("topic", "syslog", "kafka topic to publish on")
	zkstring  = flag.String("zkstring", "localhost:2181", "ZooKeeper broker connection string")
	flushTime = flag.String("flushTime", "10s", "max time before flushing messages to kafka, e.g. 1s, 2m")
)

type config struct {
	port      int
	verbose   bool
	publish   bool
	topic     string
	zkstring  string
	flushTime time.Duration
}

func (c *config) String() string {
	return fmt.Sprintf("port: %d, verbose: %t, publish: %t, topic: %s, zkstring: %s, flushTime: %s",
		c.port, c.verbose, c.publish, c.topic, c.zkstring, c.flushTime)
}

type server struct {
	listener    net.Listener
	connections []net.Conn
	client      *sarama.Client
	producer    *sarama.Producer
}

func (s *server) process(line []byte) {
	p := rfc3164.NewParser(line)
	if err := p.Parse(); err != nil {
		logger.Println("failed to parse:", err)
		return
	}

	parts := p.Dump()
	content := parts["content"].(string)

	if cfg.publish {
		if cfg.verbose {
			logger.Println("enqueuing", content)
		}

		err := s.producer.QueueMessage(cfg.topic,
			nil, sarama.StringEncoder(content))
		if err != nil {
			logger.Println("failed publishing", err)
		}
	}

	if cfg.verbose {
		logger.Println(content)
	}
}

func (s *server) handleConnection(conn net.Conn) {
	logger.Println("got connection from:", conn.RemoteAddr())
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		b := []byte(scanner.Text())
		logger.Printf("received %d bytes\n", len(b))
		s.process(b)
	}

	if err := scanner.Err(); err != nil {
		logger.Println("error reading from connection:", err)
	}
}

func (s *server) stop() {
	s.listener.Close()

	for _, conn := range s.connections {
		logger.Println("closing connection to", conn.RemoteAddr())
		conn.Close()
	}

	if cfg.publish {
		s.client.Close()
		s.producer.Close()
	}
}

func (s *server) start() error {
	logger.Printf("starting to listen on %d\n", cfg.port)

	if cfg.verbose {
		logger.Println("verbose is enabled, all messages will be printed to stderr")
	}

	// listen for inbound syslog messages over tcp
	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.port))
	if err != nil {
		return err
	}
	s.listener = listener

	if cfg.publish {
		// client for kafka
		brokers, err := LookupBrokers(cfg.zkstring)
		if err != nil {
			return err
		}

		brokerStr := make([]string, len(brokers))
		for i, b := range brokers {
			brokerStr[i] = fmt.Sprintf("%s:%d", b.Host, b.Port)
		}

		logger.Println("connecting to Kafka, using brokers from ZooKeeper:", brokerStr)
		client, err := sarama.NewClient("syslog", brokerStr, sarama.NewClientConfig())
		if err != nil {
			return err
		}
		s.client = client

		// producer for kafka
		producerConfig := sarama.NewProducerConfig()
		producerConfig.MaxBufferTime = cfg.flushTime

		producer, err := sarama.NewProducer(client, producerConfig)
		if err != nil {
			return err
		}
		s.producer = producer

		go func() {
			for err := range s.producer.Errors() {
				if err != nil {
					logger.Println(err)
				}
			}
		}()
	}

	logger.Println("waiting for syslog connection")
	for {
		// TODO
		// We get errors here when killing the app with Ctrl-C:
		// server.go:134: failed to accept connection: use of closed network connection
		conn, err := listener.Accept()
		if err != nil {
			logger.Println("failed to accept connection:", err)
			return err
		}
		s.connections = append(s.connections, conn)
		go s.handleConnection(conn)
	}
}

func handleInterrupt(s *server) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGUSR1)

	go func() {
		for sig := range signals {
			switch sig {
			case syscall.SIGINT:
				logger.Println("got kill signal, closing", len(s.connections), "connections")
				s.stop()
				os.Exit(0)
				break
			case syscall.SIGUSR1:
				// TODO: refresh/reload things?
				logger.Println("refreshing kafka endpoints?")
				break
			}
		}
	}()
}

func main() {
	flag.Parse()

	ft, err := time.ParseDuration(*flushTime)
	if err != nil {
		logger.Fatalln(err)
	}

	cfg = &config{
		port:      *port,
		verbose:   *verbose,
		publish:   *publish,
		topic:     *topic,
		zkstring:  *zkstring,
		flushTime: ft,
	}

	logger.Println("starting with config:", cfg)

	server := &server{
		connections: []net.Conn{},
	}

	handleInterrupt(server)

	if err := server.start(); err != nil {
		logger.Println("failed to listen:", err)
		os.Exit(1)
	}
}
<|endoftext|>


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

// Package orivil organizes all of the server components to be one runnable server,
// and also provides some useful methods.
package orivil

import (
	"gopkg.in/orivil/middle.v0"
	"gopkg.in/orivil/router.v0"
	"gopkg.in/orivil/service.v0"
	"gopkg.in/orivil/session.v0"
	"gopkg.in/orivil/view.v0"
	"gopkg.in/orivil/watcher.v0"
	"net/http"
	"path/filepath"
	"reflect"
	"strings"
	"time"
	"gopkg.in/orivil/grace.v0"
	"io"
	"os"
	"fmt"
	"strconv"
	"net/url"
	"bufio"
	"html/template"
	"gopkg.in/orivil/validator.v0"
)

const (
	VERSION = "v2.0"
)

const (
	SvcApp = "orivil.App"
	SvcServer = "orivil.Server"
)

var (
	// the unique key for server, Orivil will read the value from config file "app.yml"
	Key string

	// import these packages for downloading them
	_ watcher.AutoCommand

	_ validator.Validator
)

type FileHandler interface {
	// HandleFile to check if handle the url as static file
	HandleFile(*http.Request) bool
	// ServeFile for serve static file
	ServeFile(w http.ResponseWriter, r *http.Request, fileName string)
}

type NotFoundHandler interface {
	NotFound(w http.ResponseWriter, r *http.Request)
}

type Server struct {
	SContainer      *service.Container
	MContainer      *middle.Container
	RContainer      *router.Container
	MiddleBag       *middle.Bag
	VContainer      *view.Container
	registers       []Register
	fileHandler     FileHandler
	notFoundHandler NotFoundHandler
	*grace.GraceServer
}

func NewServer(addr string) *Server {
	readTimeOut := time.Second * time.Duration(CfgApp.READ_TIMEOUT)
	writeTimeOut := time.Second * time.Duration(CfgApp.WRITE_TIMEOUT)
	httpServer := &http.Server{
		Addr: addr,
		ReadTimeout: readTimeOut,
		WriteTimeout: writeTimeOut,
	}
	graceServer := grace.NewGraceServer(httpServer)

	// public service container, for store "service providers"
	sContainer := service.NewPublicContainer()

	// middleware bag for config middlewares and match middlewares
	middleBag := middle.NewMiddlewareBag()

	// middleware container dependent on service container, for store
	// middlewares to service container
	mContainer := middle.NewContainer(middleBag, sContainer)

	// view combiner
	combiner := view.NewContainer(CfgApp.DEBUG, CfgApp.VIEW_FILE_EXT)

	// filtering register controller actions to router
	routeFilter := NewRouteFilter()

	// filtering register controller extends methods to router
	routeFilter.AddStructs([]interface{}{
		&App{},
	})

	// filtering register actions to router
	routeFilter.AddActions([]string{
		"SetMiddle",
	})

	// route container collect all of the controller comments, and add
	// them to the router if possible
	rContainer := router.NewContainer(DirBundle, routeFilter.FilterAction)

	server := &Server{
		SContainer: sContainer,
		MiddleBag:  middleBag,
		MContainer: mContainer,
		RContainer: rContainer,
		VContainer: combiner,
		GraceServer: graceServer,
	}

	server.Handler = server

	// set default not found handler
	server.notFoundHandler = &defaultNotFoundHandler{}

	// set default static file server handler
	server.fileHandler = &defaultFileHandler{}

	// register base service
	server.RegisterBundle(
		new(BaseRegister),
	)
	return server
}

func (s *Server) ListenAndServe() error {

	s.init()

	// if the server was graceful stopped, the error will be nil.
	err := s.GraceServer.ListenAndServe()
	s.close()
	return err
}

func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {

	s.init()

	err := s.GraceServer.ListenAndServeTLS(certFile, keyFile)
	s.close()
	return err
}

// SetNotFoundHandler sets the 404 not found handler.
func (s *Server) SetNotFoundHandler(h NotFoundHandler) {
	s.notFoundHandler = h
}

// SetFileHandler sets the static file handler.
func (s *Server) SetFileHandler(h FileHandler) {
	s.fileHandler = h
}

// ServeHTTP serves the incoming http request, every request goes through the function,
// including static file requests.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	start := time.Now()
	path := r.URL.Path
	// handle static file
	if s.fileHandler.HandleFile(r) {
		s.serveFile(w, r, path)
	} else {

		var app *App
		defer func() {
			err := recover()
			if app != nil {
				s.storeSession(app)
				for _, f := range app.defers {
					f()
				}
			}
			if err, ok := err.(error); ok {
				handleError(w, r, app, err)
			} else if err != nil {
				panic(err)
			}
		}()

		path = r.Method + path

		// match route
		if action, params, controller, ok := s.RContainer.Match(path); !ok {

			s.notFoundHandler.NotFound(w, r)
		} else {
			// new private container
			privateContainer := service.NewPrivateContainer(s.SContainer)

			// new app
			app = &App{
				Params:    params,
				Action:    action,
				Response:  w,
				Request:   r,
				Container: privateContainer,
				VContainer: s.VContainer,
				Server: s,
				data:  make(map[string]interface{}, 1),
				Start: start,
			}

			// cache the orivil.App and orivil.Server to private container.
			app.AddCache(SvcApp, app)
			app.AddCache(SvcServer, s)

			// match middleware
			middleNames := s.MContainer.Get(action)
			middles := make([]interface{}, len(middleNames))

			// get middleware instances from private container
			for index, service := range middleNames {
				middles[index] = privateContainer.Get(service)
			}

			// call middleware
			s.callMiddles(middles, app)

			// call controller action
			value := reflect.ValueOf(controller())
			s.setControllerDependence(value, app)
			method := action[strings.LastIndex(action, ".") + 1:]
			actionFun, _ := value.Type().MethodByName(method)
			actionFun.Func.Call([]reflect.Value{value})

			// call "Terminate" middleware
			s.callMiddlesTerminate(middles, app)

			// send view file or api data
			app.flash()
		}
	}
}

func (s *Server) serveFile(w http.ResponseWriter, r *http.Request, urlPath string) {
	var filename string
	if CfgApp.DEBUG {
		q := r.URL.Query()
		debug := q.Get("debug")
		if debug == "true" {
			line, err := strconv.Atoi(q.Get("line"))
			if err != nil {
				panic(err)
			}
			filename, err := url.QueryUnescape(urlPath)
			if err != nil {
				panic(err)
			}
			filename = filename[1:]
			file, err := os.Open(filename)
			if err != nil {
				panic(err)
			}
			var lines []string
			scanner := bufio.NewScanner(file)
			for scanner.Scan() {
				lines = append(lines, scanner.Text())
			}
			if err := scanner.Err(); err != nil {
				panic(err)
			}
			tpl := template.New("debug")
			tpl.Funcs(template.FuncMap{
				"add": func(a, b int) int {
					return a + b
				},
			})
			_, err = tpl.Parse(debugFileTemplate)
			if err != nil {
				panic(err)
			}
			err = tpl.Execute(w, map[string]interface{}{
				"lines": lines,
				"line": line,
			})
			if err != nil {
				panic(err)
			}
			return
		}
	}

	if strings.HasPrefix(urlPath, "/bundle-") {
		str := strings.TrimPrefix(urlPath, "/bundle-")
		firstIdx := strings.Index(str, "/")
		var bundle string
		if firstIdx > 1 && firstIdx < len(str) {
			bundle = str[:firstIdx]
			urlPath = str[firstIdx:]
		}
		filename = filepath.Join(DirBundle, bundle, "public", urlPath)
	} else {
		filename = filepath.Join(DirStaticFile, urlPath)
	}
	s.fileHandler.ServeFile(w, r, filename)
}

func (s *Server) storeSession(a *App) {
	// if permanent session service was used, store it
	if s, ok := a.GetCache(SvcPermanentSession).(*session.Session); ok {

		session.StorePermanentSession(s)
	}
}

func (s *Server) setControllerDependence(controller reflect.Value, app *App) {
	v := controller.Elem()
	len := v.NumField()
	for i := 0; i < len; i++ {
		fi := v.Field(i)
		if fi.CanSet() && fi.Type().String() == "*orivil.App" {
			fi.Set(reflect.ValueOf(app))
			break
		}
	}
}

func (s *Server) callMiddles(middles []interface{}, app *App) {
	for _, middle := range middles {
		switch mid := middle.(type) {

		case RequestHandler:

			mid.Handle(app)
		case func(*App):

			mid(app)
		case TerminateHandler:
		default:
			panic(fmt.Errorf("unkown middleware type: %v", reflect.TypeOf(middle)))
		}
	}
}

func (s *Server) callMiddlesTerminate(middles []interface{}, app *App) {
	for _, middle := range middles {
		if h, ok := middle.(TerminateHandler); ok {
			h.Terminate(app)
		}
	}
}

func (s *Server) Version() string {

	return VERSION
}

// PrintInfo prints the server information to os.Stdout.
func (s *Server) PrintInfo() {

	s.PrintInfoAt(os.Stdout)
}

// PrintInfoAt prints the server information to the param w
func (s *Server) PrintInfoAt(w io.Writer) {
	routeMsg := router.GetAllRouteMsg(s.RContainer)
	fmt.Fprintf(w, "\n[routes]:\n")
	for _, msg := range routeMsg {
		fmt.Fprintln(w, msg)
	}

	actions := s.RContainer.GetActions()
	middleMsg := middle.GetMiddlesMsg(s.MContainer, actions)
	fmt.Fprintf(w, "\n[middlewares]:\n")
	for _, msg := range middleMsg {
		fmt.Fprintln(w, msg)
	}
}

// RegisterBundle collects all bundle registers
func (s *Server) RegisterBundle(r ...Register) {
	s.registers = append(s.registers, r...)
}

// Initialize all bundles
func (s *Server) init() {

	// register services
	for _, r := range s.registers {
		r.RegService(s.SContainer)
	}

	// register routes
	for _, r := range s.registers {
		r.RegRoute(s.RContainer)
	}

	// register middleware
	for _, r := range s.registers {
		r.RegMiddle(s.MContainer)
	}

	allActions := s.RContainer.GetActions()
	for bundle, controllers := range allActions {
		for controller, actions := range controllers {
			s.MiddleBag.AddController(bundle, controller, actions)
		}
	}

	// config middleware
	for _, r := range s.registers {

		bundle := filepath.Base(reflect.TypeOf(r).Elem().PkgPath())
		s.MiddleBag.SetCurrent(bundle, "")
		r.CfgMiddle(s.MiddleBag)
	}

	cProviders := s.RContainer.GetControllers()
	for bundle, controllers := range allActions {
		for controller, _ := range controllers {
			c := cProviders[bundle + "." + controller]()
			s.MiddleBag.SetCurrent(bundle, controller)
			if r, ok := c.(MiddlewareConfigure); ok {
				r.CfgMiddle(s.MiddleBag)
			}
		}
	}

	// boot services
	for _, r := range s.registers {
		r.Boot(s)
	}
}

func (s *Server) close() {
	for _, r := range s.registers {
		r.Close()
	}
}

// defaultFileHandler implements "FileHandler" interface for handling static files.
type defaultFileHandler struct{}

// HandleFile checks whether or not to handle the request as static file request.
func (s *defaultFileHandler) HandleFile(r *http.Request) bool {
	return filepath.Ext(r.URL.Path) != ""
}

// ServeFile serves static file
func (s *defaultFileHandler) ServeFile(w http.ResponseWriter, r *http.Request, name string) {
	http.ServeFile(w, r, name)
}

// implements NotFoundHandler interface
type defaultNotFoundHandler struct{}

func (h *defaultNotFoundHandler) NotFound(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("X-Content-Type-Options", "nosniff")
	w.WriteHeader(404)
	w.Write(notFoundPage)
}
<commit_msg>add some pkgs<commit_after>// Copyright 2016 orivil Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package orivil organizes all of the server components to be one runnable server,
// and also provides some useful methods.
package orivil

import (
	"gopkg.in/orivil/middle.v0"
	"gopkg.in/orivil/router.v0"
	"gopkg.in/orivil/service.v0"
	"gopkg.in/orivil/session.v0"
	"gopkg.in/orivil/view.v0"
	"net/http"
	"path/filepath"
	"reflect"
	"strings"
	"time"
	"gopkg.in/orivil/grace.v0"
	"io"
	"os"
	"fmt"
	"strconv"
	"net/url"
	"bufio"
	"html/template"

	// import these packages for downloading them
	_ "gopkg.in/orivil/xsrftoken.v0"
	_ "gopkg.in/orivil/validator.v0"
	_ "gopkg.in/orivil/watcher.v0"
)

const (
	VERSION = "v2.0"
)

const (
	SvcApp = "orivil.App"
	SvcServer = "orivil.Server"
)

var (
	// the unique key for server, Orivil will read the value from config file "app.yml"
	Key string
)

type FileHandler interface {
	// HandleFile to check if handle the url as static file
	HandleFile(*http.Request) bool
	// ServeFile for serve static file
	ServeFile(w http.ResponseWriter, r *http.Request, fileName string)
}

type NotFoundHandler interface {
	NotFound(w http.ResponseWriter, r *http.Request)
}

type Server struct {
	SContainer      *service.Container
	MContainer      *middle.Container
	RContainer      *router.Container
	MiddleBag       *middle.Bag
	VContainer      *view.Container
	registers       []Register
	fileHandler     FileHandler
	notFoundHandler NotFoundHandler
	*grace.GraceServer
}

func NewServer(addr string) *Server {
	readTimeOut := time.Second * time.Duration(CfgApp.READ_TIMEOUT)
	writeTimeOut := time.Second * time.Duration(CfgApp.WRITE_TIMEOUT)
	httpServer := &http.Server{
		Addr: addr,
		ReadTimeout: readTimeOut,
		WriteTimeout: writeTimeOut,
	}
	graceServer := grace.NewGraceServer(httpServer)

	// public service container, for store "service providers"
	sContainer := service.NewPublicContainer()

	// middleware bag for config middlewares and match middlewares
	middleBag := middle.NewMiddlewareBag()

	// middleware container dependent on service container, for store
	// middlewares to service container
	mContainer := middle.NewContainer(middleBag, sContainer)

	// view combiner
	combiner := view.NewContainer(CfgApp.DEBUG, CfgApp.VIEW_FILE_EXT)

	// filtering register controller actions to router
	routeFilter := NewRouteFilter()

	// filtering register controller extends methods to router
	routeFilter.AddStructs([]interface{}{
		&App{},
	})

	// filtering register actions to router
	routeFilter.AddActions([]string{
		"SetMiddle",
	})

	// route container collect all of the controller comments, and add
	// them to the router if possible
	rContainer := router.NewContainer(DirBundle, routeFilter.FilterAction)

	server := &Server{
		SContainer: sContainer,
		MiddleBag:  middleBag,
		MContainer: mContainer,
		RContainer: rContainer,
		VContainer: combiner,
		GraceServer: graceServer,
	}

	server.Handler = server

	// set default not found handler
	server.notFoundHandler = &defaultNotFoundHandler{}

	// set default static file server handler
	server.fileHandler = &defaultFileHandler{}

	// register base service
	server.RegisterBundle(
		new(BaseRegister),
	)
	return server
}

func (s *Server) ListenAndServe() error {

	s.init()

	// if the server was graceful stopped, the error will be nil.
	err := s.GraceServer.ListenAndServe()
	s.close()
	return err
}

func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {

	s.init()

	err := s.GraceServer.ListenAndServeTLS(certFile, keyFile)
	s.close()
	return err
}

// SetNotFoundHandler sets the 404 not found handler.
func (s *Server) SetNotFoundHandler(h NotFoundHandler) {
	s.notFoundHandler = h
}

// SetFileHandler sets the static file handler.
func (s *Server) SetFileHandler(h FileHandler) {
	s.fileHandler = h
}

// ServeHTTP serves the incoming http request, every request goes through the function,
// including static file requests.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	start := time.Now()
	path := r.URL.Path
	// handle static file
	if s.fileHandler.HandleFile(r) {
		s.serveFile(w, r, path)
	} else {

		var app *App
		defer func() {
			err := recover()
			if app != nil {
				s.storeSession(app)
				for _, f := range app.defers {
					f()
				}
			}
			if err, ok := err.(error); ok {
				handleError(w, r, app, err)
			} else if err != nil {
				panic(err)
			}
		}()

		path = r.Method + path

		// match route
		if action, params, controller, ok := s.RContainer.Match(path); !ok {

			s.notFoundHandler.NotFound(w, r)
		} else {
			// new private container
			privateContainer := service.NewPrivateContainer(s.SContainer)

			// new app
			app = &App{
				Params:    params,
				Action:    action,
				Response:  w,
				Request:   r,
				Container: privateContainer,
				VContainer: s.VContainer,
				Server: s,
				data:  make(map[string]interface{}, 1),
				Start: start,
			}

			// cache the orivil.App and orivil.Server to private container.
			app.AddCache(SvcApp, app)
			app.AddCache(SvcServer, s)

			// match middleware
			middleNames := s.MContainer.Get(action)
			middles := make([]interface{}, len(middleNames))

			// get middleware instances from private container
			for index, service := range middleNames {
				middles[index] = privateContainer.Get(service)
			}

			// call middleware
			s.callMiddles(middles, app)

			// call controller action
			value := reflect.ValueOf(controller())
			s.setControllerDependence(value, app)
			method := action[strings.LastIndex(action, ".") + 1:]
			actionFun, _ := value.Type().MethodByName(method)
			actionFun.Func.Call([]reflect.Value{value})

			// call "Terminate" middleware
			s.callMiddlesTerminate(middles, app)

			// send view file or api data
			app.flash()
		}
	}
}

func (s *Server) serveFile(w http.ResponseWriter, r *http.Request, urlPath string) {
	var filename string
	if CfgApp.DEBUG {
		q := r.URL.Query()
		debug := q.Get("debug")
		if debug == "true" {
			line, err := strconv.Atoi(q.Get("line"))
			if err != nil {
				panic(err)
			}
			filename, err := url.QueryUnescape(urlPath)
			if err != nil {
				panic(err)
			}
			filename = filename[1:]
			file, err := os.Open(filename)
			if err != nil {
				panic(err)
			}
			var lines []string
			scanner := bufio.NewScanner(file)
			for scanner.Scan() {
				lines = append(lines, scanner.Text())
			}
			if err := scanner.Err(); err != nil {
				panic(err)
			}
			tpl := template.New("debug")
			tpl.Funcs(template.FuncMap{
				"add": func(a, b int) int {
					return a + b
				},
			})
			_, err = tpl.Parse(debugFileTemplate)
			if err != nil {
				panic(err)
			}
			err = tpl.Execute(w, map[string]interface{}{
				"lines": lines,
				"line": line,
			})
			if err != nil {
				panic(err)
			}
			return
		}
	}

	if strings.HasPrefix(urlPath, "/bundle-") {
		str := strings.TrimPrefix(urlPath, "/bundle-")
		firstIdx := strings.Index(str, "/")
		var bundle string
		if firstIdx > 1 && firstIdx < len(str) {
			bundle = str[:firstIdx]
			urlPath = str[firstIdx:]
		}
		filename = filepath.Join(DirBundle, bundle, "public", urlPath)
	} else {
		filename = filepath.Join(DirStaticFile, urlPath)
	}
	s.fileHandler.ServeFile(w, r, filename)
}

func (s *Server) storeSession(a *App) {
	// if permanent session service was used, store it
	if s, ok := a.GetCache(SvcPermanentSession).(*session.Session); ok {

		session.StorePermanentSession(s)
	}
}

func (s *Server) setControllerDependence(controller reflect.Value, app *App) {
	v := controller.Elem()
	len := v.NumField()
	for i := 0; i < len; i++ {
		fi := v.Field(i)
		if fi.CanSet() && fi.Type().String() == "*orivil.App" {
			fi.Set(reflect.ValueOf(app))
			break
		}
	}
}

func (s *Server) callMiddles(middles []interface{}, app *App) {
	for _, middle := range middles {
		switch mid := middle.(type) {

		case RequestHandler:

			mid.Handle(app)
		case func(*App):

			mid(app)
		case TerminateHandler:
		default:
			panic(fmt.Errorf("unkown middleware type: %v", reflect.TypeOf(middle)))
		}
	}
}

func (s *Server) callMiddlesTerminate(middles []interface{}, app *App) {
	for _, middle := range middles {
		if h, ok := middle.(TerminateHandler); ok {
			h.Terminate(app)
		}
	}
}

func (s *Server) Version() string {

	return VERSION
}

// PrintInfo prints the server information to os.Stdout.
func (s *Server) PrintInfo() {

	s.PrintInfoAt(os.Stdout)
}

// PrintInfoAt prints the server information to the param w
func (s *Server) PrintInfoAt(w io.Writer) {
	routeMsg := router.GetAllRouteMsg(s.RContainer)
	fmt.Fprintf(w, "\n[routes]:\n")
	for _, msg := range routeMsg {
		fmt.Fprintln(w, msg)
	}

	actions := s.RContainer.GetActions()
	middleMsg := middle.GetMiddlesMsg(s.MContainer, actions)
	fmt.Fprintf(w, "\n[middlewares]:\n")
	for _, msg := range middleMsg {
		fmt.Fprintln(w, msg)
	}
}

// RegisterBundle collects all bundle registers
func (s *Server) RegisterBundle(r ...Register) {
	s.registers = append(s.registers, r...)
}

// Initialize all bundles
func (s *Server) init() {

	// register services
	for _, r := range s.registers {
		r.RegService(s.SContainer)
	}

	// register routes
	for _, r := range s.registers {
		r.RegRoute(s.RContainer)
	}

	// register middleware
	for _, r := range s.registers {
		r.RegMiddle(s.MContainer)
	}

	allActions := s.RContainer.GetActions()
	for bundle, controllers := range allActions {
		for controller, actions := range controllers {
			s.MiddleBag.AddController(bundle, controller, actions)
		}
	}

	// config middleware
	for _, r := range s.registers {

		bundle := filepath.Base(reflect.TypeOf(r).Elem().PkgPath())
		s.MiddleBag.SetCurrent(bundle, "")
		r.CfgMiddle(s.MiddleBag)
	}

	cProviders := s.RContainer.GetControllers()
	for bundle, controllers := range allActions {
		for controller, _ := range controllers {
			c := cProviders[bundle + "." + controller]()
			s.MiddleBag.SetCurrent(bundle, controller)
			if r, ok := c.(MiddlewareConfigure); ok {
				r.CfgMiddle(s.MiddleBag)
			}
		}
	}

	// boot services
	for _, r := range s.registers {
		r.Boot(s)
	}
}

func (s *Server) close() {
	for _, r := range s.registers {
		r.Close()
	}
}

// defaultFileHandler implements "FileHandler" interface for handling static files.
type defaultFileHandler struct{}

// HandleFile checks whether or not to handle the request as static file request.
func (s *defaultFileHandler) HandleFile(r *http.Request) bool {
	return filepath.Ext(r.URL.Path) != ""
}

// ServeFile serves static file
func (s *defaultFileHandler) ServeFile(w http.ResponseWriter, r *http.Request, name string) {
	http.ServeFile(w, r, name)
}

// implements NotFoundHandler interface
type defaultNotFoundHandler struct{}

func (h *defaultNotFoundHandler) NotFound(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("X-Content-Type-Options", "nosniff")
	w.WriteHeader(404)
	w.Write(notFoundPage)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/speps/go-hashids"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const DEFAULT_RESPONSE = `{"goslow": "response"}`
const MAX_DELAY = 99
const MIN_STATUS_CODE = 100
const MAX_STATUS_CODE = 599

const CREATE_SUBDOMAIN_NAME = "create"
const ADD_RULE_SUBDOMAIN_PREFIX = "admin-"
const BUG_REPORTS_EMAIL = "codumentary.com@gmail.com"

var REDIRECT_STATUSES = map[int]bool{301: true, 302: true}
var EMPTY_HEADERS = map[string]string{}

const MAX_GENERATE_SITE_NAME_ATTEMPTS = 5
const DURATION_BETWEEN_GENERATE_SITE_NAME_ATTEMPTS = time.Duration(10) * time.Millisecond

type Server struct {
	config  *Config
	storage *Storage
	hasher  *hashids.HashID
}

type TemplateData struct {
	*Rule
	Domain string
}

func NewServer(config *Config) *Server {
	storage, err := NewStorage(config.driver, config.dataSource)
	if err != nil {
		log.Fatal(err)
	}

	server := &Server{config: config, storage: storage,
		hasher: newHasher(config.siteSalt, config.minSiteLength),
	}
	if config.createDefaultRules {
		server.createDefaultRules()
	}
	return server
}

func newHasher(salt string, minLength int) *hashids.HashID {
	hd := hashids.NewData()
	hd.Salt = salt
	hd.MinLength = minLength
	hd.Alphabet = "abcdefghijklmnopqrstuvwxyz1234567890"
	return hashids.NewWithData(hd)
}

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	log.Printf("%s %s", req.Method, req.URL.Path)
	switch {
	case server.isOptions(req):
		allowCrossDomainRequests(w, req)

	case server.isCreateSite(req):
		server.handleCreateSite(w, req)

	case server.isAddRule(req):
		server.handleAddRule(w, req)

	default:
		allowCrossDomainRequests(w, req)
		server.respondFromRule(w, req)
	}
}

func (server *Server) isOptions(req *http.Request) bool {
	return req.Method == "OPTIONS"
}

// TODO: check crossbrowser compatibility
func allowCrossDomainRequests(w http.ResponseWriter, req *http.Request) {
	header := w.Header()
	header.Set("Access-Control-Allow-Origin", "*")
	header.Set("Access-Control-Allow-Credentials", "true")
	header["Access-Control-Allow-Headers"] = req.Header["Access-Control-Request-Headers"]
}

func (server *Server) isCreateSite(req *http.Request) bool {
	if req.Method != "POST" {
		return false
	}

	if server.isInSingleSiteMode() {
		return false
	}
	return getSubdomain(req.Host) == CREATE_SUBDOMAIN_NAME
}

func (server *Server) isInSingleSiteMode() bool {
	return server.config.singleDomainUrlPath != ""
}

func getSubdomain(url string) string {
	return strings.Split(url, ".")[0]
}

func (server *Server) handleCreateSite(w http.ResponseWriter, req *http.Request) {
	rule, err := server.createSite(req)
	if err != nil {
		server.handleError(err, w)
		return
	}
	if req.FormValue("output") == "short" {
		fmt.Fprintf(w, "%s.%s", rule.Site, server.config.endpoint)
		return
	}
	CREATE_SITE_TEMPLATE.Execute(w, server.makeTemplateData(rule))
	io.WriteString(w, "\n")
	ADD_RULE_TEMPLATE.Execute(w, server.makeTemplateData(rule))
}

func (server *Server) createSite(req *http.Request) (*Rule, error) {
	site, err := server.generateUniqueSiteName(MAX_GENERATE_SITE_NAME_ATTEMPTS)
	if err != nil {
		return nil, err
	}
	rule, err := server.addRule(site, req)
	return rule, err
}

func (server *Server) generateUniqueSiteName(maxAttempts uint) (string, error) {
	for ; maxAttempts > 0; maxAttempts-- {
		site, err := server.makeSiteNameFrom(generateUniqueNumbers())
		if err != nil {
			break
		}
		err = server.storage.CreateSite(site)
		if err == nil {
			return site, nil
		}
		time.Sleep(DURATION_BETWEEN_GENERATE_SITE_NAME_ATTEMPTS)
	}
	return "", errors.New(fmt.Sprintf(`Can't create site.
Try again in a few seconds or contact %s for help`, BUG_REPORTS_EMAIL))
}

func (server *Server) makeSiteNameFrom(numbers []int) (string, error) {
	return server.hasher.Encode(numbers)
}

func generateUniqueNumbers() []int {
	utc := time.Now().UTC()
	seconds := int(utc.Unix()) // TODO: fix me at 2037-12-31
	milliseconds := (utc.Nanosecond() / 1000000) % 1000
	return []int{seconds, milliseconds}
}

func (server *Server) addRule(site string, req *http.Request) (*Rule, error) {
	rule, err := server.makeRule(site, req)
	if err != nil {
		return nil, err
	}
	return rule, server.storage.UpsertRule(rule)
}

func (server *Server) makeRule(site string, req *http.Request) (*Rule, error) {
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, err
	}

	values, err := url.ParseQuery(req.URL.RawQuery)
	if err != nil {
		return nil, err
	}
	path := server.getPath(req)
	delay, err := getDelay(values)
	if err != nil {
		return nil, err
	}
	return &Rule{Site: site, StatusCode: http.StatusOK, Headers: EMPTY_HEADERS,
		Path: path, Method: values.Get("method"),
		Body: string(body), Delay: delay}, nil
}

func getDelay(values url.Values) (time.Duration, error) {
	_, contains := values["delay"]
	if !contains {
		return time.Duration(0), nil
	}
	delayInSeconds, err := strconv.ParseFloat(values.Get("delay"), 64)
	if err != nil {
		return time.Duration(0), err
	}
	return time.Duration(delayInSeconds*1000) * time.Millisecond, nil
}

func (server *Server) handleError(err error, w http.ResponseWriter) {
	log.Print(err)
	http.Error(w, fmt.Sprintf("Internal error: %s. For real", err), http.StatusInternalServerError)
}

func (server *Server) makeTemplateData(rule *Rule) *TemplateData {
	var domain string
	if server.isInSingleSiteMode() {
		domain = server.config.endpoint
	} else {
		domain = fmt.Sprintf("%s.%s", rule.Site, server.config.endpoint)
	}
	return &TemplateData{Rule: rule, Domain: domain}
}

func (server *Server) respondFromRule(w http.ResponseWriter, req *http.Request) {
	rule, found, err := server.storage.FindRuleMatching(server.getSite(req), req)
	if err != nil {
		server.handleError(err, w)
		return
	}

	if found {
		ApplyRule(rule, w)
	} else {
		http.Error(w, "No rule. For real.", http.StatusNotFound)
	}
}

func (server *Server) isAddRule(r *http.Request) bool {
	if r.Method != "POST" {
		return false
	}
	if server.isInSingleSiteMode() {
		return server.isSpecialPath(r.URL.Path)
	}
	return strings.HasPrefix(getSubdomain(r.Host), ADD_RULE_SUBDOMAIN_PREFIX)
}

func (server *Server) isSpecialPath(path string) bool {
	specialPath := server.config.singleDomainUrlPath
	if !strings.HasPrefix(path, specialPath) {
		return false
	}
	if strings.HasSuffix(specialPath, "/") {
		return true
	}
	suffix := strings.TrimPrefix(path, specialPath)
	return suffix == "" || suffix[0] == '?' || suffix[0] == '/'
}

func (server *Server) handleAddRule(w http.ResponseWriter, req *http.Request) {
	rule, err := server.addRule(server.getSite(req), req)
	if err != nil {
		server.handleError(err, w)
		return
	}
	ADD_RULE_TEMPLATE.Execute(w, server.makeTemplateData(rule))
}

func (server *Server) getSite(req *http.Request) string {
	if server.isInSingleSiteMode() {
		return ""
	}
	subdomain := getSubdomain(req.Host)
	if server.isAddRule(req) {
		return strings.TrimPrefix(subdomain, ADD_RULE_SUBDOMAIN_PREFIX)
	}
	return subdomain
}

func (server *Server) getPath(req *http.Request) string {
	if server.isInSingleSiteMode() {
		path := strings.TrimPrefix(req.URL.Path, server.config.singleDomainUrlPath)
		return ensureHasPrefix(path, "/")
	}
	return req.URL.Path
}

func ensureHasPrefix(s, prefix string) string {
	if !strings.HasPrefix(s, prefix) {
		return prefix + s
	}
	return s
}

func ApplyRule(rule *Rule, w http.ResponseWriter) {
	time.Sleep(rule.Delay)

	addHeaders(rule.Headers, w)
	w.WriteHeader(rule.StatusCode)
	io.WriteString(w, rule.Body)
}

func addHeaders(headers map[string]string, w http.ResponseWriter) {
	responseHeader := w.Header()
	for key, value := range headers {
		responseHeader.Add(key, value)
	}
}

func (server *Server) createDefaultRules() {
	server.createDelayRules()
	server.createStatusRules()
}

func (server *Server) createDelayRules() {
	for i := 0; i <= MAX_DELAY; i++ {
		delaySite := strconv.Itoa(i)
		delay := time.Duration(i) * time.Second

		server.storage.UpsertRule(&Rule{Site: delaySite, Headers: EMPTY_HEADERS, Delay: delay,
			StatusCode: http.StatusOK, Body: DEFAULT_RESPONSE,
		})
	}
}

func (server *Server) createStatusRules() {
	for i := MIN_STATUS_CODE; i <= MAX_STATUS_CODE; i++ {
		statusSite := strconv.Itoa(i)
		headers := server.headersForStatus(i)
		server.storage.UpsertRule(&Rule{Site: statusSite, StatusCode: i,
			Headers: headers, Body: DEFAULT_RESPONSE})
	}
}

func (server *Server) headersForStatus(status int) map[string]string {
	_, isRedirect := REDIRECT_STATUSES[status]
	if isRedirect {
		// TODO: check that protocol-independent location is legal HTTP
		// TODO: header should respect current port
		host := fmt.Sprintf("//0.goslow.link")
		return map[string]string{"Location": host}
	}
	return EMPTY_HEADERS
}

func (server *Server) ListenAndServe() error {
	log.Printf("listening on %s", server.config.listenOn)
	return http.ListenAndServe(server.config.listenOn, server)
}
<commit_msg>Group constants<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/speps/go-hashids"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const DEFAULT_RESPONSE = `{"goslow": "response"}`

const (
	MAX_DELAY       = 99
	MIN_STATUS_CODE = 100
	MAX_STATUS_CODE = 599
)

const (
	CREATE_SUBDOMAIN_NAME     = "create"
	ADD_RULE_SUBDOMAIN_PREFIX = "admin-"
)

const BUG_REPORTS_EMAIL = "codumentary.com@gmail.com"

var REDIRECT_STATUSES = map[int]bool{301: true, 302: true}
var EMPTY_HEADERS = map[string]string{}

const (
	MAX_GENERATE_SITE_NAME_ATTEMPTS              = 5
	DURATION_BETWEEN_GENERATE_SITE_NAME_ATTEMPTS = time.Duration(10) * time.Millisecond
)

type Server struct {
	config  *Config
	storage *Storage
	hasher  *hashids.HashID
}

type TemplateData struct {
	*Rule
	Domain string
}

func NewServer(config *Config) *Server {
	storage, err := NewStorage(config.driver, config.dataSource)
	if err != nil {
		log.Fatal(err)
	}

	server := &Server{config: config, storage: storage,
		hasher: newHasher(config.siteSalt, config.minSiteLength),
	}
	if config.createDefaultRules {
		server.createDefaultRules()
	}
	return server
}

func newHasher(salt string, minLength int) *hashids.HashID {
	hd := hashids.NewData()
	hd.Salt = salt
	hd.MinLength = minLength
	hd.Alphabet = "abcdefghijklmnopqrstuvwxyz1234567890"
	return hashids.NewWithData(hd)
}

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	log.Printf("%s %s", req.Method, req.URL.Path)
	switch {
	case server.isOptions(req):
		allowCrossDomainRequests(w, req)

	case server.isCreateSite(req):
		server.handleCreateSite(w, req)

	case server.isAddRule(req):
		server.handleAddRule(w, req)

	default:
		allowCrossDomainRequests(w, req)
		server.respondFromRule(w, req)
	}
}

func (server *Server) isOptions(req *http.Request) bool {
	return req.Method == "OPTIONS"
}

// TODO: check crossbrowser compatibility
func allowCrossDomainRequests(w http.ResponseWriter, req *http.Request) {
	header := w.Header()
	header.Set("Access-Control-Allow-Origin", "*")
	header.Set("Access-Control-Allow-Credentials", "true")
	header["Access-Control-Allow-Headers"] = req.Header["Access-Control-Request-Headers"]
}

func (server *Server) isCreateSite(req *http.Request) bool {
	if req.Method != "POST" {
		return false
	}

	if server.isInSingleSiteMode() {
		return false
	}
	return getSubdomain(req.Host) == CREATE_SUBDOMAIN_NAME
}

func (server *Server) isInSingleSiteMode() bool {
	return server.config.singleDomainUrlPath != ""
}

func getSubdomain(url string) string {
	return strings.Split(url, ".")[0]
}

func (server *Server) handleCreateSite(w http.ResponseWriter, req *http.Request) {
	rule, err := server.createSite(req)
	if err != nil {
		server.handleError(err, w)
		return
	}
	if req.FormValue("output") == "short" {
		fmt.Fprintf(w, "%s.%s", rule.Site, server.config.endpoint)
		return
	}
	CREATE_SITE_TEMPLATE.Execute(w, server.makeTemplateData(rule))
	io.WriteString(w, "\n")
	ADD_RULE_TEMPLATE.Execute(w, server.makeTemplateData(rule))
}

func (server *Server) createSite(req *http.Request) (*Rule, error) {
	site, err := server.generateUniqueSiteName(MAX_GENERATE_SITE_NAME_ATTEMPTS)
	if err != nil {
		return nil, err
	}
	rule, err := server.addRule(site, req)
	return rule, err
}

func (server *Server) generateUniqueSiteName(maxAttempts uint) (string, error) {
	for ; maxAttempts > 0; maxAttempts-- {
		site, err := server.makeSiteNameFrom(generateUniqueNumbers())
		if err != nil {
			break
		}
		err = server.storage.CreateSite(site)
		if err == nil {
			return site, nil
		}
		time.Sleep(DURATION_BETWEEN_GENERATE_SITE_NAME_ATTEMPTS)
	}
	return "", errors.New(fmt.Sprintf(`Can't create site.
Try again in a few seconds or contact %s for help`, BUG_REPORTS_EMAIL))
}

func (server *Server) makeSiteNameFrom(numbers []int) (string, error) {
	return server.hasher.Encode(numbers)
}

func generateUniqueNumbers() []int {
	utc := time.Now().UTC()
	seconds := int(utc.Unix()) // TODO: fix me at 2037-12-31
	milliseconds := (utc.Nanosecond() / 1000000) % 1000
	return []int{seconds, milliseconds}
}

func (server *Server) addRule(site string, req *http.Request) (*Rule, error) {
	rule, err := server.makeRule(site, req)
	if err != nil {
		return nil, err
	}
	return rule, server.storage.UpsertRule(rule)
}

func (server *Server) makeRule(site string, req *http.Request) (*Rule, error) {
	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, err
	}

	values, err := url.ParseQuery(req.URL.RawQuery)
	if err != nil {
		return nil, err
	}
	path := server.getPath(req)
	delay, err := getDelay(values)
	if err != nil {
		return nil, err
	}
	return &Rule{Site: site, StatusCode: http.StatusOK, Headers: EMPTY_HEADERS,
		Path: path, Method: values.Get("method"),
		Body: string(body), Delay: delay}, nil
}

func getDelay(values url.Values) (time.Duration, error) {
	_, contains := values["delay"]
	if !contains {
		return time.Duration(0), nil
	}
	delayInSeconds, err := strconv.ParseFloat(values.Get("delay"), 64)
	if err != nil {
		return time.Duration(0), err
	}
	return time.Duration(delayInSeconds*1000) * time.Millisecond, nil
}

func (server *Server) handleError(err error, w http.ResponseWriter) {
	log.Print(err)
	http.Error(w, fmt.Sprintf("Internal error: %s. For real", err), http.StatusInternalServerError)
}

func (server *Server) makeTemplateData(rule *Rule) *TemplateData {
	var domain string
	if server.isInSingleSiteMode() {
		domain = server.config.endpoint
	} else {
		domain = fmt.Sprintf("%s.%s", rule.Site, server.config.endpoint)
	}
	return &TemplateData{Rule: rule, Domain: domain}
}

func (server *Server) respondFromRule(w http.ResponseWriter, req *http.Request) {
	rule, found, err := server.storage.FindRuleMatching(server.getSite(req), req)
	if err != nil {
		server.handleError(err, w)
		return
	}

	if found {
		ApplyRule(rule, w)
	} else {
		http.Error(w, "No rule. For real.", http.StatusNotFound)
	}
}

func (server *Server) isAddRule(r *http.Request) bool {
	if r.Method != "POST" {
		return false
	}
	if server.isInSingleSiteMode() {
		return server.isSpecialPath(r.URL.Path)
	}
	return strings.HasPrefix(getSubdomain(r.Host), ADD_RULE_SUBDOMAIN_PREFIX)
}

func (server *Server) isSpecialPath(path string) bool {
	specialPath := server.config.singleDomainUrlPath
	if !strings.HasPrefix(path, specialPath) {
		return false
	}
	if strings.HasSuffix(specialPath, "/") {
		return true
	}
	suffix := strings.TrimPrefix(path, specialPath)
	return suffix == "" || suffix[0] == '?' || suffix[0] == '/'
}

func (server *Server) handleAddRule(w http.ResponseWriter, req *http.Request) {
	rule, err := server.addRule(server.getSite(req), req)
	if err != nil {
		server.handleError(err, w)
		return
	}
	ADD_RULE_TEMPLATE.Execute(w, server.makeTemplateData(rule))
}

func (server *Server) getSite(req *http.Request) string {
	if server.isInSingleSiteMode() {
		return ""
	}
	subdomain := getSubdomain(req.Host)
	if server.isAddRule(req) {
		return strings.TrimPrefix(subdomain, ADD_RULE_SUBDOMAIN_PREFIX)
	}
	return subdomain
}

func (server *Server) getPath(req *http.Request) string {
	if server.isInSingleSiteMode() {
		path := strings.TrimPrefix(req.URL.Path, server.config.singleDomainUrlPath)
		return ensureHasPrefix(path, "/")
	}
	return req.URL.Path
}

func ensureHasPrefix(s, prefix string) string {
	if !strings.HasPrefix(s, prefix) {
		return prefix + s
	}
	return s
}

func ApplyRule(rule *Rule, w http.ResponseWriter) {
	time.Sleep(rule.Delay)

	addHeaders(rule.Headers, w)
	w.WriteHeader(rule.StatusCode)
	io.WriteString(w, rule.Body)
}

func addHeaders(headers map[string]string, w http.ResponseWriter) {
	responseHeader := w.Header()
	for key, value := range headers {
		responseHeader.Add(key, value)
	}
}

func (server *Server) createDefaultRules() {
	server.createDelayRules()
	server.createStatusRules()
}

func (server *Server) createDelayRules() {
	for i := 0; i <= MAX_DELAY; i++ {
		delaySite := strconv.Itoa(i)
		delay := time.Duration(i) * time.Second

		server.storage.UpsertRule(&Rule{Site: delaySite, Headers: EMPTY_HEADERS, Delay: delay,
			StatusCode: http.StatusOK, Body: DEFAULT_RESPONSE,
		})
	}
}

func (server *Server) createStatusRules() {
	for i := MIN_STATUS_CODE; i <= MAX_STATUS_CODE; i++ {
		statusSite := strconv.Itoa(i)
		headers := server.headersForStatus(i)
		server.storage.UpsertRule(&Rule{Site: statusSite, StatusCode: i,
			Headers: headers, Body: DEFAULT_RESPONSE})
	}
}

func (server *Server) headersForStatus(status int) map[string]string {
	_, isRedirect := REDIRECT_STATUSES[status]
	if isRedirect {
		// TODO: check that protocol-independent location is legal HTTP
		// TODO: header should respect current port
		host := fmt.Sprintf("//0.goslow.link")
		return map[string]string{"Location": host}
	}
	return EMPTY_HEADERS
}

func (server *Server) ListenAndServe() error {
	log.Printf("listening on %s", server.config.listenOn)
	return http.ListenAndServe(server.config.listenOn, server)
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
)

func main() {
	// These assets are embedded into the executable using
	// https://github.com/jteeuwen/go-bindata
	// to generate bindata.go -- this removes the need to manage the path
	// just to run the demo
	angular, ng, bootstrap, app, index, glyphicons := mustLoadAssets()
	http.HandleFunc("/angular.min.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/javascript")
		w.Write(angular)
	})
	http.HandleFunc("/ng-flow-standalone.min.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/javascript")
		w.Write(ng)
	})
	http.HandleFunc("/app.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/javascript")
		w.Write(app)
	})
	http.HandleFunc("/bootstrap-combined.min.css", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/css")
		w.Write(bootstrap)
	})
	http.HandleFunc("/img/glyphicons-halflings.png", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "image/png")
		w.Write(glyphicons)
	})
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Write(index)
	})
	//http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {  })
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func mustLoadAssets() ([]byte, []byte, []byte, []byte, []byte, []byte) {
	angular, err := Asset("html/angular.min.js")
	if err != nil {
		log.Fatal("Problem loading angular static asset: ", err)
	}
	ng, err := Asset("html/ng-flow-standalone.min.js")
	if err != nil {
		log.Fatal("Problem loading ng-flow static asset: ", err)
	}
	bootstrap, err := Asset("html/bootstrap-combined.min.css")
	if err != nil {
		log.Fatal("Problem loading bootstrap static asset: ", err)
	}
	app, err := Asset("html/app.js")
	if err != nil {
		log.Fatal("Problem loading app static asset: ", err)
	}
	index, err := Asset("html/index.html")
	if err != nil {
		log.Fatal("Problem loading index.html static asset: ", err)
	}
	glyphicons, err := Asset("html/glyphicons-halflings.png")
	if err != nil {
		log.Fatal("Problem loading glyphicons static asset: ", err)
	}
	return angular, ng, bootstrap, app, index, glyphicons
}
<commit_msg>comments update<commit_after>package main

import (
	"log"
	"net/http"

	"github.com/patdek/ngflow"
)

func main() {
	// These assets are embedded into the executable using
	// https://github.com/jteeuwen/go-bindata
	// to generate bindata.go -- this removes the need to manage the path
	// just to run the demo ... it should "just work"(TM)
	angular, ng, bootstrap, app, index, glyphicons := mustLoadAssets()
	http.HandleFunc("/angular.min.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/javascript")
		w.Write(angular)
	})
	http.HandleFunc("/ng-flow-standalone.min.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/javascript")
		w.Write(ng)
	})
	http.HandleFunc("/app.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/javascript")
		w.Write(app)
	})
	http.HandleFunc("/bootstrap-combined.min.css", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/css")
		w.Write(bootstrap)
	})
	http.HandleFunc("/img/glyphicons-halflings.png", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "image/png")
		w.Write(glyphicons)
	})
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Write(index)
	})
	http.HandleFunc("/upload", ngflow.UploadHandler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func mustLoadAssets() ([]byte, []byte, []byte, []byte, []byte, []byte) {
	angular, err := Asset("html/angular.min.js")
	if err != nil {
		log.Fatal("Problem loading angular static asset: ", err)
	}
	ng, err := Asset("html/ng-flow-standalone.min.js")
	if err != nil {
		log.Fatal("Problem loading ng-flow static asset: ", err)
	}
	bootstrap, err := Asset("html/bootstrap-combined.min.css")
	if err != nil {
		log.Fatal("Problem loading bootstrap static asset: ", err)
	}
	app, err := Asset("html/app.js")
	if err != nil {
		log.Fatal("Problem loading app static asset: ", err)
	}
	index, err := Asset("html/index.html")
	if err != nil {
		log.Fatal("Problem loading index.html static asset: ", err)
	}
	glyphicons, err := Asset("html/glyphicons-halflings.png")
	if err != nil {
		log.Fatal("Problem loading glyphicons static asset: ", err)
	}
	return angular, ng, bootstrap, app, index, glyphicons
}
<|endoftext|>