<commit_before>package main

import (
	"github.com/gorilla/mux"
	"net/http"
)

type Route struct {
	Name        string
	Method      string
	Pattern     string
	HandlerFunc appHandler
}

type Routes []Route

var routes = Routes{
	Route{
		"Search",
		"POST",
		"/search",
		Search,
	},
	Route{
		"Show",
		"GET",
		"/image/{hash}",
		Show,
	},
}

func NewRouter() *mux.Router {
	router := mux.NewRouter().StrictSlash(true)
	for _, route := range routes {
		var handler http.Handler
		handler = route.HandlerFunc
		handler = Logger(handler, route.Name)

		router.
			Methods(route.Method).
			Path(route.Pattern).
			Name(route.Name).
			Handler(handler)

	}
	return router
}
<commit_msg>Added the Add method<commit_after>package main

import (
	"github.com/gorilla/mux"
	"net/http"
)

type Route struct {
	Name        string
	Method      string
	Pattern     string
	HandlerFunc appHandler
}

type Routes []Route

var routes = Routes{
	Route{
		"Search",
		"POST",
		"/search",
		Search,
	},
	Route{
		"Add",
		"POST",
		"/add",
		Add,
	},
	Route{
		"Show",
		"GET",
		"/image/{hash}",
		Show,
	},
}

func NewRouter() *mux.Router {
	router := mux.NewRouter().StrictSlash(true)
	for _, route := range routes {
		var handler http.Handler
		handler = route.HandlerFunc
		handler = Logger(handler, route.Name)

		router.
			Methods(route.Method).
			Path(route.Pattern).
			Name(route.Name).
			Handler(handler)

	}
	return router
}
<|endoftext|>


<commit_before>// +build lua

package server

import (
	"encoding/hex"
	"fmt"
	"io"
	"sync"

	"github.com/siddontang/go/hack"
	"github.com/siddontang/go/num"
	lua "github.com/siddontang/golua"
	"github.com/siddontang/ledisdb/ledis"
)

//ledis <-> lua type conversion, same as http://redis.io/commands/eval

type luaWriter struct {
	l *lua.State
}

func (w *luaWriter) writeError(err error) {
	panic(err)
}

func (w *luaWriter) writeStatus(status string) {
	w.l.NewTable()
	top := w.l.GetTop()

	w.l.PushString("ok")
	w.l.PushString(status)
	w.l.SetTable(top)
}

func (w *luaWriter) writeInteger(n int64) {
	w.l.PushInteger(n)
}

func (w *luaWriter) writeBulk(b []byte) {
	if b == nil {
		w.l.PushBoolean(false)
	} else {
		w.l.PushString(hack.String(b))
	}
}

func (w *luaWriter) writeArray(lst []interface{}) {
	if lst == nil {
		w.l.PushBoolean(false)
		return
	}

	w.l.CreateTable(len(lst), 0)
	top := w.l.GetTop()

	for i := range lst {
		w.l.PushInteger(int64(i) + 1)

		switch v := lst[i].(type) {
		case []interface{}:
			w.writeArray(v)
		case [][]byte:
			w.writeSliceArray(v)
		case []byte:
			w.writeBulk(v)
		case nil:
			w.writeBulk(nil)
		case int64:
			w.writeInteger(v)
		default:
			panic("invalid array type")
		}

		w.l.SetTable(top)
	}
}

func (w *luaWriter) writeSliceArray(lst [][]byte) {
	if lst == nil {
		w.l.PushBoolean(false)
		return
	}

	w.l.CreateTable(len(lst), 0)
	for i, v := range lst {
		if v == nil {
			w.l.PushBoolean(false)
		} else {
			w.l.PushString(hack.String(v))
		}
		w.l.RawSeti(-2, i+1)
	}
}

func (w *luaWriter) writeFVPairArray(lst []ledis.FVPair) {
	if lst == nil {
		w.l.PushBoolean(false)
		return
	}

	w.l.CreateTable(len(lst)*2, 0)
	for i, v := range lst {
		w.l.PushString(hack.String(v.Field))
		w.l.RawSeti(-2, 2*i+1)

		w.l.PushString(hack.String(v.Value))
		w.l.RawSeti(-2, 2*i+2)
	}
}

func (w *luaWriter) writeScorePairArray(lst []ledis.ScorePair, withScores bool) {
	if lst == nil {
		w.l.PushBoolean(false)
		return
	}

	if withScores {
		w.l.CreateTable(len(lst)*2, 0)
		for i, v := range lst {
			w.l.PushString(hack.String(v.Member))
			w.l.RawSeti(-2, 2*i+1)

			w.l.PushString(hack.String(num.FormatInt64ToSlice(v.Score)))
			w.l.RawSeti(-2, 2*i+2)
		}
	} else {
		w.l.CreateTable(len(lst), 0)
		for i, v := range lst {
			w.l.PushString(hack.String(v.Member))
			w.l.RawSeti(-2, i+1)
		}
	}
}

func (w *luaWriter) writeBulkFrom(n int64, rb io.Reader) {
	w.writeError(fmt.Errorf("unsupport"))
}

func (w *luaWriter) flush() {

}

type script struct {
	sync.Mutex

	app *App
	l   *lua.State
	c   *client

	chunks map[string]struct{}
}

func (app *App) openScript() {
	s := new(script)
	s.app = app

	s.chunks = make(map[string]struct{})

	app.script = s

	l := lua.NewState()

	l.OpenBase()
	l.OpenLibs()
	l.OpenMath()
	l.OpenString()
	l.OpenTable()
	l.OpenPackage()

	l.OpenCJson()
	l.OpenCMsgpack()
	l.OpenStruct()

	l.Register("error", luaErrorHandler)

	s.l = l
	s.c = newClient(app)
	s.c.db = nil

	w := new(luaWriter)
	w.l = l
	s.c.resp = w

	l.NewTable()
	l.PushString("call")
	l.PushGoFunction(luaCall)
	l.SetTable(-3)

	l.PushString("pcall")
	l.PushGoFunction(luaPCall)
	l.SetTable(-3)

	l.PushString("sha1hex")
	l.PushGoFunction(luaSha1Hex)
	l.SetTable(-3)

	l.PushString("error_reply")
	l.PushGoFunction(luaErrorReply)
	l.SetTable(-3)

	l.PushString("status_reply")
	l.PushGoFunction(luaStatusReply)
	l.SetTable(-3)

	l.SetGlobal("ledis")

	setMapState(l, s)
}

func (app *App) closeScript() {
	app.script.l.Close()
	delMapState(app.script.l)
	app.script = nil
}

var mapState = map[*lua.State]*script{}
var stateLock sync.Mutex

func setMapState(l *lua.State, s *script) {
	stateLock.Lock()
	defer stateLock.Unlock()

	mapState[l] = s
}

func getMapState(l *lua.State) *script {
	stateLock.Lock()
	defer stateLock.Unlock()

	return mapState[l]
}

func delMapState(l *lua.State) {
	stateLock.Lock()
	defer stateLock.Unlock()

	delete(mapState, l)
}

func luaErrorHandler(l *lua.State) int {
	msg := l.ToString(1)
	panic(fmt.Errorf(msg))
}

func luaCall(l *lua.State) int {
	return luaCallGenericCommand(l)
}

func luaPCall(l *lua.State) (n int) {
	defer func() {
		if e := recover(); e != nil {
			luaPushError(l, fmt.Sprintf("%v", e))
			n = 1
		}
		return
	}()
	return luaCallGenericCommand(l)
}

func luaErrorReply(l *lua.State) int {
	return luaReturnSingleFieldTable(l, "err")
}

func luaStatusReply(l *lua.State) int {
	return luaReturnSingleFieldTable(l, "ok")
}

func luaReturnSingleFieldTable(l *lua.State, filed string) int {
	if l.GetTop() != 1 || l.Type(-1) != lua.LUA_TSTRING {
		luaPushError(l, "wrong number or type of arguments")
		return 1
	}

	l.NewTable()
	l.PushString(filed)
	l.PushValue(-3)
	l.SetTable(-3)
	return 1
}

func luaSha1Hex(l *lua.State) int {
	argc := l.GetTop()
	if argc != 1 {
		luaPushError(l, "wrong number of arguments")
		return 1
	}

	s := l.ToString(1)
	s = hex.EncodeToString(hack.Slice(s))

	l.PushString(s)
	return 1
}

func luaPushError(l *lua.State, msg string) {
	l.NewTable()
	l.PushString("err")
	err := l.NewError(msg)
	l.PushString(err.Error())
	l.SetTable(-3)
}

func luaCallGenericCommand(l *lua.State) int {
	s := getMapState(l)
	if s == nil {
		panic("Invalid lua call")
	} else if s.c.db == nil {
		panic("Invalid lua call, not prepared")
	}

	c := s.c

	argc := l.GetTop()
	if argc < 1 {
		panic("Please specify at least one argument for ledis.call()")
	}

	c.cmd = l.ToString(1)

	c.args = make([][]byte, argc-1)

	for i := 2; i <= argc; i++ {
		switch l.Type(i) {
		case lua.LUA_TNUMBER:
			c.args[i-2] = []byte(fmt.Sprintf("%.17g", l.ToNumber(i)))
		case lua.LUA_TSTRING:
			c.args[i-2] = []byte(l.ToString(i))
		default:
			panic("Lua ledis() command arguments must be strings or integers")
		}
	}

	c.perform()

	return 1
}

func luaSetGlobalArray(l *lua.State, name string, ay [][]byte) {
	l.NewTable()

	for i := 0; i < len(ay); i++ {
		l.PushString(hack.String(ay[i]))
		l.RawSeti(-2, i+1)
	}

	l.SetGlobal(name)
}

func luaReplyToLedisReply(l *lua.State) interface{} {
	base := l.GetTop()
	defer func() {
		l.SetTop(base - 1)
	}()

	switch l.Type(-1) {
	case lua.LUA_TSTRING:
		return hack.Slice(l.ToString(-1))
	case lua.LUA_TBOOLEAN:
		if l.ToBoolean(-1) {
			return int64(1)
		} else {
			return nil
		}
	case lua.LUA_TNUMBER:
		return int64(l.ToInteger(-1))
	case lua.LUA_TTABLE:
		l.PushString("err")
		l.GetTable(-2)
		if l.Type(-1) == lua.LUA_TSTRING {
			return fmt.Errorf("%s", l.ToString(-1))
		}

		l.Pop(1)
		l.PushString("ok")
		l.GetTable(-2)
		if l.Type(-1) == lua.LUA_TSTRING {
			return l.ToString(-1)
		} else {
			l.Pop(1)

			ay := make([]interface{}, 0)

			for i := 1; ; i++ {
				l.PushInteger(int64(i))
				l.GetTable(-2)
				if l.Type(-1) == lua.LUA_TNIL {
					l.Pop(1)
					break
				}

				ay = append(ay, luaReplyToLedisReply(l))
			}
			return ay

		}
	default:
		return nil
	}
}
<commit_msg>support redis as a global var in lua scripts (#289)<commit_after>// +build lua

package server

import (
	"encoding/hex"
	"fmt"
	"io"
	"sync"

	"github.com/siddontang/go/hack"
	"github.com/siddontang/go/num"
	lua "github.com/siddontang/golua"
	"github.com/siddontang/ledisdb/ledis"
)

//ledis <-> lua type conversion, same as http://redis.io/commands/eval

type luaWriter struct {
	l *lua.State
}

func (w *luaWriter) writeError(err error) {
	panic(err)
}

func (w *luaWriter) writeStatus(status string) {
	w.l.NewTable()
	top := w.l.GetTop()

	w.l.PushString("ok")
	w.l.PushString(status)
	w.l.SetTable(top)
}

func (w *luaWriter) writeInteger(n int64) {
	w.l.PushInteger(n)
}

func (w *luaWriter) writeBulk(b []byte) {
	if b == nil {
		w.l.PushBoolean(false)
	} else {
		w.l.PushString(hack.String(b))
	}
}

func (w *luaWriter) writeArray(lst []interface{}) {
	if lst == nil {
		w.l.PushBoolean(false)
		return
	}

	w.l.CreateTable(len(lst), 0)
	top := w.l.GetTop()

	for i := range lst {
		w.l.PushInteger(int64(i) + 1)

		switch v := lst[i].(type) {
		case []interface{}:
			w.writeArray(v)
		case [][]byte:
			w.writeSliceArray(v)
		case []byte:
			w.writeBulk(v)
		case nil:
			w.writeBulk(nil)
		case int64:
			w.writeInteger(v)
		default:
			panic("invalid array type")
		}

		w.l.SetTable(top)
	}
}

func (w *luaWriter) writeSliceArray(lst [][]byte) {
	if lst == nil {
		w.l.PushBoolean(false)
		return
	}

	w.l.CreateTable(len(lst), 0)
	for i, v := range lst {
		if v == nil {
			w.l.PushBoolean(false)
		} else {
			w.l.PushString(hack.String(v))
		}
		w.l.RawSeti(-2, i+1)
	}
}

func (w *luaWriter) writeFVPairArray(lst []ledis.FVPair) {
	if lst == nil {
		w.l.PushBoolean(false)
		return
	}

	w.l.CreateTable(len(lst)*2, 0)
	for i, v := range lst {
		w.l.PushString(hack.String(v.Field))
		w.l.RawSeti(-2, 2*i+1)

		w.l.PushString(hack.String(v.Value))
		w.l.RawSeti(-2, 2*i+2)
	}
}

func (w *luaWriter) writeScorePairArray(lst []ledis.ScorePair, withScores bool) {
	if lst == nil {
		w.l.PushBoolean(false)
		return
	}

	if withScores {
		w.l.CreateTable(len(lst)*2, 0)
		for i, v := range lst {
			w.l.PushString(hack.String(v.Member))
			w.l.RawSeti(-2, 2*i+1)

			w.l.PushString(hack.String(num.FormatInt64ToSlice(v.Score)))
			w.l.RawSeti(-2, 2*i+2)
		}
	} else {
		w.l.CreateTable(len(lst), 0)
		for i, v := range lst {
			w.l.PushString(hack.String(v.Member))
			w.l.RawSeti(-2, i+1)
		}
	}
}

func (w *luaWriter) writeBulkFrom(n int64, rb io.Reader) {
	w.writeError(fmt.Errorf("unsupport"))
}

func (w *luaWriter) flush() {

}

type script struct {
	sync.Mutex

	app *App
	l   *lua.State
	c   *client

	chunks map[string]struct{}
}

func (app *App) openScript() {
	s := new(script)
	s.app = app

	s.chunks = make(map[string]struct{})

	app.script = s

	l := lua.NewState()

	l.OpenBase()
	l.OpenLibs()
	l.OpenMath()
	l.OpenString()
	l.OpenTable()
	l.OpenPackage()

	l.OpenCJson()
	l.OpenCMsgpack()
	l.OpenStruct()

	l.Register("error", luaErrorHandler)

	s.l = l
	s.c = newClient(app)
	s.c.db = nil

	w := new(luaWriter)
	w.l = l
	s.c.resp = w

	setGlobalDBScriptVar(l, "ledis")
	setGlobalDBScriptVar(l, "redis")

	setMapState(l, s)
}

func (app *App) closeScript() {
	app.script.l.Close()
	delMapState(app.script.l)
	app.script = nil
}

var mapState = map[*lua.State]*script{}
var stateLock sync.Mutex

func setGlobalDBScriptVar(l *lua.State, name string) {
	l.NewTable()
	l.PushString("call")
	l.PushGoFunction(luaCall)
	l.SetTable(-3)

	l.PushString("pcall")
	l.PushGoFunction(luaPCall)
	l.SetTable(-3)

	l.PushString("sha1hex")
	l.PushGoFunction(luaSha1Hex)
	l.SetTable(-3)

	l.PushString("error_reply")
	l.PushGoFunction(luaErrorReply)
	l.SetTable(-3)

	l.PushString("status_reply")
	l.PushGoFunction(luaStatusReply)
	l.SetTable(-3)

	l.SetGlobal(name)
}

func setMapState(l *lua.State, s *script) {
	stateLock.Lock()
	defer stateLock.Unlock()

	mapState[l] = s
}

func getMapState(l *lua.State) *script {
	stateLock.Lock()
	defer stateLock.Unlock()

	return mapState[l]
}

func delMapState(l *lua.State) {
	stateLock.Lock()
	defer stateLock.Unlock()

	delete(mapState, l)
}

func luaErrorHandler(l *lua.State) int {
	msg := l.ToString(1)
	panic(fmt.Errorf(msg))
}

func luaCall(l *lua.State) int {
	return luaCallGenericCommand(l)
}

func luaPCall(l *lua.State) (n int) {
	defer func() {
		if e := recover(); e != nil {
			luaPushError(l, fmt.Sprintf("%v", e))
			n = 1
		}
		return
	}()
	return luaCallGenericCommand(l)
}

func luaErrorReply(l *lua.State) int {
	return luaReturnSingleFieldTable(l, "err")
}

func luaStatusReply(l *lua.State) int {
	return luaReturnSingleFieldTable(l, "ok")
}

func luaReturnSingleFieldTable(l *lua.State, filed string) int {
	if l.GetTop() != 1 || l.Type(-1) != lua.LUA_TSTRING {
		luaPushError(l, "wrong number or type of arguments")
		return 1
	}

	l.NewTable()
	l.PushString(filed)
	l.PushValue(-3)
	l.SetTable(-3)
	return 1
}

func luaSha1Hex(l *lua.State) int {
	argc := l.GetTop()
	if argc != 1 {
		luaPushError(l, "wrong number of arguments")
		return 1
	}

	s := l.ToString(1)
	s = hex.EncodeToString(hack.Slice(s))

	l.PushString(s)
	return 1
}

func luaPushError(l *lua.State, msg string) {
	l.NewTable()
	l.PushString("err")
	err := l.NewError(msg)
	l.PushString(err.Error())
	l.SetTable(-3)
}

func luaCallGenericCommand(l *lua.State) int {
	s := getMapState(l)
	if s == nil {
		panic("Invalid lua call")
	} else if s.c.db == nil {
		panic("Invalid lua call, not prepared")
	}

	c := s.c

	argc := l.GetTop()
	if argc < 1 {
		panic("Please specify at least one argument for ledis.call()")
	}

	c.cmd = l.ToString(1)

	c.args = make([][]byte, argc-1)

	for i := 2; i <= argc; i++ {
		switch l.Type(i) {
		case lua.LUA_TNUMBER:
			c.args[i-2] = []byte(fmt.Sprintf("%.17g", l.ToNumber(i)))
		case lua.LUA_TSTRING:
			c.args[i-2] = []byte(l.ToString(i))
		default:
			panic("Lua ledis() command arguments must be strings or integers")
		}
	}

	c.perform()

	return 1
}

func luaSetGlobalArray(l *lua.State, name string, ay [][]byte) {
	l.NewTable()

	for i := 0; i < len(ay); i++ {
		l.PushString(hack.String(ay[i]))
		l.RawSeti(-2, i+1)
	}

	l.SetGlobal(name)
}

func luaReplyToLedisReply(l *lua.State) interface{} {
	base := l.GetTop()
	defer func() {
		l.SetTop(base - 1)
	}()

	switch l.Type(-1) {
	case lua.LUA_TSTRING:
		return hack.Slice(l.ToString(-1))
	case lua.LUA_TBOOLEAN:
		if l.ToBoolean(-1) {
			return int64(1)
		} else {
			return nil
		}
	case lua.LUA_TNUMBER:
		return int64(l.ToInteger(-1))
	case lua.LUA_TTABLE:
		l.PushString("err")
		l.GetTable(-2)
		if l.Type(-1) == lua.LUA_TSTRING {
			return fmt.Errorf("%s", l.ToString(-1))
		}

		l.Pop(1)
		l.PushString("ok")
		l.GetTable(-2)
		if l.Type(-1) == lua.LUA_TSTRING {
			return l.ToString(-1)
		} else {
			l.Pop(1)

			ay := make([]interface{}, 0)

			for i := 1; ; i++ {
				l.PushInteger(int64(i))
				l.GetTable(-2)
				if l.Type(-1) == lua.LUA_TNIL {
					l.Pop(1)
					break
				}

				ay = append(ay, luaReplyToLedisReply(l))
			}
			return ay

		}
	default:
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
  "encoding/binary"
  "encoding/hex"
  "errors"

  "github.com/google/gopacket"
  "github.com/google/gopacket/layers"
  "github.com/google/gopacket/pcap"

  "log"
  "net"
  "time"
)

func CreateStream(config Config, destination string) chan []byte {
  if handle == nil {
    SetupSockets(config)
  }

  dest := net.ParseIP(destination)
  flow := make(chan []byte)
  go HandleStream(dest, flow)
  return flow
}

func HandleStream(dest net.IP, que chan []byte) {
  for {
    req := <-que
    ConditionalForward(req, dest)
  }
}

var (
  handle *pcap.Handle
  ipv4Layer layers.IPv4
  ipv4Parser *gopacket.DecodingLayerParser
  linkHeader []byte
)

func SetupSockets(config Config) {
  var err error
  ipv4Parser = gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ipv4Layer)

  handle, err = pcap.OpenLive(config.Device, 1024, false, 30 * time.Second)
  if err != nil {
    panic(err)
  }

  srcBytes, _ := hex.DecodeString(config.Src)
  dstBytes, _ := hex.DecodeString(config.Dst)
  linkHeader = append(dstBytes, srcBytes...)
  linkHeader = append(linkHeader, 0, 0)

//  var ipv6Layer layers.ipv6
//  ipv6Parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv6, &ipv6Layer)
}

func ConditionalForward(packet []byte, dest net.IP) error {
  if p4 := dest.To4(); len(p4) == net.IPv4len {
    return ConditionalForward4(packet, dest)
  } else {
    return ConditionalForward6(packet, dest)
  }
}

func ConditionalForward4(packet []byte, dest net.IP) error {
  // Make sure destination is okay
  decoded := []gopacket.LayerType{}
  if err := ipv4Parser.DecodeLayers(packet, &decoded); len(decoded) != 1 {
    return err
  }
  if dest.Equal(ipv4Layer.DstIP) {
    log.Println("Intended packet was to", ipv4Layer.DstIP, "not the authorized", dest)
    return errors.New("INVALID DESTINATION")
  }

  // Prepend with ethernet header
  pktlen := uint16(len(packet))
  binary.BigEndian.PutUint16(linkHeader[12:], pktlen)

  if err := handle.WritePacketData(append(linkHeader, packet...)); err != nil {
    log.Println("Couldn't send packet", err)
    return err
  }
  return nil
}

func ConditionalForward6(packet []byte, dest net.IP) error {
  return errors.New("UNSUPPORTED")
}
<commit_msg>switch to ethernet II link-layer header<commit_after>package main

import (
  "encoding/binary"
  "encoding/hex"
  "errors"

  "github.com/google/gopacket"
  "github.com/google/gopacket/layers"
  "github.com/google/gopacket/pcap"

  "log"
  "net"
  "time"
)

func CreateStream(config Config, destination string) chan []byte {
  if handle == nil {
    SetupSockets(config)
  }

  dest := net.ParseIP(destination)
  flow := make(chan []byte)
  go HandleStream(dest, flow)
  return flow
}

func HandleStream(dest net.IP, que chan []byte) {
  for {
    req := <-que
    ConditionalForward(req, dest)
  }
}

var (
  handle *pcap.Handle
  ipv4Layer layers.IPv4
  ipv4Parser *gopacket.DecodingLayerParser
  linkHeader []byte
)

func SetupSockets(config Config) {
  var err error
  ipv4Parser = gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ipv4Layer)

  handle, err = pcap.OpenLive(config.Device, 1024, false, 30 * time.Second)
  if err != nil {
    panic(err)
  }

  srcBytes, _ := hex.DecodeString(config.Src)
  dstBytes, _ := hex.DecodeString(config.Dst)
  linkHeader = append(dstBytes, srcBytes...)
  linkHeader = append(linkHeader, 0x08, 0) // IPv4 EtherType

//  var ipv6Layer layers.ipv6
//  ipv6Parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv6, &ipv6Layer)
}

func ConditionalForward(packet []byte, dest net.IP) error {
  if p4 := dest.To4(); len(p4) == net.IPv4len {
    return ConditionalForward4(packet, dest)
  } else {
    return ConditionalForward6(packet, dest)
  }
}

func ConditionalForward4(packet []byte, dest net.IP) error {
  // Make sure destination is okay
  decoded := []gopacket.LayerType{}
  if err := ipv4Parser.DecodeLayers(packet, &decoded); len(decoded) != 1 {
    return err
  }
  if dest.Equal(ipv4Layer.DstIP) {
    log.Println("Intended packet was to", ipv4Layer.DstIP, "not the authorized", dest)
    return errors.New("INVALID DESTINATION")
  }

  if err := handle.WritePacketData(append(linkHeader, packet...)); err != nil {
    log.Println("Couldn't send packet", err)
    return err
  }
  return nil
}

func ConditionalForward6(packet []byte, dest net.IP) error {
  return errors.New("UNSUPPORTED")
}
<|endoftext|>


<commit_before>// Beam is a protocol and library for service-oriented communication,
// with an emphasis on real-world patterns, simplicity and not reinventing the wheel.
//
// See http://github.com/dotcloud/beam.
//
// github.com/dotcloud/beam/server is a server-side implementation of the Beam protocol.

package client

import (
	beam "github.com/dotcloud/beam/common"
)

type Server struct {
}

// New initializes a new beam server.
func New() srv *Server {

}

// RegisterJob exposes the function <h> as a remote job to be invoked by clients
// under the name <name>.
func (srv *Server) RegisterJob(name string, h JobHandler) {
}

// ServeJob is the server's default job handler. It is called every time a new job is created.
func (srv *Server) ServeJob(name string, args []string, env map[string]string, streams beam.Streamer, db beam.DB) error {
}

// A JobHandler is a function which can be invoked as a job by beam clients.
// The API for invoking jobs resembles that of unix processes:
//  - A job is invoked under a certain <name>.
//  - It may receive arguments as a string array (<args>).
//  - It may receive an environment as a map of key-value pairs (<env>).
//  - It may read from, and write to, streams of binary data. (<streams>).
//  - It returns value which can either indicate "success" or a variety of error conditions.
//
// Additionally, a job may modify the server's database, which is shared with all other jobs.
// This is similar to how multiple unix processes share access to the same filesystem.
//
type JobHandler func(name string, args []string, env map[string]string, streams beam.Streamer, db beam.DB) error

// ListenAndSerce listens on the address <addr> at protocol <proto> and then
// handles incoming requests on incoming following the beam protocol.
func (srv *Server) ListenAndServe(proto, addr string) error {

}
<commit_msg>Typos in comments<commit_after>// Beam is a protocol and library for service-oriented communication,
// with an emphasis on real-world patterns, simplicity and not reinventing the wheel.
//
// See http://github.com/dotcloud/beam.
//
// github.com/dotcloud/beam/server is a server-side implementation of the Beam protocol.

package client

import (
	beam "github.com/dotcloud/beam/common"
)

type Server struct {
}

// New initializes a new beam server.
func New() srv *Server {

}

// RegisterJob exposes the function <h> as a remote job to be invoked by clients
// under the name <name>.
func (srv *Server) RegisterJob(name string, h JobHandler) {
}

// ServeJob is the server's default job handler. It is called every time a new job is created.
func (srv *Server) ServeJob(name string, args []string, env map[string]string, streams beam.Streamer, db beam.DB) error {
}

// A JobHandler is a function which can be invoked as a job by beam clients.
// The API for invoking jobs resembles that of unix processes:
//  - A job is invoked under a certain <name>.
//  - It may receive arguments as a string array (<args>).
//  - It may receive an environment as a map of key-value pairs (<env>).
//  - It may read from, and write to, streams of binary data. (<streams>).
//  - It returns value which can either indicate "success" or a variety of error conditions.
//
// Additionally, a job may modify the server's database, which is shared with all other jobs.
// This is similar to how multiple unix processes share access to the same filesystem.
//
type JobHandler func(name string, args []string, env map[string]string, streams beam.Streamer, db beam.DB) error

// ListenAndServe listens on the address <addr> at protocol <proto> and then
// handles incoming requests following the beam protocol.
func (srv *Server) ListenAndServe(proto, addr string) error {

}
<|endoftext|>


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

package main

import (
	"fmt"
	"net/http"
	"log"
	"os"
	"os/exec"
	"encoding/json"
	"bytes"
	"flag"
	"go/build"
	"path"
	"path/filepath"
	"errors"
)

const (
	projectDirName = "github.com/emepyc/guiHive"
)

var (
	port string
)

func init () {
	flag.StringVar(&port, "port", "12345", "Port to listen (defaults to 12345)")
	flag.Parse()
}

func checkError (s string, err error, ss ...string) {
	if err != nil {
		log.Printf("%s: %s [%s]\n",s, err, ss)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func scriptHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	checkError("Can't parse Form", err)

	debug("METHOD: %s"  , r.Method)
	debug("URL: %s"     , r.URL)
	debug("FRAGMENT: %s", r.URL.Fragment)
	debug("PATH: %s"    , r.URL.Path)
	debug("BODY: %s"    , r.Body)
	debug("URL2: %s"    , r.Form)

	var outMsg bytes.Buffer
	var errMsg bytes.Buffer

	fname := os.Getenv("GUIHIVE_BASEDIR") + r.URL.Path
	args, err := json.Marshal(r.Form)
	checkError("Can't Marshal JSON:", err)
	debug("ARGS in Go side: %s", args)

	debug("EXECUTING SCRIPT: %s", fname)
	cmd := exec.Command(fname, string(args))
	cmd.Stdout = &outMsg
	cmd.Stderr = &errMsg

	if err := cmd.Start(); err != nil {
		log.Println("Error Starting Command: ", err)
	}
	if err := cmd.Wait(); err != nil {
		log.Println("Error Executing Command: ", err)
	}

	debug("OUTMSG: %s", outMsg.Bytes())
	debug("ERRMSG: %s", errMsg.Bytes())
	fmt.Fprintf(w, string(outMsg.Bytes()))

}

func pathExists(name string) bool {
	_, err := os.Stat(name)
	if os.IsNotExist(err) {
		return false
	}
	return err == nil
}

func guessProjectDir() (string, error) {
	// First, we try to find the project dir in the working directory
	serverPath := os.Args[0]
	serverDir := filepath.Dir(serverPath)
	debug("SERVER_DIR: %s", serverDir);
	pathToIndex := serverDir + "/../static/index.html"
	debug("PATH_TO_INDEX: %s", pathToIndex);
	absPathToIndex, err := filepath.Abs(pathToIndex)
	debug("ABS_PATH_TO_INDEX: %s", absPathToIndex);
	if err != nil {
		return "", err
	}
	if pathExists(absPathToIndex) {
		return path.Clean(absPathToIndex + "/../.."), nil
	}
	for _, srcdir := range build.Default.SrcDirs() {
		dirName := path.Join(srcdir, projectDirName)
		if pathExists(dirName) {
			return dirName, nil
		}
	}
	return "", errors.New("Project directory not found")
}

func setEnvVar() error {
	projectDirectory, err := guessProjectDir()
	if err != nil {
		return err
	}
	debug("PROJECT_DIRECTORY: %s\n", projectDirectory)

	// PER5LIB
	perl5lib := os.Getenv("PERL5LIB")
	pathToLibs := path.Clean(projectDirectory + "/scripts/lib")
	if perl5lib == "" {
		perl5lib = pathToLibs
	} else {
		perl5lib = perl5lib + ":" + pathToLibs
	}
	err = os.Setenv("PERL5LIB", perl5lib)
	if err != nil {
		return err
	}
	debug("PERL5LIB: %s\n", os.Getenv("PERL5LIB"))

	//GUIHIVE_BASEDIR
	if err := os.Setenv("GUIHIVE_BASEDIR", projectDirectory + "/"); err != nil {
		return err
	}
	debug("GUIHIVE_BASEDIR: %s", os.Getenv("GUIHIVE_BASEDIR"))

	return nil
}

func main() {

	//  Fix environmental variables
	errV := setEnvVar()
	checkError("Problem setting environmental variables: ", errV);

	relPath := os.Getenv("GUIHIVE_BASEDIR")
	http.HandleFunc("/",         handler)
	http.Handle("/static/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/styles/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/javascript/",  http.FileServer(http.Dir(relPath)))
	http.Handle("/images/",      http.FileServer(http.Dir(relPath)))
	http.HandleFunc("/scripts/", scriptHandler)
	debug("Listening to port: %s", port)
	err := http.ListenAndServe(":"+port, nil)
	checkError("ListenAndServe ", err)
}
<commit_msg>less debug output<commit_after>// Copyright 2012 Miguel Pignatelli. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"net/http"
	"log"
	"os"
	"os/exec"
	"encoding/json"
	"bytes"
	"flag"
	"go/build"
	"path"
	"path/filepath"
	"errors"
)

const (
	projectDirName = "github.com/emepyc/guiHive"
)

var (
	port string
)

func init () {
	flag.StringVar(&port, "port", "12345", "Port to listen (defaults to 12345)")
	flag.Parse()
}

func checkError (s string, err error, ss ...string) {
	if err != nil {
		log.Printf("%s: %s [%s]\n",s, err, ss)
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func scriptHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	checkError("Can't parse Form", err)

	debug("METHOD: %s"  , r.Method)
	debug("URL: %s"     , r.URL)
	debug("FRAGMENT: %s", r.URL.Fragment)
	debug("PATH: %s"    , r.URL.Path)
	debug("BODY: %s"    , r.Body)
	debug("URL2: %s"    , r.Form)

	var outMsg bytes.Buffer
	var errMsg bytes.Buffer

	fname := os.Getenv("GUIHIVE_BASEDIR") + r.URL.Path
	args, err := json.Marshal(r.Form)
	checkError("Can't Marshal JSON:", err)
	debug("ARGS in Go side: %s", args)

	debug("EXECUTING SCRIPT: %s", fname)
	cmd := exec.Command(fname, string(args))
	cmd.Stdout = &outMsg
	cmd.Stderr = &errMsg

	if err := cmd.Start(); err != nil {
		log.Println("Error Starting Command: ", err)
	}
	if err := cmd.Wait(); err != nil {
		log.Println("Error Executing Command: ", err)
	}

	debug("OUTMSG: %s", outMsg.Bytes())
	debug("ERRMSG: %s", errMsg.Bytes())
	fmt.Fprintf(w, string(outMsg.Bytes()))

}

func pathExists(name string) bool {
	_, err := os.Stat(name)
	if os.IsNotExist(err) {
		return false
	}
	return err == nil
}

func guessProjectDir() (string, error) {
	// First, we try to find the project dir in the working directory
	serverPath := os.Args[0]
	serverDir := filepath.Dir(serverPath)
	pathToIndex := serverDir + "/../static/index.html"
	absPathToIndex, err := filepath.Abs(pathToIndex)
	if err != nil {
		return "", err
	}
	if pathExists(absPathToIndex) {
		return path.Clean(absPathToIndex + "/../.."), nil
	}
	for _, srcdir := range build.Default.SrcDirs() {
		dirName := path.Join(srcdir, projectDirName)
		if pathExists(dirName) {
			return dirName, nil
		}
	}
	return "", errors.New("Project directory not found")
}

func setEnvVar() error {
	projectDirectory, err := guessProjectDir()
	if err != nil {
		return err
	}
	debug("PROJECT_DIRECTORY: %s\n", projectDirectory)

	// PER5LIB
	perl5lib := os.Getenv("PERL5LIB")
	pathToLibs := path.Clean(projectDirectory + "/scripts/lib")
	if perl5lib == "" {
		perl5lib = pathToLibs
	} else {
		perl5lib = perl5lib + ":" + pathToLibs
	}
	err = os.Setenv("PERL5LIB", perl5lib)
	if err != nil {
		return err
	}
	debug("PERL5LIB: %s\n", os.Getenv("PERL5LIB"))

	//GUIHIVE_BASEDIR
	if err := os.Setenv("GUIHIVE_BASEDIR", projectDirectory + "/"); err != nil {
		return err
	}
	debug("GUIHIVE_BASEDIR: %s", os.Getenv("GUIHIVE_BASEDIR"))

	return nil
}

func main() {

	//  Fix environmental variables
	errV := setEnvVar()
	checkError("Problem setting environmental variables: ", errV);

	relPath := os.Getenv("GUIHIVE_BASEDIR")
	http.HandleFunc("/",         handler)
	http.Handle("/static/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/styles/",      http.FileServer(http.Dir(relPath)))
	http.Handle("/javascript/",  http.FileServer(http.Dir(relPath)))
	http.Handle("/images/",      http.FileServer(http.Dir(relPath)))
	http.HandleFunc("/scripts/", scriptHandler)
	debug("Listening to port: %s", port)
	err := http.ListenAndServe(":"+port, nil)
	checkError("ListenAndServe ", err)
}
<|endoftext|>


<commit_before>package main

import "container/list"
import "encoding/json"
import "log"
import "net/http"
import "time"

type Message struct {
	Time time.Time
	Text string
}

type FetchResponse struct {
	Messages []Message
	Time     time.Time
}

type StoreRequest struct {
	StartTime time.Time
	Messages  chan<- []Message
}

type Store struct {
	Add chan Message
	Get chan StoreRequest
}

// TODO: Monotonic clock

func manage_store(store Store) {
	messages := list.New()
	message_count := 0
	max_messages := 1000
	waiting := list.New()
	for {
		select {
		case new_message := <-store.Add:
			messages.PushBack(new_message)
			for waiter := waiting.Front(); waiter != nil; waiter = waiter.Next() {
				waiter.Value.(StoreRequest).Messages <- []Message{new_message}
				close(waiter.Value.(StoreRequest).Messages)
			}
			waiting.Init()
			if message_count < max_messages {
				message_count++
			} else {
				messages.Remove(messages.Front())
			}
		case request := <-store.Get:
			if messages.Back() == nil || !request.StartTime.Before(messages.Back().Value.(Message).Time) {
				waiting.PushBack(request)
			} else {
				start := messages.Back()
				response_size := 1
				if messages.Front().Value.(Message).Time.After(request.StartTime) {
					start = messages.Front()
					response_size = message_count
				} else {
					for start.Prev().Value.(Message).Time.After(request.StartTime) {
						start = start.Prev()
						response_size++
					}
				}
				response_messages := make([]Message, 0, response_size)
				for m := start; m != nil; m = m.Next() {
					response_messages = append(response_messages, m.Value.(Message))
				}
				request.Messages <- response_messages
			}
		}
	}
}

func start_store() Store {
	store := Store{make(chan Message, 20), make(chan StoreRequest, 20)}
	go manage_store(store)
	return store
}

func start_server(store Store) {
	http.HandleFunc("/fetch", func(w http.ResponseWriter, r *http.Request) {
		var since time.Time
		url_since := r.FormValue("since")
		if url_since != "" {
			err := json.Unmarshal([]byte(url_since), &since)
			if err != nil {
				log.Print("fetch: parse since: ", err)
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte("Could not parse since as date"))
				return
			}
		}
		messages_from_store := make(chan []Message, 1)
		store.Get <- StoreRequest{since, messages_from_store}

		json_encoded, err := json.Marshal(FetchResponse{<-messages_from_store, time.Now()})
		if err != nil {
			log.Print("json encode: ", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		w.Header().Add("Content-Type", "application/json")
		w.Write(json_encoded)
	})

	http.HandleFunc("/speak", func(w http.ResponseWriter, r *http.Request) {
		store.Add <- Message{time.Now(), r.FormValue("text")}
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

func main() {
	store := start_store()
	start_server(store)
}
<commit_msg>Fetch returns a list of messages<commit_after>package main

import "container/list"
import "encoding/json"
import "log"
import "net/http"
import "time"

type Message struct {
	Time time.Time
	Text string
}

type StoreRequest struct {
	StartTime time.Time
	Messages  chan<- []Message
}

type Store struct {
	Add chan Message
	Get chan StoreRequest
}

// TODO: Monotonic clock

func manage_store(store Store) {
	messages := list.New()
	message_count := 0
	max_messages := 1000
	waiting := list.New()
	for {
		select {
		case new_message := <-store.Add:
			messages.PushBack(new_message)
			for waiter := waiting.Front(); waiter != nil; waiter = waiter.Next() {
				waiter.Value.(StoreRequest).Messages <- []Message{new_message}
				close(waiter.Value.(StoreRequest).Messages)
			}
			waiting.Init()
			if message_count < max_messages {
				message_count++
			} else {
				messages.Remove(messages.Front())
			}
		case request := <-store.Get:
			if messages.Back() == nil || !request.StartTime.Before(messages.Back().Value.(Message).Time) {
				waiting.PushBack(request)
			} else {
				start := messages.Back()
				response_size := 1
				if messages.Front().Value.(Message).Time.After(request.StartTime) {
					start = messages.Front()
					response_size = message_count
				} else {
					for start.Prev().Value.(Message).Time.After(request.StartTime) {
						start = start.Prev()
						response_size++
					}
				}
				response_messages := make([]Message, 0, response_size)
				for m := start; m != nil; m = m.Next() {
					response_messages = append(response_messages, m.Value.(Message))
				}
				request.Messages <- response_messages
			}
		}
	}
}

func start_store() Store {
	store := Store{make(chan Message, 20), make(chan StoreRequest, 20)}
	go manage_store(store)
	return store
}

func start_server(store Store) {
	http.HandleFunc("/fetch", func(w http.ResponseWriter, r *http.Request) {
		var since time.Time
		url_since := r.FormValue("since")
		if url_since != "" {
			err := json.Unmarshal([]byte(url_since), &since)
			if err != nil {
				log.Print("fetch: parse since: ", err)
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte("Could not parse since as date"))
				return
			}
		}
		messages_from_store := make(chan []Message, 1)
		store.Get <- StoreRequest{since, messages_from_store}

		json_encoded, err := json.Marshal(<-messages_from_store)
		if err != nil {
			log.Print("json encode: ", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		w.Header().Add("Content-Type", "application/json")
		w.Write(json_encoded)
	})

	http.HandleFunc("/speak", func(w http.ResponseWriter, r *http.Request) {
		store.Add <- Message{time.Now(), r.FormValue("text")}
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

func main() {
	store := start_store()
	start_server(store)
}
<|endoftext|>


<commit_before>package server

import (
	"fmt"
	"log"

	"github.com/labstack/echo"
	//	mw "github.com/labstack/echo/middleware"
	"github.com/nzai/stockrecorder/config"
)

//	启动服务
func Start() {

	e := echo.New()

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

	//	注册路由
	registerRoute(e)

	log.Print("启动Http服务")
	// Start server
	e.Run(fmt.Sprintf(":%d", config.Get().Port))
}
<commit_msg>server启用gzip大大减小数据传输量<commit_after>package server

import (
	"fmt"
	"log"

	"github.com/labstack/echo"
	mw "github.com/labstack/echo/middleware"
	"github.com/nzai/stockrecorder/config"
)

//	启动服务
func Start() {

	e := echo.New()

	// Middleware
	//	e.Use(mw.Logger())
	//	e.Use(mw.Recover())
	e.Use(mw.Gzip())

	//	注册路由
	registerRoute(e)

	log.Printf("启动Http服务,端口:%d", config.Get().Port)
	// Start server
	e.Run(fmt.Sprintf(":%d", config.Get().Port))
}
<|endoftext|>


<commit_before>package main

import (
	"log"

	"github.com/c-fs/cfs/disk"
	pb "github.com/c-fs/cfs/proto"
	"golang.org/x/net/context"
)

type server struct {
	// server contains a map of disks.
	// The key in the map is the name of the disk.
	disks map[string]*disk.Disk
}

func NewServer() *server {
	return &server{disks: make(map[string]*disk.Disk)}
}

func (s *server) Write(ctx context.Context, req *pb.WriteRequest) (*pb.WriteReply, error) {
	dn, fn, err := splitDiskAndFile(req.Name)
	if err != nil {
		log.Printf("server: write error (%v)", err)
		return &pb.WriteReply{}, nil
	}

	d := s.Disk(dn)
	if d == nil {
		log.Printf("server: write error (cannot find disk %s)", dn)
		return &pb.WriteReply{}, nil
	}

	n, _ := d.WriteAt(fn, req.Data, req.Offset)
	// TODO: add error
	reply := &pb.WriteReply{BytesWritten: int64(n)}
	return reply, nil
}

func (s *server) Read(ctx context.Context, req *pb.ReadRequest) (*pb.ReadReply, error) {
	dn, fn, err := splitDiskAndFile(req.Name)
	if err != nil {
		log.Printf("server: read error (%v)", err)
		return &pb.ReadReply{}, nil
	}

	d := s.Disk(dn)
	if d == nil {
		log.Printf("server: read error (cannot find disk %s)", dn)
		return &pb.ReadReply{}, nil
	}

	// TODO: reuse buffer
	data := make([]byte, req.Length)
	n, err := d.ReadAt(fn, data, req.Offset)
	// TODO: add error
	if err != nil {
		log.Printf("server: read error (%v)", err)
		return &pb.ReadReply{}, nil
	}
	reply := &pb.ReadReply{BytesRead: int64(n), Data: data}
	return reply, nil
}

func (s *server) Rename(ctx context.Context, req *pb.RenameRequest) (*pb.RenameReply, error) {
	dn0, ofn, err := splitDiskAndFile(req.Oldname)
	if err != nil {
		log.Printf("server: rename error (%v)", err)
		return &pb.RenameReply{}, nil
	}
	dn1, nfn, err := splitDiskAndFile(req.Newname)
	if err != nil {
		log.Printf("server: rename error (%v)", err)
		return &pb.RenameReply{}, nil
	}
	if dn0 != dn1 {
		log.Printf("server: rename error (%v)", "not same disk")
		return &pb.RenameReply{}, nil
	}

	d := s.Disk(dn0)
	if d == nil {
		log.Printf("server: read error (cannot find disk %s)", dn0)
		return &pb.RenameReply{}, nil
	}

	err = d.Rename(ofn, nfn)
	if err != nil {
		log.Printf("server: rename error (%v)", err)
		return &pb.RenameReply{}, nil
	}
	reply := &pb.RenameReply{}
	return reply, nil
}

func (s *server) Remove(ctx context.Context, req *pb.RemoveRequest) (*pb.RemoveReply, error) {
	dn, fn, err := splitDiskAndFile(req.Name)
	if err != nil {
		log.Printf("server: rename error (%v)", err)
		return &pb.RemoveReply{}, nil
	}

	d := s.Disk(dn)
	if d == nil {
		log.Printf("server: rename error (cannot find disk %s)", dn)
		return &pb.RemoveReply{}, nil
	}

	err = d.Remove(fn, req.All)
	if err != nil {
		log.Printf("server: read error (%v)", err)
		return &pb.RemoveReply{}, nil
	}
	reply := &pb.RemoveReply{}
	return reply, nil
}
<commit_msg>Fix remove typo<commit_after>package main

import (
	"log"

	"github.com/c-fs/cfs/disk"
	pb "github.com/c-fs/cfs/proto"
	"golang.org/x/net/context"
)

type server struct {
	// server contains a map of disks.
	// The key in the map is the name of the disk.
	disks map[string]*disk.Disk
}

func NewServer() *server {
	return &server{disks: make(map[string]*disk.Disk)}
}

func (s *server) Write(ctx context.Context, req *pb.WriteRequest) (*pb.WriteReply, error) {
	dn, fn, err := splitDiskAndFile(req.Name)
	if err != nil {
		log.Printf("server: write error (%v)", err)
		return &pb.WriteReply{}, nil
	}

	d := s.Disk(dn)
	if d == nil {
		log.Printf("server: write error (cannot find disk %s)", dn)
		return &pb.WriteReply{}, nil
	}

	n, _ := d.WriteAt(fn, req.Data, req.Offset)
	// TODO: add error
	reply := &pb.WriteReply{BytesWritten: int64(n)}
	return reply, nil
}

func (s *server) Read(ctx context.Context, req *pb.ReadRequest) (*pb.ReadReply, error) {
	dn, fn, err := splitDiskAndFile(req.Name)
	if err != nil {
		log.Printf("server: read error (%v)", err)
		return &pb.ReadReply{}, nil
	}

	d := s.Disk(dn)
	if d == nil {
		log.Printf("server: read error (cannot find disk %s)", dn)
		return &pb.ReadReply{}, nil
	}

	// TODO: reuse buffer
	data := make([]byte, req.Length)
	n, err := d.ReadAt(fn, data, req.Offset)
	// TODO: add error
	if err != nil {
		log.Printf("server: read error (%v)", err)
		return &pb.ReadReply{}, nil
	}
	reply := &pb.ReadReply{BytesRead: int64(n), Data: data}
	return reply, nil
}

func (s *server) Rename(ctx context.Context, req *pb.RenameRequest) (*pb.RenameReply, error) {
	dn0, ofn, err := splitDiskAndFile(req.Oldname)
	if err != nil {
		log.Printf("server: rename error (%v)", err)
		return &pb.RenameReply{}, nil
	}
	dn1, nfn, err := splitDiskAndFile(req.Newname)
	if err != nil {
		log.Printf("server: rename error (%v)", err)
		return &pb.RenameReply{}, nil
	}
	if dn0 != dn1 {
		log.Printf("server: rename error (%v)", "not same disk")
		return &pb.RenameReply{}, nil
	}

	d := s.Disk(dn0)
	if d == nil {
		log.Printf("server: read error (cannot find disk %s)", dn0)
		return &pb.RenameReply{}, nil
	}

	err = d.Rename(ofn, nfn)
	if err != nil {
		log.Printf("server: rename error (%v)", err)
		return &pb.RenameReply{}, nil
	}
	reply := &pb.RenameReply{}
	return reply, nil
}

func (s *server) Remove(ctx context.Context, req *pb.RemoveRequest) (*pb.RemoveReply, error) {
	dn, fn, err := splitDiskAndFile(req.Name)
	if err != nil {
		log.Printf("server: remove error (%v)", err)
		return &pb.RemoveReply{}, nil
	}

	d := s.Disk(dn)
	if d == nil {
		log.Printf("server: remove error (cannot find disk %s)", dn)
		return &pb.RemoveReply{}, nil
	}

	err = d.Remove(fn, req.All)
	if err != nil {
		log.Printf("server: read error (%v)", err)
		return &pb.RemoveReply{}, nil
	}
	reply := &pb.RemoveReply{}
	return reply, nil
}
<|endoftext|>


<commit_before>package object

import "testing"

func TestPrimitivesPrimitiveness(t *testing.T) {
	var prims [3]Value
	prims[0] = Boolean(false)
	prims[1] = Number(42)
	prims[2] = String("Hello, world!")

	for i := 0; i < len(prims); i++ {
		if !prims[i].IsPrimitive() {
			t.Errorf("%v.isPrimitive() = false", prims[i])
		}
	}
}

func TestBoolean(t *testing.T) {
	b := Boolean(false)
	if b.Parent() != Value(BooleanProto) {
		t.Errorf("%v.Parent() != BooleanProto", b)
	}
	if b.Parent().Parent() != Value(ObjectProto) {
		t.Errorf("%v.Parent().Parent() != ObjectProto", b)
	}
}

func TestNumber(t *testing.T) {
	b := Number(0)
	if b.Parent() != Value(NumberProto) {
		t.Errorf("%v.Parent() != NumberProto", b)
	}
	if b.Parent().Parent() != Value(ObjectProto) {
		t.Errorf("%v.Parent().Parent() != ObjectProto", b)
	}
}

func TestString(t *testing.T) {
	b := String("")
	if b.Parent() != Value(StringProto) {
		t.Errorf("%v.Parent() != StringProto", b)
	}
	if b.Parent().Parent() != Value(ObjectProto) {
		t.Errorf("%v.Parent().Parent() != ObjectProto", b)
	}
}
<commit_msg>Tidyvariable names.<commit_after>package object

import "testing"

func TestPrimitivesPrimitiveness(t *testing.T) {
	var prims [3]Value
	prims[0] = Boolean(false)
	prims[1] = Number(42)
	prims[2] = String("Hello, world!")

	for i := 0; i < len(prims); i++ {
		if !prims[i].IsPrimitive() {
			t.Errorf("%v.isPrimitive() = false", prims[i])
		}
	}
}

func TestBoolean(t *testing.T) {
	b := Boolean(false)
	if b.Parent() != Value(BooleanProto) {
		t.Errorf("%v.Parent() != BooleanProto", b)
	}
	if b.Parent().Parent() != Value(ObjectProto) {
		t.Errorf("%v.Parent().Parent() != ObjectProto", b)
	}
}

func TestNumber(t *testing.T) {
	n := Number(0)
	if n.Parent() != Value(NumberProto) {
		t.Errorf("%v.Parent() != NumberProto", n)
	}
	if n.Parent().Parent() != Value(ObjectProto) {
		t.Errorf("%v.Parent().Parent() != ObjectProto", n)
	}
}

func TestString(t *testing.T) {
	var s Value = String("")
	if s.Parent() != Value(StringProto) {
		t.Errorf("%v.Parent() != StringProto", s)
	}
	if s.Parent().Parent() != Value(ObjectProto) {
		t.Errorf("%v.Parent().Parent() != ObjectProto", s)
	}
}
<|endoftext|>


<commit_before>package server

import (
	"errors"
	"fmt"
	"io"
	"ldpserver/ldp"
	"ldpserver/textstore"
	"ldpserver/util"
	// "log"
)

const defaultSlug string = "node"

type Server struct {
	settings ldp.Settings
	minter   chan string
	// this should use an interface to it's not tied to "textStore"
	nextResource chan textstore.Store
}

func NewServer(rootUri string, dataPath string) Server {
	var server Server
	server.settings = ldp.SettingsNew(rootUri, dataPath)
	ldp.CreateRoot(server.settings)
	server.minter = CreateMinter(server.settings.IdFile())
	server.nextResource = make(chan textstore.Store)
	return server
}

func (server Server) GetNode(path string) (ldp.Node, error) {
	return ldp.GetNode(server.settings, path)
}

func (server Server) GetHead(path string) (ldp.Node, error) {
	return ldp.GetHead(server.settings, path)
}

// PUT
func (server Server) ReplaceRdfSource(triples string, parentPath string, slug string, etag string) (ldp.Node, error) {
	var path string
	isRootNode := (parentPath == ".") && (slug == ".")
	if isRootNode {
		path = "/"
	} else {
		newPath, err := server.getNewPath(slug)
		if err != nil {
			return ldp.Node{}, err
		}
		path = util.UriConcat(parentPath, newPath)
	}

	resource := server.createResourceFromPath(path)
	if resource.Error() != nil && resource.Error() != textstore.AlreadyExistsError {
		return ldp.Node{}, resource.Error()
	}

	if resource.Error() == textstore.AlreadyExistsError {
		// call replace existing (must validate etag, validate that previous version was RDF too)
		return ldp.ReplaceRdfNode(server.settings, triples, path, etag)
	}

	// create new node, no need to test etag
	node, err := ldp.NewRdfNode(server.settings, triples, path)
	if err != nil {
		return ldp.Node{}, err
	}

	if !isRootNode {
		container, err := server.getContainer(parentPath)
		if err != nil {
			return ldp.Node{}, err
		}

		if err := container.AddChild(node); err != nil {
			return ldp.Node{}, err
		}
	}

	return node, nil
}

// POST
func (server Server) CreateRdfSource(triples string, parentPath string, slug string) (ldp.Node, error) {
	container, err := server.getContainer(parentPath)
	if err != nil {
		return ldp.Node{}, err
	}

	newPath, err := server.getNewPath(slug)
	if err != nil {
		return ldp.Node{}, err
	}

	// TODO: Allow overwriting of resources on PUT.
	//       Need to figure out the ramifications of overwriting
	//       a container (e.g. what happens to contained objects?)
	//       or overwriting an RDF Source with a Non-RDF source
	//       (or viceversa)

	resource := server.createResource(parentPath, newPath)
	if resource.Error() != nil {

		if resource.Error() != textstore.AlreadyExistsError {
			return ldp.Node{}, resource.Error()
		}

		if slug == "" {
			// We generated a duplicate node.
			return ldp.Node{}, ldp.DuplicateNodeError
		}

		// The user provided slug is duplicated.
		// Let's try with one of our own.
		return server.CreateRdfSource(triples, parentPath, "")
	}

	path := util.UriConcat(parentPath, newPath)
	// log.Printf("== triples passed by the client: \r\n%s\r\n\r\n\r\n", triples)
	node, err := ldp.NewRdfNode(server.settings, triples, path)
	if err != nil {
		return ldp.Node{}, err
	}

	if err := container.AddChild(node); err != nil {
		return ldp.Node{}, err
	}
	return node, nil
}

func (server Server) CreateNonRdfSource(reader io.ReadCloser, parentPath string, slug string) (ldp.Node, error) {
	container, err := server.getContainer(parentPath)
	if err != nil {
		return ldp.Node{}, err
	}

	newPath, err := server.getNewPath(slug)
	if err != nil {
		return ldp.Node{}, err
	}

	newResource := true
	resource := server.createResource(parentPath, newPath)
	if resource.Error() != nil {
		if resource.Error() == textstore.AlreadyExistsError {
			node, err := ldp.GetHead(server.settings, newPath)
			if err != nil {
				return ldp.Node{}, errors.New("Cannot validate resource to overwrite")
			} else if node.IsRdf() {
				return ldp.Node{}, errors.New("Cannot overwrite RDF Source with Non-RDF Source")
			}
			newResource = false
		} else {
			return ldp.Node{}, resource.Error()
		}
	}

	node, err := ldp.NewNonRdfNode(server.settings, reader, parentPath, newPath)
	if err != nil {
		return node, err
	}

	if newResource {
		if err := container.AddChild(node); err != nil {
			return node, err
		}
	}

	return node, nil
}

func (server Server) PatchNode(path string, triples string) error {
	node, err := ldp.GetNode(server.settings, path)
	if err != nil {
		return err
	}
	return node.Patch(triples)
}

func (server Server) getNewPath(slug string) (string, error) {
	if slug == "" {
		// Generate a new server URI (e.g. node34)
		return MintNextUri(defaultSlug, server.minter), nil
	}

	if !util.IsValidSlug(slug) {
		errorMsg := fmt.Sprintf("Invalid Slug received (%s). Slug must not include special characters.", slug)
		return "", errors.New(errorMsg)
	}
	return slug, nil
}

func (server Server) createResource(parentPath string, newPath string) textstore.Store {
	path := util.UriConcat(parentPath, newPath)
	return server.createResourceFromPath(path)
}

func (server Server) createResourceFromPath(path string) textstore.Store {
	pathOnDisk := util.PathConcat(server.settings.DataPath(), path)
	// Queue up the creation of a new resource
	go func(pathOnDisk string) {
		server.nextResource <- textstore.CreateStore(pathOnDisk)
	}(pathOnDisk)

	// Wait for the new resource to be available.
	resource := <-server.nextResource
	return resource
}

func (server Server) getContainer(path string) (ldp.Node, error) {

	if isRootPath(path) {
		// Shortcut. We know for sure this is a container
		return ldp.GetHead(server.settings, "/")
	}

	node, err := ldp.GetNode(server.settings, path)
	if err != nil {
		return node, err
	} else if !node.IsBasicContainer() {
		errorMsg := fmt.Sprintf("%s is not a container", path)
		return node, errors.New(errorMsg)
	}
	return node, nil
}

func (server Server) getContainerUri(parentPath string) (string, error) {
	if isRootPath(parentPath) {
		return server.settings.RootUri(), nil
	}

	// Make sure the parent node exists and it's a container
	parentNode, err := ldp.GetNode(server.settings, parentPath)
	if err != nil {
		return "", err
	} else if !parentNode.IsBasicContainer() {
		return "", errors.New("Parent is not a container")
	}
	return parentNode.Uri(), nil
}

func isRootPath(path string) bool {
	return path == "" || path == "/"
}
<commit_msg>Removed code duplication<commit_after>package server

import (
	"errors"
	"fmt"
	"io"
	"ldpserver/ldp"
	"ldpserver/textstore"
	"ldpserver/util"
	// "log"
)

const defaultSlug string = "node"

type Server struct {
	settings ldp.Settings
	minter   chan string
	// this should use an interface to it's not tied to "textStore"
	nextResource chan textstore.Store
}

func NewServer(rootUri string, dataPath string) Server {
	var server Server
	server.settings = ldp.SettingsNew(rootUri, dataPath)
	ldp.CreateRoot(server.settings)
	server.minter = CreateMinter(server.settings.IdFile())
	server.nextResource = make(chan textstore.Store)
	return server
}

func (server Server) GetNode(path string) (ldp.Node, error) {
	return ldp.GetNode(server.settings, path)
}

func (server Server) GetHead(path string) (ldp.Node, error) {
	return ldp.GetHead(server.settings, path)
}

// PUT
func (server Server) ReplaceRdfSource(triples string, parentPath string, slug string, etag string) (ldp.Node, error) {
	path, err := server.getNewPath(parentPath, slug)
	if err != nil {
		return ldp.Node{}, err
	}

	resource := server.createResource(path)
	if resource.Error() != nil && resource.Error() != textstore.AlreadyExistsError {
		return ldp.Node{}, resource.Error()
	}

	if resource.Error() == textstore.AlreadyExistsError {
		// Replace existing node
		return ldp.ReplaceRdfNode(server.settings, triples, path, etag)
	}

	// Create new node
	node, err := ldp.NewRdfNode(server.settings, triples, path)
	if err != nil {
		return ldp.Node{}, err
	}

	if path != "/" {
		err = server.addNodeToContainer(node, parentPath)
	}

	return node, err
}

// POST
func (server Server) CreateRdfSource(triples string, parentPath string, slug string) (ldp.Node, error) {
	path, err := server.getNewPath(parentPath, slug)
	if err != nil {
		return ldp.Node{}, err
	}

	resource := server.createResource(path)
	if resource.Error() != nil && resource.Error() != textstore.AlreadyExistsError {
		return ldp.Node{}, resource.Error()
	}

	if resource.Error() == textstore.AlreadyExistsError {
		if slug == "" {
			// We generated a duplicate node.
			return ldp.Node{}, ldp.DuplicateNodeError
		}

		// The user provided slug is duplicated.
		// Let's try with one of our own.
		return server.CreateRdfSource(triples, parentPath, "")
	}

	// Create new node
	node, err := ldp.NewRdfNode(server.settings, triples, path)
	if err != nil {
		return ldp.Node{}, err
	}

	if path != "/" {
		err = server.addNodeToContainer(node, parentPath)
	}

	return node, err
}

func (server Server) CreateNonRdfSource(reader io.ReadCloser, parentPath string, slug string) (ldp.Node, error) {
	container, err := server.getContainer(parentPath)
	if err != nil {
		return ldp.Node{}, err
	}

	newPath, err := server.getNewPath(parentPath, slug)
	if err != nil {
		return ldp.Node{}, err
	}

	newResource := true
	path := util.UriConcat(parentPath, newPath)
	resource := server.createResource(path)
	if resource.Error() != nil {
		if resource.Error() == textstore.AlreadyExistsError {
			node, err := ldp.GetHead(server.settings, newPath)
			if err != nil {
				return ldp.Node{}, errors.New("Cannot validate resource to overwrite")
			} else if node.IsRdf() {
				return ldp.Node{}, errors.New("Cannot overwrite RDF Source with Non-RDF Source")
			}
			newResource = false
		} else {
			return ldp.Node{}, resource.Error()
		}
	}

	node, err := ldp.NewNonRdfNode(server.settings, reader, parentPath, newPath)
	if err != nil {
		return node, err
	}

	if newResource {
		if err := container.AddChild(node); err != nil {
			return node, err
		}
	}

	return node, nil
}

func (server Server) PatchNode(path string, triples string) error {
	node, err := ldp.GetNode(server.settings, path)
	if err != nil {
		return err
	}
	return node.Patch(triples)
}

func (server Server) addNodeToContainer(node ldp.Node, path string) error {
	container, err := server.getContainer(path)
	if err != nil {
		return err
	}
	return container.AddChild(node)
}

func (server Server) getNewPath(parentPath string, slug string) (string, error) {
	isRootNode := (parentPath == ".") && (slug == ".")
	if isRootNode {
		// special case
		return "/", nil
	}

	if slug == "" {
		// Generate a new server URI (e.g. node34)
		slug = MintNextUri(defaultSlug, server.minter)
	}

	if !util.IsValidSlug(slug) {
		return "", fmt.Errorf("Invalid Slug received (%s)", slug)
	}
	return util.UriConcat(parentPath, slug), nil
}

func (server Server) createResource(path string) textstore.Store {
	pathOnDisk := util.PathConcat(server.settings.DataPath(), path)
	// Queue up the creation of a new resource
	go func(pathOnDisk string) {
		server.nextResource <- textstore.CreateStore(pathOnDisk)
	}(pathOnDisk)

	// Wait for the new resource to be available.
	resource := <-server.nextResource
	return resource
}

func (server Server) getContainer(path string) (ldp.Node, error) {

	if isRootPath(path) {
		// Shortcut. We know for sure this is a container
		return ldp.GetHead(server.settings, "/")
	}

	node, err := ldp.GetNode(server.settings, path)
	if err != nil {
		return node, err
	} else if !node.IsBasicContainer() {
		errorMsg := fmt.Sprintf("%s is not a container", path)
		return node, errors.New(errorMsg)
	}
	return node, nil
}

func (server Server) getContainerUri(parentPath string) (string, error) {
	if isRootPath(parentPath) {
		return server.settings.RootUri(), nil
	}

	// Make sure the parent node exists and it's a container
	parentNode, err := ldp.GetNode(server.settings, parentPath)
	if err != nil {
		return "", err
	} else if !parentNode.IsBasicContainer() {
		return "", errors.New("Parent is not a container")
	}
	return parentNode.Uri(), nil
}

func isRootPath(path string) bool {
	return path == "" || path == "/"
}
<|endoftext|>


<commit_before>package main

import (
	// General
	"github.com/golang/glog"
	"flag"
	"github.com/iambc/xerrors"
	"reflect"
	"os"
	"strconv"
	_ "github.com/gorilla/mux"

	//API
	"net/http"
	"encoding/json"

	//DB
	"database/sql"
	_ "github.com/lib/pq"
)

var dbh *sql.DB
var dbConnString string

func main() {
    flag.Parse()
    var err error
    dbh, err = sql.Open("postgres", dbConnString)
    if err != nil {
	glog.Fatal("Connection to the database has failed")
    }
    dbConnString = os.Getenv("ABC_DB_CONN_STRING")

    go http.ListenAndServe(":"+os.Getenv("ABC_FILES_SERVER_URL"), http.FileServer(http.Dir(os.Getenv("ABC_FILES_DIR"))))
    http.HandleFunc("/api", Handler)
    http.ListenAndServe(`:`+ os.Getenv("ABC_SERVER_ENDPOINT_URL"), nil)
}

func Handler(res http.ResponseWriter, req *http.Request){
    values := req.URL.Query()
    command, isPassed := values[`command`]
    if !isPassed {
	res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'command' is undefined.","Payload":null}`))
	return
    }

    var resp []byte
    var err error
    if(command[0] == `getBoards` ) {
	apiKey, isPassed := values[`api_key`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'api_key' is undefined.","Payload":null}`))
	    return
	}
	resp, err = getBoards(apiKey[0])

    } else if(command[0] == `getActiveThreadsForBoard`) {
	apiKey, isPassed := values[`api_key`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'api_key' is undefined.","Payload":null}`))
	    return
	}
	boardId, isPassed := values[`board_id`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'board_id' is undefined.","Payload":null}`))
	    return
	}
	boardIdInt, err := strconv.Atoi(boardId[0])
	if err != nil {
	    res.Write([]byte(`{"Status":"error","Msg":"Wrong value for parameter board_id","Payload":null}`))
	    return
	}
	resp, err = getActiveThreadsForBoard(apiKey[0], boardIdInt)

    } else if(command[0] == `getPostsForThread`) {
	apiKey, isPassed := values[`api_key`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'api_key' is undefined.","Payload":null}`))
	    return
	}
	threadId, isPassed := values[`thread_id`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'board_id' is undefined.","Payload":null}`))
	    return
	}
	threadIdInt, err := strconv.Atoi(threadId[0])
	if err != nil {
	    res.Write([]byte(`{"Status":"error","Msg":"Wrong value for parameter board_id","Payload":null}`))
	    return
	}
	resp, err = getPostsForThread(apiKey[0], threadIdInt)

    } else if(command[0] == `addPostToThread`) {
	threadId, isPassed := values[`thread_id`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'board_id' is undefined.","Payload":null}`))
	    return
	}
	threadIdInt, err := strconv.Atoi(threadId[0])
	if err != nil {
	    res.Write([]byte(`{"Status":"error","Msg":"Wrong value for parameter board_id","Payload":null}`))
	    return
	}
	threadBodyPost, isPassed := values[`thread_body_post`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'thread_body_post' is undefined.","Payload":null}`))
	    return
	}
	attachmentUrl, isPassed := values[`attachment_id`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'attachment_id' is undefined.","Payload":null}`))
	    return
	}
	clientRemoteAddr, isPassed := values[`clientRemoteAddr`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'clientRemoteAddr' is undefined.","Payload":null}`))
	    return
	}
	resp, err = addPostToThread(threadIdInt, threadBodyPost[0], &attachmentUrl[0], clientRemoteAddr[0])

    } else if(command[0] == `addThread`) {
	boardId, isPassed := values[`board_id`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'board_id' is undefined.","Payload":null}`))
	    return
	}
	boardIdInt, err := strconv.Atoi(boardId[0])
	if err != nil {
	    res.Write([]byte(`{"Status":"error","Msg":"Wrong value for parameter board_id","Payload":null}`))
	    return
	}
	threadName, isPassed := values[`thread_name`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'thread_name' is undefined.","Payload":null}`))
	    return
	}
	resp, err = addThread(boardIdInt, threadName[0])

    } else {
	res.Write([]byte(`{"Status":"error","Msg":"No such command exists.","Payload":null}`))
	glog.Error("command: ", command[0])
    }

    res.Header().Set("Access-Control-Allow-Origin", "*")
    if err != nil{
	if string(reflect.TypeOf(err).Name())  == `XError` {
	    res.Write([]byte(`{"Status":"`+ err.(xerrors.XError).Code +`","Msg":"` + err.Error()  +`","Payload":null}`))
	} else {
	    res.Write([]byte(`{"Status":"000","Msg":"Application Error!","Payload":null}`))
	}
	glog.Error(err)
	    return
    }
    res.Write(resp)

}

func getBoards(apiKey string)  ([]byte, error) {

    rows, err := dbh.Query("select b.id, b.name, b.descr from boards b join image_board_clusters ibc on ibc.id = b.image_board_cluster_id where api_key = $1;", apiKey)
    if err != nil {
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `002`, true)
    }
    defer rows.Close()

    var curr_boards []boards
    for rows.Next() {
	var board boards
	err = rows.Scan(&board.Id, &board.Name, &board.Descr)
	if err != nil {
	    return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `003`, true)
	}
	curr_boards = append(curr_boards, board)
    }
    bytes, err1 := json.Marshal(api_request{"ok", nil, &curr_boards})
    if err1 != nil {
	return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `004`, true)
    }
    return bytes, nil
}

func getActiveThreadsForBoard(apiKey string, boardId int)  ([]byte, error) {

    rows, err := dbh.Query(`select t.id, t.name, count(*), (select count(*) from thread_posts where thread_id = t.id and attachment_url is not null) from threads t
				join boards b on b.id = t.board_id
				join image_board_clusters ibc on ibc.id = b.image_board_cluster_id
				left join thread_posts tp on tp.thread_id = t.id
			    where t.is_active = TRUE and t.board_id = $1 and ibc.api_key = $2 group by 1,2 order by t.id;`, boardId, apiKey)
    if err != nil {
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `006`, true)
    }
    defer rows.Close()

    var active_threads []threads
    for rows.Next() {
	glog.Info("Popped new thread")
        var thread threads
        err = rows.Scan(&thread.Id, &thread.Name, &thread.PostCount, &thread.PostCountWithAttachment)
        if err != nil {
            return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `007`, true)
        }
        active_threads = append(active_threads, thread)
    }
    var bytes []byte
    var err1 error
    if(len(active_threads) == 0){
        errMsg := "No objects returned."
        bytes, err1 = json.Marshal(api_request{"error", &errMsg, &active_threads})
    }else {
        bytes, err1 = json.Marshal(api_request{"ok", nil, &active_threads})
    }

    if err1 != nil {
        return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `008`, true)
    }

    return bytes, nil
}

func getPostsForThread(apiKey string, threadId int)  ([]byte, error) {
    rows, err := dbh.Query(`select tp.id, tp.body, tp.attachment_url, tp.inserted_at, tp.source_ip
			    from thread_posts tp join threads t on t.id = tp.thread_id
						 join boards b on b.id = t.board_id
						 join image_board_clusters ibc on ibc.id = b.image_board_cluster_id
			    where tp.thread_id = $1 and ibc.api_key = $2 and t.is_active = true;`, threadId, apiKey)
    if err != nil {
	glog.Error(err)
        return []byte{}, xerrors.NewSysErr()
    }
    defer rows.Close()

    var curr_posts []thread_posts
    for rows.Next() {
	glog.Info("new post for thread with id: ", threadId)
        var curr_post thread_posts
        err = rows.Scan(&curr_post.Id, &curr_post.Body, &curr_post.AttachmentUrl, &curr_post.InsertedAt, &curr_post.SourceIp)
        if err != nil {
	    glog.Error(err)
            return []byte{}, xerrors.NewSysErr()
        }
        curr_posts = append(curr_posts, curr_post)
    }

    var bytes []byte
    var err1 error
    if(len(curr_posts) == 0){
	errMsg := "No objects returned."
	bytes, err1 = json.Marshal(api_request{"error", &errMsg, &curr_posts})
    }else {
	bytes, err1 = json.Marshal(api_request{"ok", nil, &curr_posts})
    }

    if err1 != nil {
        return []byte{}, xerrors.NewSysErr()
    }

    return bytes, nil
}

func addPostToThread(threadId int, threadBodyPost string, attachmentUrl *string, clientRemoteAddr string) ([]byte,error) {
    var is_limit_reached bool
    var max_post_length int
    var min_post_length int
    err := dbh.QueryRow("select (select count(*) from thread_posts  where thread_id = $1) > max_posts_per_thread, min_post_length, max_post_length  from threads where id = $1;", threadId).Scan(&is_limit_reached, &min_post_length, &max_post_length)
    if err != nil {
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `009`, true)
    }

    if is_limit_reached {
	dbh.QueryRow("UPDATE threads set is_active = false where id = $1", threadId).Scan()
	return []byte{}, xerrors.NewUIErr(`Thread post limit reached!`, `Thread post limit reached!`, `010`, true)
    }

   if(min_post_length > len(threadBodyPost)  && min_post_length != -1){
	return []byte{}, xerrors.NewUIErr(`Post length is less than minimum length!`, `Post length is less than minimum length! post length: ` + strconv.Itoa(len(threadBodyPost))  +` min length: ` + strconv.Itoa(min_post_length) , `020`, false)
    }
   if(max_post_length < len(threadBodyPost)  && max_post_length != -1){
	return []byte{}, xerrors.NewUIErr(`Post length is more than maximum length!`, `Post length is more than maximum length! post length: ` + strconv.Itoa(len(threadBodyPost))  +` max length: ` + strconv.Itoa(max_post_length) , `021`, false)
    }

    _, err = dbh.Query("INSERT INTO thread_posts(body, thread_id, attachment_url, source_ip) VALUES($1, $2, $3, $4)", threadBodyPost, threadId, attachmentUrl, clientRemoteAddr)

    if err != nil {
	glog.Error(err)
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `011`, true)
    }

    bytes, err1 := json.Marshal(api_request{"ok", nil, nil})
    if err1 != nil {
        return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `012`, true)
    }

    return bytes, nil
}

func addThread(boardId int, threadName string) ([]byte, error) {
    var is_limit_reached bool
    err := dbh.QueryRow("select (select count(*) from threads  where board_id = $1) > thread_setting_max_thread_count  from boards where id = $1;", boardId).Scan(&is_limit_reached)
    if err != nil {
	glog.Error("COULD NOT SELECT thread_count")
	return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `015`, true)
    }
    if is_limit_reached {
	return []byte{}, xerrors.NewUIErr(`Thread limit reached!`, `Thread limit reached!`, `016`, true)
    }

    var threadId int
    err = dbh.QueryRow("INSERT INTO threads(name, board_id, limits_reached_action_id, max_posts_per_thread) VALUES($1, $2, 1, 10)  RETURNING id, name", threadName, boardId).Scan(&threadId, &threadName)

    if err != nil {
	glog.Error("INSERT FAILED")
        return []byte{}, xerrors.NewUIErr(err.Error(), err.Error(), `017`, true)
    }

    a := struct{
		    Id int
		    Name string
		}{
		    threadId,
		    threadName,
		}
    bytes, err1 := json.Marshal(api_request{`ok`, nil, a })
    if err1 != nil {
        return []byte{}, xerrors.NewUIErr(err1.Error(), err1.Error(), `018`, true)
    }

    return bytes, nil
}

<commit_msg>refactor: removed api methods<commit_after>package main

import (
	// General
	"github.com/golang/glog"
	"flag"
	"github.com/iambc/xerrors"
	"reflect"
	"os"
	"strconv"
	_ "github.com/gorilla/mux"
	"net/http"

	//DB
	"database/sql"
	_ "github.com/lib/pq"
)

var dbh *sql.DB
var dbConnString string

func main() {
    flag.Parse()
    var err error
    dbh, err = sql.Open("postgres", dbConnString)
    if err != nil {
	glog.Fatal("Connection to the database has failed")
    }
    dbConnString = os.Getenv("ABC_DB_CONN_STRING")

    go http.ListenAndServe(":"+os.Getenv("ABC_FILES_SERVER_URL"), http.FileServer(http.Dir(os.Getenv("ABC_FILES_DIR"))))
    http.HandleFunc("/api", Handler)
    http.ListenAndServe(`:`+ os.Getenv("ABC_SERVER_ENDPOINT_URL"), nil)
}

func Handler(res http.ResponseWriter, req *http.Request){
    values := req.URL.Query()
    command, isPassed := values[`command`]
    if !isPassed {
	res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'command' is undefined.","Payload":null}`))
	return
    }

    var resp []byte
    var err error
    if(command[0] == `getBoards` ) {
	apiKey, isPassed := values[`api_key`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'api_key' is undefined.","Payload":null}`))
	    return
	}
	resp, err = getBoards(apiKey[0])

    } else if(command[0] == `getActiveThreadsForBoard`) {
	apiKey, isPassed := values[`api_key`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'api_key' is undefined.","Payload":null}`))
	    return
	}
	boardId, isPassed := values[`board_id`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'board_id' is undefined.","Payload":null}`))
	    return
	}
	boardIdInt, err := strconv.Atoi(boardId[0])
	if err != nil {
	    res.Write([]byte(`{"Status":"error","Msg":"Wrong value for parameter board_id","Payload":null}`))
	    return
	}
	resp, err = getActiveThreadsForBoard(apiKey[0], boardIdInt)

    } else if(command[0] == `getPostsForThread`) {
	apiKey, isPassed := values[`api_key`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'api_key' is undefined.","Payload":null}`))
	    return
	}
	threadId, isPassed := values[`thread_id`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'board_id' is undefined.","Payload":null}`))
	    return
	}
	threadIdInt, err := strconv.Atoi(threadId[0])
	if err != nil {
	    res.Write([]byte(`{"Status":"error","Msg":"Wrong value for parameter board_id","Payload":null}`))
	    return
	}
	resp, err = getPostsForThread(apiKey[0], threadIdInt)

    } else if(command[0] == `addPostToThread`) {
	threadId, isPassed := values[`thread_id`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'board_id' is undefined.","Payload":null}`))
	    return
	}
	threadIdInt, err := strconv.Atoi(threadId[0])
	if err != nil {
	    res.Write([]byte(`{"Status":"error","Msg":"Wrong value for parameter board_id","Payload":null}`))
	    return
	}
	threadBodyPost, isPassed := values[`thread_body_post`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'thread_body_post' is undefined.","Payload":null}`))
	    return
	}
	attachmentUrl, isPassed := values[`attachment_id`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'attachment_id' is undefined.","Payload":null}`))
	    return
	}
	clientRemoteAddr, isPassed := values[`clientRemoteAddr`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'clientRemoteAddr' is undefined.","Payload":null}`))
	    return
	}
	resp, err = addPostToThread(threadIdInt, threadBodyPost[0], &attachmentUrl[0], clientRemoteAddr[0])

    } else if(command[0] == `addThread`) {
	boardId, isPassed := values[`board_id`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'board_id' is undefined.","Payload":null}`))
	    return
	}
	boardIdInt, err := strconv.Atoi(boardId[0])
	if err != nil {
	    res.Write([]byte(`{"Status":"error","Msg":"Wrong value for parameter board_id","Payload":null}`))
	    return
	}
	threadName, isPassed := values[`thread_name`]
	if !isPassed {
	    res.Write([]byte(`{"Status":"error","Msg":"Paremeter 'thread_name' is undefined.","Payload":null}`))
	    return
	}
	resp, err = addThread(boardIdInt, threadName[0])

    } else {
	res.Write([]byte(`{"Status":"error","Msg":"No such command exists.","Payload":null}`))
	glog.Error("command: ", command[0])
    }

    res.Header().Set("Access-Control-Allow-Origin", "*")
    if err != nil{
	if string(reflect.TypeOf(err).Name())  == `XError` {
	    res.Write([]byte(`{"Status":"`+ err.(xerrors.XError).Code +`","Msg":"` + err.Error()  +`","Payload":null}`))
	} else {
	    res.Write([]byte(`{"Status":"000","Msg":"Application Error!","Payload":null}`))
	}
	glog.Error(err)
	    return
    }
    res.Write(resp)

}

<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"encoding/binary"
	"errors"
	"net"
	"sync"
)

type Server struct {
	conn        *net.UDPConn
	clients     map[int64]*Client
	clientMutex sync.Mutex
}

// Starts listening on specified address and returns a Server object
func Listen(address string) (*Server, error) {
	addr, _ := net.ResolveUDPAddr("udp", address)
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		return nil, errors.New("Failed to listen on " + addr.String())
	}

	server := &Server{
		conn:    conn,
		clients: make(map[int64]*Client),
	}

	return server, nil
}

func (s *Server) Run() (err error) {
	for {
		b := make([]byte, 1024)
		var n int
		var addr *net.UDPAddr

		n, addr, _ = s.conn.ReadFromUDP(b)
		// TODO: handle error

		if n < 16 { // If packet is smaller than 16 bytes we may ignore it
			continue
		}
		go s.handleRequest(addr, n, b)
	}
	return
}

func (s *Server) handleRequest(addr *net.UDPAddr, n int, b []byte) {
	var header requestHeader
	buf := bytes.NewReader(b)

	if err := binary.Read(buf, binary.BigEndian, &header); err != nil {
		return
	}

	switch header.Action {

	case actionConnect:
		var req connectRequest
		if err := binary.Read(buf, binary.BigEndian, &req); err != nil {
			return
		}
		s.handleConnectRequest(addr, header, req)

	case actionAnnounce:
		var req announceRequest
		if err := binary.Read(buf, binary.BigEndian, &req); err != nil {
			return
		}
		s.handleAnnounceRequest(addr, header, req)
	}

}

func (s *Server) handleConnectRequest(addr *net.UDPAddr, header requestHeader, req connectRequest) {
	response := connectResponse{
		Action:        actionConnect,
		TransactionId: req.TransactionId,
		ConnectionId:  header.ConnectionId,
	}
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.BigEndian, response)
	if n, err := s.conn.WriteToUDP(buf.Bytes(), addr); err != nil || n != 16 {
		return
	}
	c := &Client{
		Addr: addr,
	}
	s.addClient(c)
}

func (s *Server) handleAnnounceRequest(addr *net.UDPAddr, header requestHeader, req announceRequest) {

}

func (s *Server) addClient(c *Client) {
	s.clientMutex.Lock()
	s.clients[c.Id] = c
	s.clientMutex.Unlock()
}

func (s *Server) removeClient(id int64) {
	s.clientMutex.Lock()
	delete(s.clients, id)
	s.clientMutex.Unlock()
}

func (s *Server) Addr() *net.UDPAddr {
	return s.conn.LocalAddr().(*net.UDPAddr)
}

func (s *Server) Close() error {
	return s.conn.Close()
}
<commit_msg>Check for correct magic number in connect request and respond with random connection id<commit_after>package server

import (
	"bytes"
	"encoding/binary"
	"errors"
	"math/rand"
	"net"
	"sync"
	"time"
)

type Server struct {
	conn        *net.UDPConn
	clients     map[int64]*Client
	clientMutex sync.Mutex
}

// Starts listening on specified address and returns a Server object
func Listen(address string) (*Server, error) {
	addr, _ := net.ResolveUDPAddr("udp", address)
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		return nil, errors.New("Failed to listen on " + addr.String())
	}

	server := &Server{
		conn:    conn,
		clients: make(map[int64]*Client),
	}

	return server, nil
}

func (s *Server) Run() (err error) {
	for {
		b := make([]byte, 1024)
		var n int
		var addr *net.UDPAddr

		n, addr, _ = s.conn.ReadFromUDP(b)
		// TODO: handle error

		if n < 16 { // If packet is smaller than 16 bytes we may ignore it
			continue
		}
		go s.handleRequest(addr, n, b)
	}
	return
}

func (s *Server) handleRequest(addr *net.UDPAddr, n int, b []byte) {
	var header requestHeader
	buf := bytes.NewReader(b)

	if err := binary.Read(buf, binary.BigEndian, &header); err != nil {
		return
	}

	switch header.Action {

	case actionConnect:
		var req connectRequest
		if err := binary.Read(buf, binary.BigEndian, &req); err != nil {
			return
		}
		s.handleConnectRequest(addr, header, req)

	case actionAnnounce:
		var req announceRequest
		if err := binary.Read(buf, binary.BigEndian, &req); err != nil {
			return
		}
		s.handleAnnounceRequest(addr, header, req)
	}

}

func (s *Server) handleConnectRequest(addr *net.UDPAddr, header requestHeader, req connectRequest) {
	if header.ConnectionId != 0x41727101980 {
		return
	}
	response := connectResponse{
		Action:        actionConnect,
		TransactionId: req.TransactionId,
		ConnectionId:  header.ConnectionId,
	}
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.BigEndian, response)
	if n, err := s.conn.WriteToUDP(buf.Bytes(), addr); err != nil || n != 16 {
		return
	}
	c := &Client{
		Addr: addr,
		Id:   rand.Int63(),
	}
	s.addClient(c)
}

func (s *Server) handleAnnounceRequest(addr *net.UDPAddr, header requestHeader, req announceRequest) {

}

func (s *Server) addClient(c *Client) {
	s.clientMutex.Lock()
	s.clients[c.Id] = c
	s.clientMutex.Unlock()
}

func (s *Server) removeClient(id int64) {
	s.clientMutex.Lock()
	delete(s.clients, id)
	s.clientMutex.Unlock()
}

func (s *Server) Addr() *net.UDPAddr {
	return s.conn.LocalAddr().(*net.UDPAddr)
}

func (s *Server) Close() error {
	return s.conn.Close()
}

func init() {
	rand.Seed(time.Now().UnixNano())
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Jesse van den Kieboom. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following disclaimer
 *      in the documentation and/or other materials provided with the
 *      distribution.
 *    * Neither the name of Google Inc. nor the names of its
 *      contributors may be used to endorse or promote products derived from
 *      this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
	"fmt"
	"net/http"
	"os"
	"path"
	"strings"
	"time"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/jessevdk/go-flags"
)

type Options struct {
	Listen         string   `short:"l" long:"listen" description:"The address to listen on" default:":8000"`
	Data           string   `short:"d" long:"data" description:"Root of the data directory" default:"data"`
	SiteData       string   `short:"s" long:"site-data" description:"Root of the site data directory" default:"site"`
	CORSDomain     []string `short:"c" long:"cors-domain" description:"An external domain for which to allow cross-requests"`
	SMTPAddress    string   `short:"e" long:"smtp-address" description:"The address (hostname[:port]) of the SMTP server" default:"localhost:25"`
	SMTPDisableTLS bool     `short:"t" long:"smtp-disable-tls" description:"Disable TLS when connecting to the smtp server"`
	PublicHost     string   `short:"p" long:"public-host" description:"The public playground host address (e.g. http://webgl.example.com/)" default:"http://localhost:8000/"`

	CORSDomainMap map[string]bool
}

var router = mux.NewRouter()
var options Options
var dataRoot string
var siteRoot string

func absPath(p string) string {
	if !path.IsAbs(p) {
		return path.Join(path.Dir(os.Args[0]), p)
	}

	return p
}

type LimitedRequestHandler struct {
}

func (l LimitedRequestHandler) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
	// Limit requests to 2MB
	req.Body = http.MaxBytesReader(wr, req.Body, 1<<21)
	router.ServeHTTP(wr, req)
}

type HandlerWrappers int

const (
	WrapNone HandlerWrappers = 1 << iota
	WrapCORS
	WrapCompress
)

func MakeHandler(h interface{}, wrap HandlerWrappers) http.Handler {
	var hh http.Handler

	if rest, ok := h.(Restish); ok {
		hh = NewRestishHandler(rest)
	} else {
		hh = h.(http.Handler)
	}

	if wrap&WrapCORS != 0 {
		hh = CORSHandler(hh)
	}

	if wrap&WrapCompress != 0 {
		hh = handlers.CompressHandler(hh)
	}

	return hh
}

func main() {
	if _, err := flags.Parse(&options); err != nil {
		os.Exit(1)
	}

	if !strings.ContainsRune(options.SMTPAddress, ':') {
		options.SMTPAddress += ":25"
	}

	options.CORSDomainMap = make(map[string]bool)

	for _, domain := range options.CORSDomain {
		options.CORSDomainMap[domain] = true
	}

	dataRoot = absPath(options.Data)

	if options.SiteData != "-" {
		siteRoot = absPath(options.SiteData)

		router.PathPrefix("/assets/").Handler(MakeHandler(http.FileServer(http.Dir(siteRoot)), WrapCompress))
		router.PathPrefix("/").Handler(MakeHandler(NewRestishHandler(SiteHandler{}), WrapCompress))
	}

	db.Open()

	srv := &http.Server{
		Addr:           options.Listen,
		Handler:        LimitedRequestHandler{},
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	if err := srv.ListenAndServe(); err != nil {
		fmt.Fprintf(os.Stderr, "Error while listening: %s\n", err)
		os.Exit(1)
	}
}
<commit_msg>Allow listening with TLS<commit_after>/*
 * Copyright (c) 2014 Jesse van den Kieboom. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following disclaimer
 *      in the documentation and/or other materials provided with the
 *      distribution.
 *    * Neither the name of Google Inc. nor the names of its
 *      contributors may be used to endorse or promote products derived from
 *      this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
	"fmt"
	"net/http"
	"os"
	"path"
	"strings"
	"time"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/jessevdk/go-flags"
)

type Options struct {
	Listen         string   `short:"l" long:"listen" description:"The address to listen on" default:":8000"`
	Data           string   `short:"d" long:"data" description:"Root of the data directory" default:"data"`
	SiteData       string   `short:"s" long:"site-data" description:"Root of the site data directory" default:"site"`
	CORSDomain     []string `short:"c" long:"cors-domain" description:"An external domain for which to allow cross-requests"`
	SMTPAddress    string   `short:"e" long:"smtp-address" description:"The address (hostname[:port]) of the SMTP server" default:"localhost:25"`
	SMTPDisableTLS bool     `short:"t" long:"smtp-disable-tls" description:"Disable TLS when connecting to the smtp server"`
	PublicHost     string   `short:"p" long:"public-host" description:"The public playground host address (e.g. http://webgl.example.com/)" default:"http://localhost:8000/"`
	SSLCert        string   `long:"ssl-cert" description:"SSL certificate file"`
	SSLKey         string   `long:"ssl-key" description:"SSL key file"`

	CORSDomainMap map[string]bool
}

var router = mux.NewRouter()
var options Options
var dataRoot string
var siteRoot string

func absPath(p string) string {
	if !path.IsAbs(p) {
		return path.Join(path.Dir(os.Args[0]), p)
	}

	return p
}

type LimitedRequestHandler struct {
}

func (l LimitedRequestHandler) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
	// Limit requests to 2MB
	req.Body = http.MaxBytesReader(wr, req.Body, 1<<21)
	router.ServeHTTP(wr, req)
}

type HandlerWrappers int

const (
	WrapNone HandlerWrappers = 1 << iota
	WrapCORS
	WrapCompress
)

func MakeHandler(h interface{}, wrap HandlerWrappers) http.Handler {
	var hh http.Handler

	if rest, ok := h.(Restish); ok {
		hh = NewRestishHandler(rest)
	} else {
		hh = h.(http.Handler)
	}

	if wrap&WrapCORS != 0 {
		hh = CORSHandler(hh)
	}

	if wrap&WrapCompress != 0 {
		hh = handlers.CompressHandler(hh)
	}

	return hh
}

func main() {
	if _, err := flags.Parse(&options); err != nil {
		os.Exit(1)
	}

	if !strings.ContainsRune(options.SMTPAddress, ':') {
		options.SMTPAddress += ":25"
	}

	options.CORSDomainMap = make(map[string]bool)

	for _, domain := range options.CORSDomain {
		options.CORSDomainMap[domain] = true
	}

	dataRoot = absPath(options.Data)

	if options.SiteData != "-" {
		siteRoot = absPath(options.SiteData)

		router.PathPrefix("/assets/").Handler(MakeHandler(http.FileServer(http.Dir(siteRoot)), WrapCompress))
		router.PathPrefix("/").Handler(MakeHandler(NewRestishHandler(SiteHandler{}), WrapCompress))
	}

	db.Open()

	srv := &http.Server{
		Addr:           options.Listen,
		Handler:        LimitedRequestHandler{},
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	var err error

	if options.SSLCert != "" && options.SSLKey != "" {
		err = srv.ListenAndServeTLS(options.SSLCert, options.SSLKey)
	} else {
		err = srv.ListenAndServe()
	}

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error while listening: %s\n", err)
		os.Exit(1)
	}
}
<|endoftext|>