<commit_before>package services

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"

	datatypes "github.com/maximilien/softlayer-go/data_types"
	softlayer "github.com/maximilien/softlayer-go/softlayer"
)

type softLayer_Virtual_Guest_Block_Device_Template_Group_Service struct {
	client softlayer.Client
}

func NewSoftLayer_Virtual_Guest_Block_Device_Template_Group_Service(client softlayer.Client) *softLayer_Virtual_Guest_Block_Device_Template_Group_Service {
	return &softLayer_Virtual_Guest_Block_Device_Template_Group_Service{
		client: client,
	}
}

func (slvgs *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) GetName() string {
	return "SoftLayer_Virtual_Guest_Block_Device_Template_Group"
}

func (slvgbdtg *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) GetObject(id int) (datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group, error) {
	response, err := slvgbdtg.client.DoRawHttpRequest(fmt.Sprintf("%s/%d/getObject.json", slvgbdtg.GetName(), id), "GET", new(bytes.Buffer))
	if err != nil {
		return datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group{}, err
	}

	vgbdtGroup := datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group{}
	err = json.Unmarshal(response, &vgbdtGroup)
	if err != nil {
		return datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group{}, err
	}

	return vgbdtGroup, nil
}

func (slvgbdtg *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) DeleteObject(id int) (bool, error) {
	response, err := slvgbdtg.client.DoRawHttpRequest(fmt.Sprintf("%s/%d.json", slvgbdtg.GetName(), id), "DELETE", new(bytes.Buffer))

	if res := string(response[:]); res != "true" {
		return false, errors.New(fmt.Sprintf("Failed to delete instance with id '%d', got '%s' as response from the API.", id, res))
	}

	return true, err
}

func (slvgbdtg *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) GetDatacenters(id int) ([]datatypes.SoftLayer_Location, error) {
	response, err := slvgbdtg.client.DoRawHttpRequest(fmt.Sprintf("%s/%d/getDatacenters.json", slvgbdtg.GetName(), id), "GET", new(bytes.Buffer))
	if err != nil {
		return []datatypes.SoftLayer_Location{}, err
	}

	locations := []datatypes.SoftLayer_Location{}
	err = json.Unmarshal(response, &locations)
	if err != nil {
		return []datatypes.SoftLayer_Location{}, err
	}

	return locations, nil
}

func (slvgbdtg *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) GetSshKeys(id int) ([]datatypes.SoftLayer_Security_Ssh_Key, error) {
	response, err := slvgbdtg.client.DoRawHttpRequest(fmt.Sprintf("%s/%d/getSshKeys.json", slvgbdtg.GetName(), id), "GET", new(bytes.Buffer))
	if err != nil {
		return []datatypes.SoftLayer_Security_Ssh_Key{}, err
	}

	sshKeys := []datatypes.SoftLayer_Security_Ssh_Key{}
	err = json.Unmarshal(response, &sshKeys)
	if err != nil {
		return []datatypes.SoftLayer_Security_Ssh_Key{}, err
	}

	return sshKeys, nil
}

func (slvgbdtg *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) GetStatus(id int) (datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group_Status, error) {
	//TODO: complete me!
	return datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group_Status{}, nil
}
<commit_msg>added implementation of SLVGBDTG#getStatus<commit_after>package services

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"

	datatypes "github.com/maximilien/softlayer-go/data_types"
	softlayer "github.com/maximilien/softlayer-go/softlayer"
)

type softLayer_Virtual_Guest_Block_Device_Template_Group_Service struct {
	client softlayer.Client
}

func NewSoftLayer_Virtual_Guest_Block_Device_Template_Group_Service(client softlayer.Client) *softLayer_Virtual_Guest_Block_Device_Template_Group_Service {
	return &softLayer_Virtual_Guest_Block_Device_Template_Group_Service{
		client: client,
	}
}

func (slvgs *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) GetName() string {
	return "SoftLayer_Virtual_Guest_Block_Device_Template_Group"
}

func (slvgbdtg *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) GetObject(id int) (datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group, error) {
	response, err := slvgbdtg.client.DoRawHttpRequest(fmt.Sprintf("%s/%d/getObject.json", slvgbdtg.GetName(), id), "GET", new(bytes.Buffer))
	if err != nil {
		return datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group{}, err
	}

	vgbdtGroup := datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group{}
	err = json.Unmarshal(response, &vgbdtGroup)
	if err != nil {
		return datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group{}, err
	}

	return vgbdtGroup, nil
}

func (slvgbdtg *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) DeleteObject(id int) (bool, error) {
	response, err := slvgbdtg.client.DoRawHttpRequest(fmt.Sprintf("%s/%d.json", slvgbdtg.GetName(), id), "DELETE", new(bytes.Buffer))

	if res := string(response[:]); res != "true" {
		return false, errors.New(fmt.Sprintf("Failed to delete instance with id '%d', got '%s' as response from the API.", id, res))
	}

	return true, err
}

func (slvgbdtg *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) GetDatacenters(id int) ([]datatypes.SoftLayer_Location, error) {
	response, err := slvgbdtg.client.DoRawHttpRequest(fmt.Sprintf("%s/%d/getDatacenters.json", slvgbdtg.GetName(), id), "GET", new(bytes.Buffer))
	if err != nil {
		return []datatypes.SoftLayer_Location{}, err
	}

	locations := []datatypes.SoftLayer_Location{}
	err = json.Unmarshal(response, &locations)
	if err != nil {
		return []datatypes.SoftLayer_Location{}, err
	}

	return locations, nil
}

func (slvgbdtg *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) GetSshKeys(id int) ([]datatypes.SoftLayer_Security_Ssh_Key, error) {
	response, err := slvgbdtg.client.DoRawHttpRequest(fmt.Sprintf("%s/%d/getSshKeys.json", slvgbdtg.GetName(), id), "GET", new(bytes.Buffer))
	if err != nil {
		return []datatypes.SoftLayer_Security_Ssh_Key{}, err
	}

	sshKeys := []datatypes.SoftLayer_Security_Ssh_Key{}
	err = json.Unmarshal(response, &sshKeys)
	if err != nil {
		return []datatypes.SoftLayer_Security_Ssh_Key{}, err
	}

	return sshKeys, nil
}

func (slvgbdtg *softLayer_Virtual_Guest_Block_Device_Template_Group_Service) GetStatus(id int) (datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group_Status, error) {
	response, err := slvgbdtg.client.DoRawHttpRequest(fmt.Sprintf("%s/%d/getStatus.json", slvgbdtg.GetName(), id), "GET", new(bytes.Buffer))
	if err != nil {
		return datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group_Status{}, err
	}

	status := datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group_Status{}
	err = json.Unmarshal(response, &status)
	if err != nil {
		return datatypes.SoftLayer_Virtual_Guest_Block_Device_Template_Group_Status{}, err
	}

	return status, nil
}
<|endoftext|>


<commit_before>package wddevkit

import (
	_ "github.com/WindomZ/go-develop-kit/cache"
	_ "github.com/WindomZ/go-develop-kit/cache/boolcache"
	_ "github.com/WindomZ/go-develop-kit/cache/numcache"
	_ "github.com/WindomZ/go-develop-kit/googleauth/otp"
	_ "github.com/WindomZ/go-develop-kit/math"
	_ "github.com/WindomZ/go-develop-kit/mutex"
	_ "github.com/WindomZ/go-develop-kit/netutil"
	_ "github.com/WindomZ/go-develop-kit/pinyin"
	_ "github.com/WindomZ/go-develop-kit/security/salt"
	_ "github.com/WindomZ/go-develop-kit/timeutil"
	_ "github.com/WindomZ/go-develop-kit/unit"
	_ "github.com/WindomZ/go-develop-kit/uuid"
)
<commit_msg>fix: go get<commit_after>package wddevkit

import (
	_ "github.com/WindomZ/go-develop-kit/cache"
	_ "github.com/WindomZ/go-develop-kit/cache/boolcache"
	_ "github.com/WindomZ/go-develop-kit/cache/numcache"
	_ "github.com/WindomZ/go-develop-kit/googleauth/otp"
	_ "github.com/WindomZ/go-develop-kit/http"
	_ "github.com/WindomZ/go-develop-kit/jsonutil"
	_ "github.com/WindomZ/go-develop-kit/math"
	_ "github.com/WindomZ/go-develop-kit/mutex"
	_ "github.com/WindomZ/go-develop-kit/netutil"
	_ "github.com/WindomZ/go-develop-kit/pinyin"
	_ "github.com/WindomZ/go-develop-kit/routestat"
	_ "github.com/WindomZ/go-develop-kit/security/encoding"
	_ "github.com/WindomZ/go-develop-kit/security/salt"
	_ "github.com/WindomZ/go-develop-kit/timeutil"
	_ "github.com/WindomZ/go-develop-kit/unit"
	_ "github.com/WindomZ/go-develop-kit/uuid"
)
<|endoftext|>


<commit_before>package golis

import (
	"log"
	"net"
)

//系统变量定义
var (
	GolisHandler IoHandler //事件处理
	GolisPackage Package   //拆包封包处理接口
)

//定义session
type Iosession struct {
	SesionId   int      //session唯一表示
	Connection net.Conn //连接
}

//session写入数据
func (this *Iosession) Write(message interface{}) {
	//触发消息发送事件
	GolisHandler.MessageSent(this, message)
	this.Connection.Write(GolisPackage.Packet(message))
}

//关闭连接
func (this *Iosession) Close() {
	GolisHandler.SessionClosed(this)
	this.Connection.Close()
}

//拆包封包接口定义
type Package interface {
	//读取连接数据
	//connData 从连接当中读取数据
	//packageChan 准备好包后交给该chan
	//remain 读取connData的剩余数据返回
	ReadConnData(connData []byte, packageChan chan<- []byte) (remain []byte)
	//拆包函数
	Unpacket(data []byte) interface{}
	//封包函数
	Packet(msg interface{}) []byte
}

//事件触发接口定义
type IoHandler interface {
	//session打开
	SessionOpened(session *Iosession)
	//session关闭
	SessionClosed(session *Iosession)
	//收到消息时触发
	MessageReceived(session *Iosession, message interface{})
	//消息发送时触发
	MessageSent(session *Iosession, message interface{})
}

//服务器端运行golis
//netPro：运行协议参数，tcp/udp
//laddr ：程序监听ip和端口，如127.0.0.1:8080
func Run(netPro, laddr string) {
	log.Println("golis is listen port:", laddr)
	netLis, err := net.Listen(netPro, laddr)
	if err != nil {
		log.Fatalln(err)
	}
	defer netLis.Close()
	log.Println("waiting clients...")
	for {
		conn, err := netLis.Accept()
		if err != nil {
			continue
		}
		go connectHandle(conn)
	}
}

//客户端程序连接服务器
func Dial(netPro, laddr string) {
	conn, err := net.Dial(netPro, laddr)
	if err != nil {
		log.Fatalln(err)
	}
	go connectHandle(conn)
}

//处理新连接
func connectHandle(conn net.Conn) {
	defer conn.Close()
	//声明一个临时缓冲区，用来存储被截断的数据
	tmpBuffer := make([]byte, 0)
	buffer := make([]byte, 512)

	//声明一个管道用于接收解包的数据
	readerChannel := make(chan []byte, 16)
	//创建session
	session := Iosession{Connection: conn}
	//触发sessionCreated事件
	GolisHandler.SessionOpened(&session)

	exitChan := make(chan bool, 0)
	go waitData(&session, readerChannel, exitChan)

	flag := true
	for flag {

		n, err := conn.Read(buffer)
		//Log(time.Now().Unix(), "tmpBuffer.len:", len(tmpBuffer), "tmpBuffer.cap:", cap(tmpBuffer), "n:", n)

		if err == nil {
			tmpBuffer = GolisPackage.ReadConnData(append(tmpBuffer, buffer[:n]...), readerChannel)
		} else {
			//			Log("client is disconnected")
			//session关闭
			GolisHandler.SessionClosed(&session)
			flag = false
			exitChan <- true
			break
		}
	}

}

//等待数据包
func waitData(session *Iosession, readerChannel <-chan []byte, exitChan chan bool) {
	for {
		select {
		case data := <-readerChannel:
			readFromData(session, data)
		case <-exitChan:
			break
		}
	}
}

//从准备好的数据读取并拆包
func readFromData(session *Iosession, data []byte) {
	message := GolisPackage.Unpacket(data) //拆包
	//收到消息到达时触发事件
	GolisHandler.MessageReceived(session, message)
}
<commit_msg>添加输出日志<commit_after>package golis

import (
	"log"
	"net"
)

//系统变量定义
var (
	GolisHandler IoHandler //事件处理
	GolisPackage Package   //拆包封包处理接口
)

//定义session
type Iosession struct {
	SesionId   int      //session唯一表示
	Connection net.Conn //连接
}

//session写入数据
func (this *Iosession) Write(message interface{}) {
	//触发消息发送事件
	GolisHandler.MessageSent(this, message)
	this.Connection.Write(GolisPackage.Packet(message))
}

//关闭连接
func (this *Iosession) Close() {
	GolisHandler.SessionClosed(this)
	this.Connection.Close()
}

//拆包封包接口定义
type Package interface {
	//读取连接数据
	//connData 从连接当中读取数据
	//packageChan 准备好包后交给该chan
	//remain 读取connData的剩余数据返回
	ReadConnData(connData []byte, packageChan chan<- []byte) (remain []byte)
	//拆包函数
	Unpacket(data []byte) interface{}
	//封包函数
	Packet(msg interface{}) []byte
}

//事件触发接口定义
type IoHandler interface {
	//session打开
	SessionOpened(session *Iosession)
	//session关闭
	SessionClosed(session *Iosession)
	//收到消息时触发
	MessageReceived(session *Iosession, message interface{})
	//消息发送时触发
	MessageSent(session *Iosession, message interface{})
}

//服务器端运行golis
//netPro：运行协议参数，tcp/udp
//laddr ：程序监听ip和端口，如127.0.0.1:8080
func Run(netPro, laddr string) {
	log.Println("golis is listen port:", laddr)
	netLis, err := net.Listen(netPro, laddr)
	if err != nil {
		log.Fatalln(err)
	}
	defer netLis.Close()
	log.Println("waiting clients...")
	for {
		conn, err := netLis.Accept()
		if err != nil {
			continue
		}
		go connectHandle(conn)
	}
}

//客户端程序连接服务器
func Dial(netPro, laddr string) {
	conn, err := net.Dial(netPro, laddr)
	if err != nil {
		log.Fatalln(err)
	}
	go connectHandle(conn)
}

//处理新连接
func connectHandle(conn net.Conn) {
	defer conn.Close()
	//声明一个临时缓冲区，用来存储被截断的数据
	tmpBuffer := make([]byte, 0)
	buffer := make([]byte, 512)

	//声明一个管道用于接收解包的数据
	readerChannel := make(chan []byte, 16)
	//创建session
	session := Iosession{Connection: conn}
	//触发sessionCreated事件
	GolisHandler.SessionOpened(&session)

	exitChan := make(chan bool, 0)
	go waitData(&session, readerChannel, exitChan)

	flag := true
	for flag {

		n, err := conn.Read(buffer)
		//Log(time.Now().Unix(), "tmpBuffer.len:", len(tmpBuffer), "tmpBuffer.cap:", cap(tmpBuffer), "n:", n)

		if err == nil {
			tmpBuffer = GolisPackage.ReadConnData(append(tmpBuffer, buffer[:n]...), readerChannel)
		} else {
			//			Log("client is disconnected")
			//session关闭
			GolisHandler.SessionClosed(&session)
			flag = false
			exitChan <- true
			break
		}
	}

}

//等待数据包
func waitData(session *Iosession, readerChannel chan []byte, exitChan chan bool) {
	for {
		select {
		case data := <-readerChannel:
			readFromData(session, data)
		case <-exitChan:
			break
		}
	}
}

//从准备好的数据读取并拆包
func readFromData(session *Iosession, data []byte) {
	message := GolisPackage.Unpacket(data) //拆包
	//收到消息到达时触发事件
	GolisHandler.MessageReceived(session, message)
}
<|endoftext|>


<commit_before>package golis

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
)

//系统变量定义

var (
	GolisHandler IoHandler                //事件处理
	Unpacket     func([]byte) interface{} //拆包
	Packet       func(interface{}) []byte //封包
)

//定义session
type Iosession struct {
	conn net.Conn
}

//session写入数据
func (this *Iosession) Write(message *interface{}) {
	//触发消息发送事件
	GolisHandler.MessageSent(this, message)
	data := Packet(message)
	totalLen := len(data)
	this.conn.Write(append(IntToBytes(totalLen), data...))
}

//事件触发接口定义
type IoHandler interface {
	//session打开
	SessionOpened(session *Iosession)
	//session关闭
	SessionClosed(session *Iosession)
	//收到消息时触发
	MessageReceived(session *Iosession, message interface{})
	//消息发送时触发
	MessageSent(session *Iosession, message interface{})
}

//运行golis
//netPro：运行协议参数，tcp/udp
//laddr ：程序监听ip和端口，如127.0.0.1:8080
func Run(netPro, laddr string) {
	Log("初始化系统完成")
	netLis, err := net.Listen(netPro, laddr)
	CheckError(err)
	defer netLis.Close()
	Log("等待客户端连接...")
	for {
		conn, err := netLis.Accept()
		if err != nil {
			continue
		}
		go connectHandle(conn)
	}
}

//处理新连接
func connectHandle(conn net.Conn) {
	//声明一个临时缓冲区，用来存储被截断的数据
	tmpBuffer := make([]byte, 0)
	buffer := make([]byte, 1024)

	//声明一个管道用于接收解包的数据
	readerChannel := make(chan []byte, 16)
	//创建session
	session := Iosession{conn}
	//触发sessionCreated事件
	GolisHandler.SessionOpened(&session)

	go reader(&session, readerChannel)

	flag := true
	for flag {
		n, err := conn.Read(buffer)
		switch err {
		case nil:
			//			tmp, data, err := getReadyData(append(tmpBuffer, buffer[:n]...))
			//			tmpBuffer = tmp
			//			if err != nil {
			//				Log(err.Error())
			//			} else {
			//				readFromData(&session, data)
			//			}
			tmpBuffer = unpack(append(tmpBuffer, buffer[:n]...), readerChannel)
		case io.EOF:
			Log("client is disconnected")
			//session关闭
			GolisHandler.SessionClosed(&session)
			flag = false
			break
		default:
			Log("none")
		}
	}

}

const (
	constDataLength = 4
)

//解包
func unpack(buffer []byte, readerChannel chan []byte) []byte {
	length := len(buffer)

	var i int
	for i = 0; i < length; i = i + 1 {
		if length < i+constDataLength {
			break
		}
		messageLength := BytesToInt(buffer[i : i+constDataLength])
		if length < i+constDataLength+messageLength {
			break
		}
		data := buffer[i+constDataLength : i+constDataLength+messageLength]
		readerChannel <- data
		i += constDataLength + messageLength - 1
	}

	if i == length {
		return make([]byte, 0)
	}
	return buffer[i:]
}

//协议中查看协议头是否满足一个协议报
func getReadyData(buffer []byte) ([]byte, []byte, error) {
	length := len(buffer)
	//	Log("length = ", length)
	if length >= 4 {
		totalLen := BytesToInt(buffer[0:4]) //get totalLen
		if totalLen == 0 {
			return make([]byte, 0), nil, errors.New("msg is null")
		} else if totalLen <= length-4 {
			return buffer[totalLen+4:], buffer[4:totalLen], nil
		}

	}
	return buffer, nil, errors.New("msg is not ready")
}

func reader(session *Iosession, readerChannel chan []byte) {
	for {
		select {
		case data := <-readerChannel:
			readFromData(session, data)
		}
	}
}

//从准备好的数据读取
func readFromData(session *Iosession, data []byte) {
	message := Unpacket(data) //拆包
	//收到消息时到达
	GolisHandler.MessageReceived(session, message)
}

//整形转换成字节
func IntToBytes(n int) []byte {
	x := int32(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形
func BytesToInt(b []byte) int {
	bytesBuffer := bytes.NewBuffer(b)

	var x int32
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int(x)
}

//整形64转换成字节
func Int64ToBytes(n int64) []byte {
	x := int64(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形64
func BytesToInt64(b []byte) int {
	bytesBuffer := bytes.NewBuffer(b)

	var x int64
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int(x)
}

//整形64转换成字节
func Int8ToBytes(n int8) []byte {
	x := int8(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形8
func BytesToInt8(b []byte) int {
	bytesBuffer := bytes.NewBuffer(b)

	var x int8
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int(x)
}

//简单日志输出
func Log(v ...interface{}) {
	fmt.Println(v...)
}

//检查错误并退出程序
func CheckError(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, "Fatal error:%s", err.Error())
		os.Exit(1)
	}
}
<commit_msg>*修改传参类型<commit_after>package golis

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
)

//系统变量定义

var (
	GolisHandler IoHandler                //事件处理
	Unpacket     func([]byte) interface{} //拆包
	Packet       func(interface{}) []byte //封包
)

//定义session
type Iosession struct {
	conn net.Conn
}

//session写入数据
func (this *Iosession) Write(message *interface{}) {
	//触发消息发送事件
	GolisHandler.MessageSent(this, message)
	data := Packet(message)
	totalLen := len(data)
	this.conn.Write(append(IntToBytes(totalLen), data...))
}

//事件触发接口定义
type IoHandler interface {
	//session打开
	SessionOpened(session *Iosession)
	//session关闭
	SessionClosed(session *Iosession)
	//收到消息时触发
	MessageReceived(session *Iosession, message interface{})
	//消息发送时触发
	MessageSent(session *Iosession, message interface{})
}

//运行golis
//netPro：运行协议参数，tcp/udp
//laddr ：程序监听ip和端口，如127.0.0.1:8080
func Run(netPro, laddr string) {
	Log("初始化系统完成")
	netLis, err := net.Listen(netPro, laddr)
	CheckError(err)
	defer netLis.Close()
	Log("等待客户端连接...")
	for {
		conn, err := netLis.Accept()
		if err != nil {
			continue
		}
		go connectHandle(conn)
	}
}

//处理新连接
func connectHandle(conn net.Conn) {
	//声明一个临时缓冲区，用来存储被截断的数据
	tmpBuffer := make([]byte, 0)
	buffer := make([]byte, 1024)

	//声明一个管道用于接收解包的数据
	readerChannel := make(chan []byte, 16)
	//创建session
	session := Iosession{conn}
	//触发sessionCreated事件
	GolisHandler.SessionOpened(&session)

	go reader(&session, readerChannel)

	flag := true
	for flag {
		n, err := conn.Read(buffer)
		switch err {
		case nil:
			//			tmp, data, err := getReadyData(append(tmpBuffer, buffer[:n]...))
			//			tmpBuffer = tmp
			//			if err != nil {
			//				Log(err.Error())
			//			} else {
			//				readFromData(&session, data)
			//			}
			tmpBuffer = unpack(append(tmpBuffer, buffer[:n]...), readerChannel)
		case io.EOF:
			Log("client is disconnected")
			//session关闭
			GolisHandler.SessionClosed(&session)
			flag = false
			break
		default:
			Log("none")
		}
	}

}

const (
	constDataLength = 4
)

//解包
func unpack(buffer []byte, readerChannel chan []byte) []byte {
	length := len(buffer)

	var i int
	for i = 0; i < length; i = i + 1 {
		if length < i+constDataLength {
			break
		}
		messageLength := BytesToInt(buffer[i : i+constDataLength])
		if length < i+constDataLength+messageLength {
			break
		}
		data := buffer[i+constDataLength : i+constDataLength+messageLength]
		readerChannel <- data
		i += constDataLength + messageLength - 1
	}

	if i == length {
		return make([]byte, 0)
	}
	return buffer[i:]
}

//协议中查看协议头是否满足一个协议报
func getReadyData(buffer []byte) ([]byte, []byte, error) {
	length := len(buffer)
	//	Log("length = ", length)
	if length >= 4 {
		totalLen := BytesToInt(buffer[0:4]) //get totalLen
		if totalLen == 0 {
			return make([]byte, 0), nil, errors.New("msg is null")
		} else if totalLen <= length-4 {
			return buffer[totalLen+4:], buffer[4:totalLen], nil
		}

	}
	return buffer, nil, errors.New("msg is not ready")
}

func reader(session *Iosession, readerChannel chan []byte) {
	for {
		select {
		case data := <-readerChannel:
			readFromData(session, data)
		}
	}
}

//从准备好的数据读取
func readFromData(session *Iosession, data []byte) {
	message := Unpacket(data) //拆包
	//收到消息时到达
	GolisHandler.MessageReceived(session, message)
}

//整形转换成字节
func IntToBytes(n int) []byte {
	x := int32(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形
func BytesToInt(b []byte) int {
	bytesBuffer := bytes.NewBuffer(b)

	var x int32
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int(x)
}

//整形64转换成字节
func Int64ToBytes(n int64) []byte {
	x := int64(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形64
func BytesToInt64(b []byte) int64 {
	bytesBuffer := bytes.NewBuffer(b)

	var x int64
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int64(x)
}

//整形64转换成字节
func Int8ToBytes(n int8) []byte {
	x := int8(n)

	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

//字节转换成整形8
func BytesToInt8(b []byte) int8 {
	bytesBuffer := bytes.NewBuffer(b)

	var x int8
	binary.Read(bytesBuffer, binary.BigEndian, &x)

	return int8(x)
}

//简单日志输出
func Log(v ...interface{}) {
	fmt.Println(v...)
}

//检查错误并退出程序
func CheckError(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, "Fatal error:%s", err.Error())
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>/*
gorbl lets you perform RBL (Real-time Blackhole List - https://en.wikipedia.org/wiki/DNSBL) lookups using Golang

This package takes inspiration from a similar module that I wrote in Python
(https://github.com/polera/rblwatch).

gorbl takes a simpler approach:  Basic lookup capability is provided by the
lib, while, unlike in rblwatch, concurrent lookups and the lists on which to
search are left to those using the lib.

JSON annotations on the types are provided as a convenience.
*/
package gorbl

import (
	"fmt"
	"net"
	"strings"
)

/*
RBLResults holds the results of the lookup.
*/
type RBLResults struct {
	// List is the RBL that was searched
	List string `json:"list"`
	// Host is the host or IP that was passed (i.e. smtp.gmail.com)
	Host string `json:"host"`
	// Results is a slice of Results - one per IP address searched
	Results []Result `json:"results"`
}

/*
Result holds the individual IP lookup results for each RBL search
*/
type Result struct {
	// Address is the IP address that was searched
	Address string `json:"address"`
	// Listed indicates whether or not the IP was on the RBL
	Listed bool `json:"listed"`
	// RBL lists sometimes add extra information as a TXT record
	// if any info is present, it will be stored here.
	Text string `json:"text"`
	// Error represents any error that was encountered (DNS timeout, host not
	// found, etc.) if any
	Error bool `json:"error"`
	// ErrorType is the type of error encountered if any
	ErrorType error `json:"error_type"`
}

/*
Reverses the octets of a given IPv4 address
64.233.171.108 becomes 108.171.233.64
*/
func Reverse(ip net.IP) string {
	if ip.To4() != nil {
		splitAddress := strings.Split(ip.String(), ".")

		for i, j := 0, len(splitAddress)-1; i < len(splitAddress)/2; i, j = i+1, j-1 {
			splitAddress[i], splitAddress[j] = splitAddress[j], splitAddress[i]
		}

		return strings.Join(splitAddress, ".")
	}
	return ""
}

func query(rbl string, host string, r *Result) {
	//	r := Result{}
	r.Listed = false

	lookup := fmt.Sprintf("%s.%s", host, rbl)

	res, err := net.LookupHost(lookup)
	if len(res) > 0 {
		r.Listed = true
		txt, _ := net.LookupTXT(lookup)
		if len(txt) > 0 {
			r.Text = txt[0]
		}
	}
	if err != nil {
		r.Error = true
		r.ErrorType = err
	}

	return
}

/*
Lookup performs the search and returns the RBLResults
*/
func Lookup(rbl_list string, target_host string) (r RBLResults) {
	r.List = rbl_list
	r.Host = target_host

	if ip, err := net.LookupIP(target_host); err == nil {
		for _, addr := range ip {
			if addr.To4() != nil {
				res := Result{}
				res.Address = addr.String()

				addr := Reverse(addr)

				query(rbl_list, addr, &res)

				r.Results = append(r.Results, res)

			}
		}
	}
	return
}
<commit_msg>Fixed issues reported by go-lint.appspot.com/github.com/polera/gorbl<commit_after>/*
Package gorbl lets you perform RBL (Real-time Blackhole List - https://en.wikipedia.org/wiki/DNSBL) lookups using Golang

This package takes inspiration from a similar module that I wrote in Python
(https://github.com/polera/rblwatch).

gorbl takes a simpler approach:  Basic lookup capability is provided by the
lib, while, unlike in rblwatch, concurrent lookups and the lists on which to
search are left to those using the lib.

JSON annotations on the types are provided as a convenience.
*/
package gorbl

import (
	"fmt"
	"net"
	"strings"
)

/*
RBLResults holds the results of the lookup.
*/
type RBLResults struct {
	// List is the RBL that was searched
	List string `json:"list"`
	// Host is the host or IP that was passed (i.e. smtp.gmail.com)
	Host string `json:"host"`
	// Results is a slice of Results - one per IP address searched
	Results []Result `json:"results"`
}

/*
Result holds the individual IP lookup results for each RBL search
*/
type Result struct {
	// Address is the IP address that was searched
	Address string `json:"address"`
	// Listed indicates whether or not the IP was on the RBL
	Listed bool `json:"listed"`
	// RBL lists sometimes add extra information as a TXT record
	// if any info is present, it will be stored here.
	Text string `json:"text"`
	// Error represents any error that was encountered (DNS timeout, host not
	// found, etc.) if any
	Error bool `json:"error"`
	// ErrorType is the type of error encountered if any
	ErrorType error `json:"error_type"`
}

/*
Reverse the octets of a given IPv4 address
64.233.171.108 becomes 108.171.233.64
*/
func Reverse(ip net.IP) string {
	if ip.To4() != nil {
		splitAddress := strings.Split(ip.String(), ".")

		for i, j := 0, len(splitAddress)-1; i < len(splitAddress)/2; i, j = i+1, j-1 {
			splitAddress[i], splitAddress[j] = splitAddress[j], splitAddress[i]
		}

		return strings.Join(splitAddress, ".")
	}
	return ""
}

func query(rbl string, host string, r *Result) {
	//	r := Result{}
	r.Listed = false

	lookup := fmt.Sprintf("%s.%s", host, rbl)

	res, err := net.LookupHost(lookup)
	if len(res) > 0 {
		r.Listed = true
		txt, _ := net.LookupTXT(lookup)
		if len(txt) > 0 {
			r.Text = txt[0]
		}
	}
	if err != nil {
		r.Error = true
		r.ErrorType = err
	}

	return
}

/*
Lookup performs the search and returns the RBLResults
*/
func Lookup(rblList string, targetHost string) (r RBLResults) {
	r.List = rblList
	r.Host = targetHost

	if ip, err := net.LookupIP(targetHost); err == nil {
		for _, addr := range ip {
			if addr.To4() != nil {
				res := Result{}
				res.Address = addr.String()

				addr := Reverse(addr)

				query(rblList, addr, &res)

				r.Results = append(r.Results, res)

			}
		}
	}
	return
}
<|endoftext|>


<commit_before>// Package grace allows for gracefully waiting for a listener to
// finish serving it's active requests.
package grace

import (
	"errors"
	"fmt"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
)

var (
	// This error is returned by Inherits() when we're not inheriting any fds.
	ErrNotInheriting = errors.New("no inherited listeners")

	// This error is returned by Listener.Accept() when Close is in progress.
	ErrAlreadyClosed = errors.New("already closed")

	// Time in the past to trigger immediate deadline.
	timeInPast = time.Now()
)

const (
	// Used to indicate a graceful restart in the new process.
	envCountKey       = "LISTEN_FDS"
	envCountKeyPrefix = envCountKey + "="

	// The error returned by the standard library when the socket is closed.
	errClosed = "use of closed network connection"
)

// A Listener providing a graceful Close process and can be sent
// across processes using the underlying File descriptor.
type Listener interface {
	net.Listener

	// Will return the underlying file representing this Listener.
	File() (f *os.File, err error)
}

type listener struct {
	Listener
	closed      bool
	closedMutex sync.RWMutex
	wg          sync.WaitGroup
}

type deadliner interface {
	SetDeadline(t time.Time) error
}

// Allows for us to notice when the connection is closed.
type conn struct {
	net.Conn
	wg   *sync.WaitGroup
	once sync.Once
}

func (c *conn) Close() error {
	defer c.once.Do(c.wg.Done)
	return c.Conn.Close()
}

// Wraps an existing File listener to provide a graceful Close() process.
func NewListener(l Listener) Listener {
	return &listener{Listener: l}
}

func (l *listener) Close() error {
	l.closedMutex.Lock()
	l.closed = true
	l.closedMutex.Unlock()

	var err error
	// Init provided sockets dont actually close so we trigger Accept to return
	// by setting the deadline.
	if os.Getppid() == 1 {
		if ld, ok := l.Listener.(deadliner); ok {
			err = ld.SetDeadline(timeInPast)
		} else {
			fmt.Fprintln(os.Stderr, "init activated server did not have SetDeadline")
		}
	} else {
		err = l.Listener.Close()
	}
	l.wg.Wait()
	return err
}

func (l *listener) Accept() (net.Conn, error) {
	// Presume we'll accept and decrement in defer if we don't. If we did this
	// after a successful accept we would have a race condition where we may end
	// up incorrectly shutting down between the time we do a successful accept
	// and the increment.
	var c net.Conn
	l.wg.Add(1)
	defer func() {
		// If we didn't accept, we decrement our presumptuous count above.
		if c == nil {
			l.wg.Done()
		}
	}()

	l.closedMutex.RLock()
	if l.closed {
		l.closedMutex.RUnlock()
		return nil, ErrAlreadyClosed
	}
	l.closedMutex.RUnlock()

	c, err := l.Listener.Accept()
	if err != nil {
		if strings.HasSuffix(err.Error(), errClosed) {
			return nil, ErrAlreadyClosed
		}

		// We use SetDeadline above to trigger Accept to return when we're trying
		// to handoff to a child as part of our restart process. In this scenario
		// we want to treat the timeout the same as a Close.
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			l.closedMutex.RLock()
			if l.closed {
				l.closedMutex.RUnlock()
				return nil, ErrAlreadyClosed
			}
			l.closedMutex.RUnlock()
		}
		return nil, err
	}
	return &conn{Conn: c, wg: &l.wg}, nil
}

type Process struct {
}

// Wait for signals to gracefully terminate or restart the process.
func (p *Process) Wait(listeners []Listener) (err error) {
	ch := make(chan os.Signal, 2)
	signal.Notify(ch, syscall.SIGTERM, syscall.SIGUSR2)
	for {
		sig := <-ch
		switch sig {
		case syscall.SIGTERM:
			signal.Stop(ch)
			var wg sync.WaitGroup
			wg.Add(len(listeners))
			for _, l := range listeners {
				go func(l Listener) {
					defer wg.Done()
					cErr := l.Close()
					if cErr != nil {
						err = cErr
					}
				}(l)
			}
			wg.Wait()
			return
		case syscall.SIGUSR2:
			rErr := Restart(listeners)
			if rErr != nil {
				return rErr
			}
		}
	}
}

// Try to inherit listeners from the parent process.
func (p *Process) Inherit() (listeners []Listener, err error) {
	countStr := os.Getenv(envCountKey)
	if countStr == "" {
		return nil, ErrNotInheriting
	}
	count, err := strconv.Atoi(countStr)
	if err != nil {
		return nil, err
	}
	// If we are inheriting, the listeners will begin at fd 3
	for i := 3; i < 3+count; i++ {
		file := os.NewFile(uintptr(i), "listener")
		tmp, err := net.FileListener(file)
		file.Close()
		if err != nil {
			return nil, err
		}
		l := tmp.(Listener)
		listeners = append(listeners, NewListener(l))
	}
	return
}

// Start the Close process in the parent. This does not wait for the
// parent to close and simply sends it the TERM signal.
func (p *Process) CloseParent() error {
	ppid := os.Getppid()
	if ppid == 1 { // init provided sockets, for example systemd
		return nil
	}
	return syscall.Kill(ppid, syscall.SIGTERM)
}

// Restart the process passing the given listeners to the new process.
func (p *Process) Restart(listeners []Listener) (err error) {
	if len(listeners) == 0 {
		return errors.New("restart must be given listeners.")
	}

	// Extract the fds from the listeners.
	files := make([]*os.File, len(listeners))
	for i, l := range listeners {
		files[i], err = l.File()
		if err != nil {
			return err
		}
		defer files[i].Close()
		syscall.CloseOnExec(int(files[i].Fd()))
	}

	// Use the original binary location. This works with symlinks such that if
	// the file it points to has been changed we will use the updated symlink.
	argv0, err := exec.LookPath(os.Args[0])
	if err != nil {
		return err
	}

	// In order to keep the working directory the same as when we started.
	wd, err := os.Getwd()
	if err != nil {
		return err
	}

	// Pass on the environment and replace the old count key with the new one.
	var env []string
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, envCountKeyPrefix) {
			env = append(env, v)
		}
	}
	env = append(env, fmt.Sprintf("%s%d", envCountKeyPrefix, len(listeners)))

	allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
	_, err = os.StartProcess(argv0, os.Args, &os.ProcAttr{
		Dir:   wd,
		Env:   env,
		Files: allFiles,
	})
	return err
}

var defaultProcess = &Process{}

// Wait for signals to gracefully terminate or restart the process.
func Wait(listeners []Listener) (err error) {
	return defaultProcess.Wait(listeners)
}

// Try to inherit listeners from the parent process.
func Inherit() (listeners []Listener, err error) {
	return defaultProcess.Inherit()
}

// Start the Close process in the parent. This does not wait for the
// parent to close and simply sends it the TERM signal.
func CloseParent() error {
	return defaultProcess.CloseParent()
}

// Restart the process passing the given listeners to the new process.
func Restart(listeners []Listener) (err error) {
	return defaultProcess.Restart(listeners)
}
<commit_msg>detect init started on startup rather than when necessary to avoid re-parenting issues. fixes #7<commit_after>// Package grace allows for gracefully waiting for a listener to
// finish serving it's active requests.
package grace

import (
	"errors"
	"fmt"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
)

var (
	// This error is returned by Inherits() when we're not inheriting any fds.
	ErrNotInheriting = errors.New("no inherited listeners")

	// This error is returned by Listener.Accept() when Close is in progress.
	ErrAlreadyClosed = errors.New("already closed")

	// Time in the past to trigger immediate deadline.
	timeInPast = time.Now()

	// Test if init activated by checking ppid on startup since we will get
	// re-parented once the old parent is killed and we will end up looking like
	// we're init started.
	initStarted = os.Getppid() == 1
)

const (
	// Used to indicate a graceful restart in the new process.
	envCountKey       = "LISTEN_FDS"
	envCountKeyPrefix = envCountKey + "="

	// The error returned by the standard library when the socket is closed.
	errClosed = "use of closed network connection"
)

// A Listener providing a graceful Close process and can be sent
// across processes using the underlying File descriptor.
type Listener interface {
	net.Listener

	// Will return the underlying file representing this Listener.
	File() (f *os.File, err error)
}

type listener struct {
	Listener
	closed      bool
	closedMutex sync.RWMutex
	wg          sync.WaitGroup
}

type deadliner interface {
	SetDeadline(t time.Time) error
}

// Allows for us to notice when the connection is closed.
type conn struct {
	net.Conn
	wg   *sync.WaitGroup
	once sync.Once
}

func (c *conn) Close() error {
	defer c.once.Do(c.wg.Done)
	return c.Conn.Close()
}

// Wraps an existing File listener to provide a graceful Close() process.
func NewListener(l Listener) Listener {
	return &listener{Listener: l}
}

func (l *listener) Close() error {
	l.closedMutex.Lock()
	l.closed = true
	l.closedMutex.Unlock()

	var err error
	// Init provided sockets dont actually close so we trigger Accept to return
	// by setting the deadline.
	if initStarted {
		if ld, ok := l.Listener.(deadliner); ok {
			err = ld.SetDeadline(timeInPast)
		} else {
			fmt.Fprintln(os.Stderr, "init activated server did not have SetDeadline")
		}
	} else {
		err = l.Listener.Close()
	}
	l.wg.Wait()
	return err
}

func (l *listener) Accept() (net.Conn, error) {
	// Presume we'll accept and decrement in defer if we don't. If we did this
	// after a successful accept we would have a race condition where we may end
	// up incorrectly shutting down between the time we do a successful accept
	// and the increment.
	var c net.Conn
	l.wg.Add(1)
	defer func() {
		// If we didn't accept, we decrement our presumptuous count above.
		if c == nil {
			l.wg.Done()
		}
	}()

	l.closedMutex.RLock()
	if l.closed {
		l.closedMutex.RUnlock()
		return nil, ErrAlreadyClosed
	}
	l.closedMutex.RUnlock()

	c, err := l.Listener.Accept()
	if err != nil {
		if strings.HasSuffix(err.Error(), errClosed) {
			return nil, ErrAlreadyClosed
		}

		// We use SetDeadline above to trigger Accept to return when we're trying
		// to handoff to a child as part of our restart process. In this scenario
		// we want to treat the timeout the same as a Close.
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			l.closedMutex.RLock()
			if l.closed {
				l.closedMutex.RUnlock()
				return nil, ErrAlreadyClosed
			}
			l.closedMutex.RUnlock()
		}
		return nil, err
	}
	return &conn{Conn: c, wg: &l.wg}, nil
}

type Process struct {
}

// Wait for signals to gracefully terminate or restart the process.
func (p *Process) Wait(listeners []Listener) (err error) {
	ch := make(chan os.Signal, 2)
	signal.Notify(ch, syscall.SIGTERM, syscall.SIGUSR2)
	for {
		sig := <-ch
		switch sig {
		case syscall.SIGTERM:
			signal.Stop(ch)
			var wg sync.WaitGroup
			wg.Add(len(listeners))
			for _, l := range listeners {
				go func(l Listener) {
					defer wg.Done()
					cErr := l.Close()
					if cErr != nil {
						err = cErr
					}
				}(l)
			}
			wg.Wait()
			return
		case syscall.SIGUSR2:
			rErr := Restart(listeners)
			if rErr != nil {
				return rErr
			}
		}
	}
}

// Try to inherit listeners from the parent process.
func (p *Process) Inherit() (listeners []Listener, err error) {
	countStr := os.Getenv(envCountKey)
	if countStr == "" {
		return nil, ErrNotInheriting
	}
	count, err := strconv.Atoi(countStr)
	if err != nil {
		return nil, err
	}
	// If we are inheriting, the listeners will begin at fd 3
	for i := 3; i < 3+count; i++ {
		file := os.NewFile(uintptr(i), "listener")
		tmp, err := net.FileListener(file)
		file.Close()
		if err != nil {
			return nil, err
		}
		l := tmp.(Listener)
		listeners = append(listeners, NewListener(l))
	}
	return
}

// Start the Close process in the parent. This does not wait for the
// parent to close and simply sends it the TERM signal.
func (p *Process) CloseParent() error {
	ppid := os.Getppid()
	if ppid == 1 { // init provided sockets, for example systemd
		return nil
	}
	return syscall.Kill(ppid, syscall.SIGTERM)
}

// Restart the process passing the given listeners to the new process.
func (p *Process) Restart(listeners []Listener) (err error) {
	if len(listeners) == 0 {
		return errors.New("restart must be given listeners.")
	}

	// Extract the fds from the listeners.
	files := make([]*os.File, len(listeners))
	for i, l := range listeners {
		files[i], err = l.File()
		if err != nil {
			return err
		}
		defer files[i].Close()
		syscall.CloseOnExec(int(files[i].Fd()))
	}

	// Use the original binary location. This works with symlinks such that if
	// the file it points to has been changed we will use the updated symlink.
	argv0, err := exec.LookPath(os.Args[0])
	if err != nil {
		return err
	}

	// In order to keep the working directory the same as when we started.
	wd, err := os.Getwd()
	if err != nil {
		return err
	}

	// Pass on the environment and replace the old count key with the new one.
	var env []string
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, envCountKeyPrefix) {
			env = append(env, v)
		}
	}
	env = append(env, fmt.Sprintf("%s%d", envCountKeyPrefix, len(listeners)))

	allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
	_, err = os.StartProcess(argv0, os.Args, &os.ProcAttr{
		Dir:   wd,
		Env:   env,
		Files: allFiles,
	})
	return err
}

var defaultProcess = &Process{}

// Wait for signals to gracefully terminate or restart the process.
func Wait(listeners []Listener) (err error) {
	return defaultProcess.Wait(listeners)
}

// Try to inherit listeners from the parent process.
func Inherit() (listeners []Listener, err error) {
	return defaultProcess.Inherit()
}

// Start the Close process in the parent. This does not wait for the
// parent to close and simply sends it the TERM signal.
func CloseParent() error {
	return defaultProcess.CloseParent()
}

// Restart the process passing the given listeners to the new process.
func Restart(listeners []Listener) (err error) {
	return defaultProcess.Restart(listeners)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"code.google.com/p/go.tools/go/loader"
	"code.google.com/p/go.tools/godoc/vfs"

	"sourcegraph.com/sourcegraph/srclib-go/gog"
	"sourcegraph.com/sourcegraph/srclib-go/gog/definfo"
	defpkg "sourcegraph.com/sourcegraph/srclib-go/golang_def"
	"sourcegraph.com/sourcegraph/srclib/graph"
	"sourcegraph.com/sourcegraph/srclib/grapher"
	"sourcegraph.com/sourcegraph/srclib/repo"
	"sourcegraph.com/sourcegraph/srclib/unit"
)

func init() {
	_, err := parser.AddCommand("graph",
		"graph a Go package",
		"Graph a Go package, producing all defs, refs, and docs.",
		&graphCmd,
	)
	if err != nil {
		log.Fatal(err)
	}
}

type GraphCmd struct{}

var graphCmd GraphCmd

func (c *GraphCmd) Execute(args []string) error {
	var unit *unit.SourceUnit
	if err := json.NewDecoder(os.Stdin).Decode(&unit); err != nil {
		return err
	}
	if err := os.Stdin.Close(); err != nil {
		return err
	}

	if err := unmarshalTypedConfig(unit.Config); err != nil {
		return err
	}
	if err := config.apply(); err != nil {
		return err
	}

	if os.Getenv("IN_DOCKER_CONTAINER") != "" {
		buildPkg, err := UnitDataAsBuildPackage(unit)
		if err != nil {
			return err
		}

		// Make a new GOPATH.
		buildContext.GOPATH = "/tmp/gopath"

		// Set up GOPATH so it has this repo.
		log.Printf("Setting up a new GOPATH at %s", buildContext.GOPATH)
		dir := filepath.Join(buildContext.GOPATH, "src", string(unit.Repo))
		if err := os.MkdirAll(filepath.Dir(dir), 0700); err != nil {
			return err
		}
		log.Printf("Creating symlink to oldname %q at newname %q.", cwd, dir)
		if err := os.Symlink(cwd, dir); err != nil {
			return err
		}

		log.Printf("Changing directory to %q.", dir)
		if err := os.Chdir(dir); err != nil {
			return err
		}
		dockerCWD = cwd

		if config.GOROOT == "" {
			cwd = dir
		}

		// Get and install deps. (Only deps not in this repo; if we call `go
		// get` on this repo, we will either try to check out a different
		// version or fail with 'stale checkout?' because the .dockerignore
		// doesn't copy the .git dir.)
		var externalDeps []string
		for _, dep := range unit.Dependencies {
			importPath := dep.(string)
			if !strings.HasPrefix(importPath, string(unit.Repo)) && importPath != "C" {
				externalDeps = append(externalDeps, importPath)
			}
		}
		cmd := exec.Command("go", "get", "-x", "-d", "-t", "-v", "./"+buildPkg.Dir)
		cmd.Args = append(cmd.Args, externalDeps...)
		cmd.Env = config.env()
		cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
		log.Printf("Downloading import dependencies: %v (env vars: %v).", cmd.Args, cmd.Env)
		if err := cmd.Run(); err != nil {
			return err
		}
		log.Printf("Finished downloading dependencies.")
	}

	out, err := Graph(unit)
	if err != nil {
		return err
	}

	// Make paths relative to repo.
	for _, gs := range out.Defs {
		if gs.File == "" {
			log.Printf("no file %+v", gs)
		}
		gs.File = relPath(cwd, gs.File)
	}
	for _, gr := range out.Refs {
		gr.File = relPath(cwd, gr.File)
	}
	for _, gd := range out.Docs {
		if gd.File != "" {
			gd.File = relPath(cwd, gd.File)
		}
	}

	if err := json.NewEncoder(os.Stdout).Encode(out); err != nil {
		return err
	}
	return nil
}

func relPath(base, path string) string {
	rp, err := filepath.Rel(base, path)
	if err != nil {
		log.Fatalf("Failed to make path %q relative to %q: %s", path, base, err)
	}

	// TODO(sqs): hack
	if strings.HasPrefix(rp, "../../../") && dockerCWD != "" {
		rp, err = filepath.Rel(dockerCWD, path)
		if err != nil {
			log.Fatalf("Failed to make path %q relative to %q: %s", path, cwd, err)
		}
	}

	return rp
}

func Graph(unit *unit.SourceUnit) (*grapher.Output, error) {
	pkg, err := UnitDataAsBuildPackage(unit)
	if err != nil {
		return nil, err
	}

	o, err := doGraph(pkg.ImportPath)
	if err != nil {
		return nil, err
	}

	o2 := grapher.Output{
		Defs: make([]*graph.Def, len(o.Defs)),
		Refs: make([]*graph.Ref, len(o.Refs)),
		Docs: make([]*graph.Doc, len(o.Docs)),
	}

	uri := string(unit.Repo)

	for i, gs := range o.Defs {
		o2.Defs[i], err = convertGoDef(gs, uri)
		if err != nil {
			return nil, err
		}
	}
	for i, gr := range o.Refs {
		o2.Refs[i], err = convertGoRef(gr, uri)
		if err != nil {
			return nil, err
		}
	}
	for i, gd := range o.Docs {
		o2.Docs[i], err = convertGoDoc(gd, uri)
		if err != nil {
			return nil, err
		}
	}

	return &o2, nil
}

func convertGoDef(gs *gog.Def, repoURI string) (*graph.Def, error) {
	resolvedTarget, err := ResolveDep(gs.DefKey.PackageImportPath, repoURI)
	if err != nil {
		return nil, err
	}
	path := graph.DefPath(pathOrDot(strings.Join(gs.Path, "/")))
	treePath := treePath(string(path))
	if !treePath.IsValid() {
		return nil, fmt.Errorf("'%s' is not a valid tree-path", treePath)
	}

	def := &graph.Def{
		DefKey: graph.DefKey{
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
			Path:     path,
		},
		TreePath: treePath,

		Name: gs.Name,
		Kind: graph.DefKind(definfo.GeneralKindMap[gs.Kind]),

		File:     gs.File,
		DefStart: gs.DeclSpan[0],
		DefEnd:   gs.DeclSpan[1],

		Exported: gs.DefInfo.Exported,
		Test:     strings.HasSuffix(gs.File, "_test.go"),
	}

	d := defpkg.DefData{
		PackageImportPath: gs.DefKey.PackageImportPath,
		DefInfo:           gs.DefInfo,
	}
	def.Data, err = json.Marshal(d)
	if err != nil {
		return nil, err
	}

	if def.Kind == "func" {
		def.Callable = true
	}

	return def, nil
}

func convertGoRef(gr *gog.Ref, repoURI string) (*graph.Ref, error) {
	resolvedTarget, err := ResolveDep(gr.Def.PackageImportPath, repoURI)
	if err != nil {
		return nil, err
	}
	if resolvedTarget == nil {
		return nil, nil
	}

	return &graph.Ref{
		DefRepo:     uriOrEmpty(resolvedTarget.ToRepoCloneURL),
		DefPath:     graph.DefPath(pathOrDot(strings.Join(gr.Def.Path, "/"))),
		DefUnit:     resolvedTarget.ToUnit,
		DefUnitType: resolvedTarget.ToUnitType,
		Def:         gr.IsDef,
		File:        gr.File,
		Start:       gr.Span[0],
		End:         gr.Span[1],
	}, nil
}

func convertGoDoc(gd *gog.Doc, repoURI string) (*graph.Doc, error) {
	resolvedTarget, err := ResolveDep(gd.PackageImportPath, repoURI)
	if err != nil {
		return nil, err
	}
	return &graph.Doc{
		DefKey: graph.DefKey{
			Path:     graph.DefPath(pathOrDot(strings.Join(gd.Path, "/"))),
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
		},
		Format: gd.Format,
		Data:   gd.Data,
		File:   gd.File,
		Start:  gd.Span[0],
		End:    gd.Span[1],
	}, nil
}

func uriOrEmpty(cloneURL string) repo.URI {
	if cloneURL == "" {
		return ""
	}
	return repo.MakeURI(cloneURL)
}

func pathOrDot(path string) string {
	if path == "" {
		return "."
	}
	return path
}

func treePath(path string) graph.TreePath {
	if path == "" || path == "." {
		return graph.TreePath(".")
	}
	return graph.TreePath(fmt.Sprintf("./%s", path))
}

func doGraph(importPath string) (*gog.Output, error) {
	// If we've overridden GOROOT and we're building a package not in
	// $GOROOT/src/pkg (such as "cmd/go"), then we need to virtualize GOROOT
	// because we can't set GOPATH=GOROOT (go/build ignores GOPATH in that
	// case).
	if config.GOROOT != "" && strings.HasPrefix(importPath, "cmd/") {
		// Unset our custom GOROOT (since we're routing FS ops to it using
		// vfs) and set it as our GOPATH.
		buildContext.GOROOT = build.Default.GOROOT
		buildContext.GOPATH = config.GOROOT

		virtualCWD = build.Default.GOROOT

		ns := vfs.NameSpace{}
		ns.Bind(filepath.Join(buildContext.GOROOT, "src/pkg"), vfs.OS(filepath.Join(config.GOROOT, "src/pkg")), "/", vfs.BindBefore)
		ns.Bind("/", vfs.OS("/"), "/", vfs.BindAfter)
		buildContext.IsDir = func(path string) bool {
			fi, err := ns.Stat(path)
			return err == nil && fi.Mode().IsDir()
		}
		buildContext.HasSubdir = func(root, dir string) (rel string, ok bool) { panic("unexpected") }
		buildContext.OpenFile = func(path string) (io.ReadCloser, error) {
			f, err := ns.Open(path)
			return f, err
		}
		buildContext.ReadDir = ns.ReadDir
	}

	// If we're using a custom GOROOT, we need to bootstrap the installation.
	if config.GOROOT != "" && !loaderConfig.SourceImports {
		if _, err := os.Stat("src/make.bash"); err == nil {
			// TODO(sqs): in docker, we can't write to this dir, so we'll have to
			// move it elsewhere
			cmd := exec.Command("bash", "make.bash")
			cmd.Dir = filepath.Join(cwd, "src")
			cmd.Env = config.env()
			cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
			log.Printf("Bootstrapping: %v (env vars: %v)", cmd.Args, cmd.Env)
			if err := cmd.Run(); err != nil {
				return nil, err
			}
		}
	}

	if !loaderConfig.SourceImports {
		// Install pkg.
		cmd := exec.Command("go", "install", "-x", "-v", importPath)
		cmd.Env = config.env()
		cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
		log.Printf("Install %q: %v (env vars: %v)", importPath, cmd.Args, cmd.Env)
		if err := cmd.Run(); err != nil {
			return nil, err
		}

		log.Println("\n\n############### AFTER BUILD ###############\n\n")
		out, err := exec.Command("find", "/tmp/gopath").Output()
		log.Println(string(out))
		log.Println(err)
	}

	importUnsafe := importPath == "unsafe"

	if err := loaderConfig.ImportWithTests(importPath); err != nil {
		return nil, err
	}

	if importUnsafe {
		// Special-case "unsafe" because go/loader does not let you load it
		// directly.
		if loaderConfig.ImportPkgs == nil {
			loaderConfig.ImportPkgs = make(map[string]bool)
		}
		loaderConfig.ImportPkgs["unsafe"] = true
	}

	build.Default = *loaderConfig.Build

	prog, err := loaderConfig.Load()
	if err != nil {
		return nil, err
	}

	g := gog.New(prog)

	var pkgs []*loader.PackageInfo
	for _, pkg := range prog.Imported {
		pkgs = append(pkgs, pkg)
	}

	for _, pkg := range pkgs {
		if err := g.Graph(pkg); err != nil {
			return nil, err
		}
	}

	return &g.Output, nil
}
<commit_msg>ignore defs/refs/docs that have no file associated with them<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"code.google.com/p/go.tools/go/loader"
	"code.google.com/p/go.tools/godoc/vfs"

	"sourcegraph.com/sourcegraph/srclib-go/gog"
	"sourcegraph.com/sourcegraph/srclib-go/gog/definfo"
	defpkg "sourcegraph.com/sourcegraph/srclib-go/golang_def"
	"sourcegraph.com/sourcegraph/srclib/graph"
	"sourcegraph.com/sourcegraph/srclib/grapher"
	"sourcegraph.com/sourcegraph/srclib/repo"
	"sourcegraph.com/sourcegraph/srclib/unit"
)

func init() {
	_, err := parser.AddCommand("graph",
		"graph a Go package",
		"Graph a Go package, producing all defs, refs, and docs.",
		&graphCmd,
	)
	if err != nil {
		log.Fatal(err)
	}
}

type GraphCmd struct{}

var graphCmd GraphCmd

func (c *GraphCmd) Execute(args []string) error {
	var unit *unit.SourceUnit
	if err := json.NewDecoder(os.Stdin).Decode(&unit); err != nil {
		return err
	}
	if err := os.Stdin.Close(); err != nil {
		return err
	}

	if err := unmarshalTypedConfig(unit.Config); err != nil {
		return err
	}
	if err := config.apply(); err != nil {
		return err
	}

	if os.Getenv("IN_DOCKER_CONTAINER") != "" {
		buildPkg, err := UnitDataAsBuildPackage(unit)
		if err != nil {
			return err
		}

		// Make a new GOPATH.
		buildContext.GOPATH = "/tmp/gopath"

		// Set up GOPATH so it has this repo.
		log.Printf("Setting up a new GOPATH at %s", buildContext.GOPATH)
		dir := filepath.Join(buildContext.GOPATH, "src", string(unit.Repo))
		if err := os.MkdirAll(filepath.Dir(dir), 0700); err != nil {
			return err
		}
		log.Printf("Creating symlink to oldname %q at newname %q.", cwd, dir)
		if err := os.Symlink(cwd, dir); err != nil {
			return err
		}

		log.Printf("Changing directory to %q.", dir)
		if err := os.Chdir(dir); err != nil {
			return err
		}
		dockerCWD = cwd

		if config.GOROOT == "" {
			cwd = dir
		}

		// Get and install deps. (Only deps not in this repo; if we call `go
		// get` on this repo, we will either try to check out a different
		// version or fail with 'stale checkout?' because the .dockerignore
		// doesn't copy the .git dir.)
		var externalDeps []string
		for _, dep := range unit.Dependencies {
			importPath := dep.(string)
			if !strings.HasPrefix(importPath, string(unit.Repo)) && importPath != "C" {
				externalDeps = append(externalDeps, importPath)
			}
		}
		cmd := exec.Command("go", "get", "-x", "-d", "-t", "-v", "./"+buildPkg.Dir)
		cmd.Args = append(cmd.Args, externalDeps...)
		cmd.Env = config.env()
		cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
		log.Printf("Downloading import dependencies: %v (env vars: %v).", cmd.Args, cmd.Env)
		if err := cmd.Run(); err != nil {
			return err
		}
		log.Printf("Finished downloading dependencies.")
	}

	out, err := Graph(unit)
	if err != nil {
		return err
	}

	// Make paths relative to repo.
	for _, gs := range out.Defs {
		if gs.File == "" {
			log.Printf("no file %+v", gs)
		}
		if gs.File != "" {
			gs.File = relPath(cwd, gs.File)
		}
	}
	for _, gr := range out.Refs {
		if gr.File != "" {
			gr.File = relPath(cwd, gr.File)
		}
	}
	for _, gd := range out.Docs {
		if gd.File != "" {
			gd.File = relPath(cwd, gd.File)
		}
	}

	if err := json.NewEncoder(os.Stdout).Encode(out); err != nil {
		return err
	}
	return nil
}

func relPath(base, path string) string {
	rp, err := filepath.Rel(base, path)
	if err != nil {
		log.Fatalf("Failed to make path %q relative to %q: %s", path, base, err)
	}

	// TODO(sqs): hack
	if strings.HasPrefix(rp, "../../../") && dockerCWD != "" {
		rp, err = filepath.Rel(dockerCWD, path)
		if err != nil {
			log.Fatalf("Failed to make path %q relative to %q: %s", path, cwd, err)
		}
	}

	return rp
}

func Graph(unit *unit.SourceUnit) (*grapher.Output, error) {
	pkg, err := UnitDataAsBuildPackage(unit)
	if err != nil {
		return nil, err
	}

	o, err := doGraph(pkg.ImportPath)
	if err != nil {
		return nil, err
	}

	o2 := grapher.Output{}

	uri := string(unit.Repo)

	for _, gs := range o.Defs {
		d, err := convertGoDef(gs, uri)
		if err != nil {
			return nil, err
		}
		if d != nil {
			o2.Defs = append(o2.Defs, d)
		}
	}
	for _, gr := range o.Refs {
		r, err := convertGoRef(gr, uri)
		if err != nil {
			return nil, err
		}
		if r != nil {
			o2.Refs = append(o2.Refs, r)
		}
	}
	for _, gd := range o.Docs {
		d, err := convertGoDoc(gd, uri)
		if err != nil {
			return nil, err
		}
		if d != nil {
			o2.Docs = append(o2.Docs, d)
		}
	}

	return &o2, nil
}

func convertGoDef(gs *gog.Def, repoURI string) (*graph.Def, error) {
	resolvedTarget, err := ResolveDep(gs.DefKey.PackageImportPath, repoURI)
	if err != nil {
		return nil, err
	}
	path := graph.DefPath(pathOrDot(strings.Join(gs.Path, "/")))
	treePath := treePath(string(path))
	if !treePath.IsValid() {
		return nil, fmt.Errorf("'%s' is not a valid tree-path", treePath)
	}

	def := &graph.Def{
		DefKey: graph.DefKey{
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
			Path:     path,
		},
		TreePath: treePath,

		Name: gs.Name,
		Kind: graph.DefKind(definfo.GeneralKindMap[gs.Kind]),

		File:     gs.File,
		DefStart: gs.DeclSpan[0],
		DefEnd:   gs.DeclSpan[1],

		Exported: gs.DefInfo.Exported,
		Test:     strings.HasSuffix(gs.File, "_test.go"),
	}

	d := defpkg.DefData{
		PackageImportPath: gs.DefKey.PackageImportPath,
		DefInfo:           gs.DefInfo,
	}
	def.Data, err = json.Marshal(d)
	if err != nil {
		return nil, err
	}

	if def.Kind == "func" {
		def.Callable = true
	}

	if def.File == "" {
		// some cgo defs have empty File; omit them
		return nil, nil
	}

	return def, nil
}

func convertGoRef(gr *gog.Ref, repoURI string) (*graph.Ref, error) {
	resolvedTarget, err := ResolveDep(gr.Def.PackageImportPath, repoURI)
	if err != nil {
		return nil, err
	}
	if resolvedTarget == nil {
		return nil, nil
	}

	return &graph.Ref{
		DefRepo:     uriOrEmpty(resolvedTarget.ToRepoCloneURL),
		DefPath:     graph.DefPath(pathOrDot(strings.Join(gr.Def.Path, "/"))),
		DefUnit:     resolvedTarget.ToUnit,
		DefUnitType: resolvedTarget.ToUnitType,
		Def:         gr.IsDef,
		File:        gr.File,
		Start:       gr.Span[0],
		End:         gr.Span[1],
	}, nil
}

func convertGoDoc(gd *gog.Doc, repoURI string) (*graph.Doc, error) {
	resolvedTarget, err := ResolveDep(gd.PackageImportPath, repoURI)
	if err != nil {
		return nil, err
	}
	return &graph.Doc{
		DefKey: graph.DefKey{
			Path:     graph.DefPath(pathOrDot(strings.Join(gd.Path, "/"))),
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
		},
		Format: gd.Format,
		Data:   gd.Data,
		File:   gd.File,
		Start:  gd.Span[0],
		End:    gd.Span[1],
	}, nil
}

func uriOrEmpty(cloneURL string) repo.URI {
	if cloneURL == "" {
		return ""
	}
	return repo.MakeURI(cloneURL)
}

func pathOrDot(path string) string {
	if path == "" {
		return "."
	}
	return path
}

func treePath(path string) graph.TreePath {
	if path == "" || path == "." {
		return graph.TreePath(".")
	}
	return graph.TreePath(fmt.Sprintf("./%s", path))
}

func doGraph(importPath string) (*gog.Output, error) {
	// If we've overridden GOROOT and we're building a package not in
	// $GOROOT/src/pkg (such as "cmd/go"), then we need to virtualize GOROOT
	// because we can't set GOPATH=GOROOT (go/build ignores GOPATH in that
	// case).
	if config.GOROOT != "" && strings.HasPrefix(importPath, "cmd/") {
		// Unset our custom GOROOT (since we're routing FS ops to it using
		// vfs) and set it as our GOPATH.
		buildContext.GOROOT = build.Default.GOROOT
		buildContext.GOPATH = config.GOROOT

		virtualCWD = build.Default.GOROOT

		ns := vfs.NameSpace{}
		ns.Bind(filepath.Join(buildContext.GOROOT, "src/pkg"), vfs.OS(filepath.Join(config.GOROOT, "src/pkg")), "/", vfs.BindBefore)
		ns.Bind("/", vfs.OS("/"), "/", vfs.BindAfter)
		buildContext.IsDir = func(path string) bool {
			fi, err := ns.Stat(path)
			return err == nil && fi.Mode().IsDir()
		}
		buildContext.HasSubdir = func(root, dir string) (rel string, ok bool) { panic("unexpected") }
		buildContext.OpenFile = func(path string) (io.ReadCloser, error) {
			f, err := ns.Open(path)
			return f, err
		}
		buildContext.ReadDir = ns.ReadDir
	}

	// If we're using a custom GOROOT, we need to bootstrap the installation.
	if config.GOROOT != "" && !loaderConfig.SourceImports {
		if _, err := os.Stat("src/make.bash"); err == nil {
			// TODO(sqs): in docker, we can't write to this dir, so we'll have to
			// move it elsewhere
			cmd := exec.Command("bash", "make.bash")
			cmd.Dir = filepath.Join(cwd, "src")
			cmd.Env = config.env()
			cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
			log.Printf("Bootstrapping: %v (env vars: %v)", cmd.Args, cmd.Env)
			if err := cmd.Run(); err != nil {
				return nil, err
			}
		}
	}

	if !loaderConfig.SourceImports {
		// Install pkg.
		cmd := exec.Command("go", "install", "-x", "-v", importPath)
		cmd.Env = config.env()
		cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
		log.Printf("Install %q: %v (env vars: %v)", importPath, cmd.Args, cmd.Env)
		if err := cmd.Run(); err != nil {
			return nil, err
		}

		log.Println("\n\n############### AFTER BUILD ###############\n\n")
		out, err := exec.Command("find", "/tmp/gopath").Output()
		log.Println(string(out))
		log.Println(err)
	}

	importUnsafe := importPath == "unsafe"

	if err := loaderConfig.ImportWithTests(importPath); err != nil {
		return nil, err
	}

	if importUnsafe {
		// Special-case "unsafe" because go/loader does not let you load it
		// directly.
		if loaderConfig.ImportPkgs == nil {
			loaderConfig.ImportPkgs = make(map[string]bool)
		}
		loaderConfig.ImportPkgs["unsafe"] = true
	}

	build.Default = *loaderConfig.Build

	prog, err := loaderConfig.Load()
	if err != nil {
		return nil, err
	}

	g := gog.New(prog)

	var pkgs []*loader.PackageInfo
	for _, pkg := range prog.Imported {
		pkgs = append(pkgs, pkg)
	}

	for _, pkg := range pkgs {
		if err := g.Graph(pkg); err != nil {
			return nil, err
		}
	}

	return &g.Output, nil
}
<|endoftext|>


<commit_before>package restpc

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"reflect"
	"runtime"
)

type Handler func(req Request) (res *Response, err error)

var handlers = map[string]uintptr{}

func callHandler(handler Handler, request Request) (res *Response, err error) {
	defer func() {
		panicMsg := recover()
		if panicMsg != nil {
			err = NewError(
				Internal,
				Internal.String(),
				fmt.Errorf(
					"panic in handler %v: %v",
					getFunctionName(handler),
					panicMsg,
				),
			)
		}
	}()
	res, err = handler(request)
	return
}

func TranslateHandler(handler Handler) http.HandlerFunc {
	handlerFuncObj := runtime.FuncForPC(reflect.ValueOf(handler).Pointer())
	handlerName := handlerFuncObj.Name()
	handlers[handlerName] = handlerFuncObj.Entry()
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r != nil && r.Body != nil {
				r.Body.Close()
			}
		}()
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "error in parsing form", http.StatusBadRequest)
			return
		}
		request := &requestImp{r: r}
		res, err := callHandler(handler, request)
		if res == nil && err == nil {
			err = NewError(Internal, "", fmt.Errorf("handler %v returned nil response with nil error", handlerName))
		}
		if err != nil {
			code := Unknown
			errorMsg := "Unknown" // FIXME: "unknown"
			rpcErr, isRpcErr := err.(RPCError)
			if isRpcErr {
				code = rpcErr.Code()
				errorMsg = rpcErr.Error() // FIXME: use a mapping or make it space-separated
			} else {
				log.Printf(
					"myrpc.TranslateHandler: handler '%v' returned non-rpc error: %#v\n",
					handlerName,
					err,
				)
				rpcErr = NewError(
					Unknown, "", err,
				)
			}
			status := HTTPStatusFromCode(code)
			jsonByte, _ := json.Marshal(map[string]string{
				"code":  code.String(),
				"error": errorMsg,
			})
			http.Error(
				w,
				string(jsonByte),
				status,
			)
			errorDispatcher(request, rpcErr)
			return
		}
		wh := w.Header()
		if res.Header != nil {
			for key, values := range res.Header {
				for _, value := range values {
					wh.Add(key, value)
				}
			}
		}
		data := res.Data
		if data == nil {
			data = map[string]interface{}{}
		}
		resBodyBytes, err := json.Marshal(data)
		if err != nil {
			log.Println("error in json.Marshal(res.Data):", err)
		} else {
			_, err := w.Write(resBodyBytes)
			if err != nil {
				log.Println("error in w.Write(resBodyBytes):", err)
			}
		}
	}
}
<commit_msg>run errorDispatcher in background<commit_after>package restpc

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"reflect"
	"runtime"
)

type Handler func(req Request) (res *Response, err error)

var handlers = map[string]uintptr{}

func callHandler(handler Handler, request Request) (res *Response, err error) {
	defer func() {
		panicMsg := recover()
		if panicMsg != nil {
			err = NewError(
				Internal,
				Internal.String(),
				fmt.Errorf(
					"panic in handler %v: %v",
					getFunctionName(handler),
					panicMsg,
				),
			)
		}
	}()
	res, err = handler(request)
	return
}

func TranslateHandler(handler Handler) http.HandlerFunc {
	handlerFuncObj := runtime.FuncForPC(reflect.ValueOf(handler).Pointer())
	handlerName := handlerFuncObj.Name()
	handlers[handlerName] = handlerFuncObj.Entry()
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r != nil && r.Body != nil {
				r.Body.Close()
			}
		}()
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "error in parsing form", http.StatusBadRequest)
			return
		}
		request := &requestImp{r: r}
		res, err := callHandler(handler, request)
		if res == nil && err == nil {
			err = NewError(Internal, "", fmt.Errorf("handler %v returned nil response with nil error", handlerName))
		}
		if err != nil {
			code := Unknown
			errorMsg := "Unknown" // FIXME: "unknown"
			rpcErr, isRpcErr := err.(RPCError)
			if isRpcErr {
				code = rpcErr.Code()
				errorMsg = rpcErr.Error() // FIXME: use a mapping or make it space-separated
			} else {
				log.Printf(
					"myrpc.TranslateHandler: handler '%v' returned non-rpc error: %#v\n",
					handlerName,
					err,
				)
				rpcErr = NewError(
					Unknown, "", err,
				)
			}
			status := HTTPStatusFromCode(code)
			jsonByte, _ := json.Marshal(map[string]string{
				"code":  code.String(),
				"error": errorMsg,
			})
			http.Error(
				w,
				string(jsonByte),
				status,
			)
			go errorDispatcher(request, rpcErr)
			return
		}
		wh := w.Header()
		if res.Header != nil {
			for key, values := range res.Header {
				for _, value := range values {
					wh.Add(key, value)
				}
			}
		}
		data := res.Data
		if data == nil {
			data = map[string]interface{}{}
		}
		resBodyBytes, err := json.Marshal(data)
		if err != nil {
			log.Println("error in json.Marshal(res.Data):", err)
		} else {
			_, err := w.Write(resBodyBytes)
			if err != nil {
				log.Println("error in w.Write(resBodyBytes):", err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package relayR

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

type callInstruction struct {
	R string        // The Relay to use
	F string        // The function to call
	A []interface{} // The arguments to use
	C string        // ConnectionId
}

type negotiation struct {
	T string
}

type negotiationResponse struct {
	ConnectionID string
}

// Handle specifies the route to server the RelayR client script from.
func Handle(route string, e *Exchange) {
	setupMainRoute(route, e)
	setupNegotiateRoute(route, e)
	setupWebSocketRoute(route, e)
}

func setupMainRoute(route string, e *Exchange) {
	http.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "GET" {
			jsonResponse(w)
			writeRelayScript(e, w, route)
		} else {
			// Decode the post request
			decoder := json.NewDecoder(r.Body)
			var call callInstruction

			decoder.Decode(&call)

			relay := e.getRelayByName(call.R)
			err := e.callMethod(relay.t, call.F, call.C, call.A)

			// if there was a problem .. report it back
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}
	})
}

func setupNegotiateRoute(route string, e *Exchange) {
	http.HandleFunc(route+"/negotiate", func(w http.ResponseWriter, r *http.Request) {
		jsonResponse(w)
		decoder := json.NewDecoder(r.Body)

		var neg negotiation

		decoder.Decode(&neg)

		encoder := json.NewEncoder(w)
		encoder.Encode(negotiationResponse{ConnectionID: e.addClient(neg.T)})
	})
}

func setupWebSocketRoute(route string, e *Exchange) {
	http.Handle(route+"/ws", socketHandler{e: e, c: e.circuits["WebSocket"].(*webSocketCircuit)})
}

// Write the client side script to the response
func writeRelayScript(e *Exchange, w http.ResponseWriter, route string) {
	buff := bytes.Buffer{}

	buff.WriteString(fmt.Sprintf(connectionClassScript, route))

	buff.WriteString(relayClassBegin)

	for _, relay := range e.Relays {
		buff.WriteString(fmt.Sprintf(relayBegin, relay.Name))

		for _, method := range relay.methods {
			buff.WriteString(fmt.Sprintf(relayMethod, lowerFirst(method), relay.Name, method))

		}
		buff.WriteString(relayEnd)
	}

	buff.WriteString(relayClassEnd)
	io.Copy(w, &buff)
}
<commit_msg>Added ClientScriptFunc for consumer processing<commit_after>package relayR

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

type callInstruction struct {
	R string        // The Relay to use
	F string        // The function to call
	A []interface{} // The arguments to use
	C string        // ConnectionId
}

type negotiation struct {
	T string
}

type negotiationResponse struct {
	ConnectionID string
}

var ClientScriptFunc func([]byte) []byte

// Handle specifies the route to server the RelayR client script from.
func Handle(route string, e *Exchange) {
	setupMainRoute(route, e)
	setupNegotiateRoute(route, e)
	setupWebSocketRoute(route, e)
}

func setupMainRoute(route string, e *Exchange) {
	http.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "GET" {
			jsonResponse(w)
			writeRelayScript(e, w, route)
		} else {
			// Decode the post request
			decoder := json.NewDecoder(r.Body)
			var call callInstruction

			decoder.Decode(&call)

			relay := e.getRelayByName(call.R)
			err := e.callMethod(relay.t, call.F, call.C, call.A)

			// if there was a problem .. report it back
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}
	})
}

func setupNegotiateRoute(route string, e *Exchange) {
	http.HandleFunc(route+"/negotiate", func(w http.ResponseWriter, r *http.Request) {
		jsonResponse(w)
		decoder := json.NewDecoder(r.Body)

		var neg negotiation

		decoder.Decode(&neg)

		encoder := json.NewEncoder(w)
		encoder.Encode(negotiationResponse{ConnectionID: e.addClient(neg.T)})
	})
}

func setupWebSocketRoute(route string, e *Exchange) {
	http.Handle(route+"/ws", socketHandler{e: e, c: e.circuits["WebSocket"].(*webSocketCircuit)})
}

// Write the client side script to the response
func writeRelayScript(e *Exchange, w http.ResponseWriter, route string) {
	resultBuff := bytes.Buffer{}
	buff := bytes.Buffer{}

	buff.WriteString(fmt.Sprintf(connectionClassScript, route))

	buff.WriteString(relayClassBegin)

	for _, relay := range e.Relays {
		buff.WriteString(fmt.Sprintf(relayBegin, relay.Name))

		for _, method := range relay.methods {
			buff.WriteString(fmt.Sprintf(relayMethod, lowerFirst(method), relay.Name, method))

		}
		buff.WriteString(relayEnd)
	}

	buff.WriteString(relayClassEnd)

	if ClientScriptFunc != nil {
		resultBuff.Write(ClientScriptFunc(buff.Bytes()))
	} else {
		resultBuff.Write(buff.Bytes())
	}

	io.Copy(w, &resultBuff)
}
<|endoftext|>


<commit_before>package gincrud

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/fatih/structs"
	"github.com/gin-gonic/gin"
	"github.com/mgutz/logxi/v1"
	"github.com/osiloke/gostore"
	"reflect"
	"runtime"
	"strconv"
)

var logger = log.New("gincrud")

type ErrorList struct {
	Msg   string                 `json:"msg"`
	Error map[string]interface{} `json:"error"`
}

type ErrorCtx struct {
	Bucket string
	key    string
	GinCtx *gin.Context
}

type SuccessCtx struct {
	Bucket string
	Key    string
	Result map[string]interface{}
	GinCtx *gin.Context
}

type MarshalError struct {
	Data map[string]interface{}
}

type UnknownContent struct {
	S string `json:"msg"`
}

func (e *UnknownContent) Error() string {
	return e.S
}

type JSONError interface {
	Serialize() map[string]interface{} //serialize error to json
}

type ParsedContent map[string]interface{}

//Convert request json data to data and map, you can handle validation here
type MarshalFn func(ctx *gin.Context) (map[string]interface{}, error)
type UnMarshalFn func(*gin.Context, []byte) (map[string]interface{}, error)

//Get unique key from object and request
type GetKey func(interface{}, *gin.Context) string

//Called when a crud operation is successful
type OnSuccess func(ctx SuccessCtx) (string, error)

//Called when a crud operation fails
type OnError func(ctx interface{}, err error) error

type Results struct {
	Data       []map[string]interface{} `json:"data"`
	Count      int                      `json:"count,omitempty"`
	TotalCount int                      `json:"total_count,omitempty"`
}

const (
	FORM_CONTENT = "form"
	JSON_CONTENT = "json"
	XML_CONTENT  = "xml"
)

func GetFunctionName(i interface{}) string {
	return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

func filterFlags(content string) string {
	for i, a := range content {
		if a == ' ' || a == ';' {
			return content[:i]
		}
	}
	return content
}
func Decode(c *gin.Context, obj interface{}) error {
	ctype := filterFlags(c.Request.Header.Get("Content-Type"))
	switch {
	case c.Request.Method == "GET" || ctype == gin.MIMEPOSTForm:
		return &UnknownContent{"unimplemented content-type: " + ctype}
	case ctype == gin.MIMEJSON:
		decoder := json.NewDecoder(c.Request.Body)
		if err := decoder.Decode(&obj); err == nil {
			return nil
		} else {
			return err
		}
	case ctype == gin.MIMEXML || ctype == gin.MIMEXML2:
		return &UnknownContent{"unimplemented content-type: " + ctype}
	default:
		err := &UnknownContent{"unknown content-type: " + ctype}
		return err
	}
}

func requestContent(c *gin.Context) (ParsedContent, error) {
	ctype := filterFlags(c.Request.Header.Get("Content-Type"))
	switch {
	case c.Request.Method == "GET" || ctype == gin.MIMEPOSTForm:
		return nil, errors.New("Unimplemented content-type: " + ctype)
	case ctype == gin.MIMEJSON:
		var obj ParsedContent
		decoder := json.NewDecoder(c.Request.Body)
		if err := decoder.Decode(obj); err == nil {
			return obj, err
		} else {
			return nil, err
		}
	case ctype == gin.MIMEXML || ctype == gin.MIMEXML2:
		return nil, errors.New("Unimplemented content-type: " + ctype)
	default:
		err := errors.New("unknown content-type: " + ctype)
		c.Fail(400, err)
		return nil, err
	}
}

//TODO: Extract core logic from each crud function i.e make doGetAll, doGet, ... they return data, err
func GetAll(bucket string, store gostore.Store, c *gin.Context, onSuccess OnSuccess, onError OnError) {
	var results []map[string]interface{}
	var err error

	count := 10
	q := c.Request.URL.Query()
	if val, ok := q["_perPage"]; ok {
		count, _ = strconv.Atoi(val[0])
	}
	var data [][][]byte

	if val, ok := q["afterKey"]; ok {
		data, err = store.GetAllAfter([]byte(val[0]), count+1, 0, bucket)
	} else if val, ok := q["beforeKey"]; ok {
		data, err = store.GetAllBefore([]byte(val[0]), count+1, 0, bucket)
	} else {
		data, err = store.GetAll(count+1, 0, bucket)
	}
	if err != nil {
		if onError != nil {
			onError(ErrorCtx{Bucket: bucket, GinCtx: c}, err)
		}
		c.JSON(200, []string{})
	} else {
		for _, element := range data {
			var result map[string]interface{}
			if err := json.Unmarshal(element[1], &result); err != nil {
				onError(ErrorCtx{Bucket: bucket, GinCtx: c}, err)
				c.JSON(500, gin.H{"msg": err})
			} else {
				if result == nil {
					result = make(map[string]interface{})
				}

				result["key"] = string(element[0])
				results = append(results, result)
			}
		}
		if len(results) == 0 {
			c.JSON(200, []string{})
		} else {
			if onSuccess != nil {
			}
			stats, _ := store.Stats(bucket)
			total_count := stats["KeyN"].(int)
			c.Writer.Header().Set("X-Total-Count", fmt.Sprintf("%d", total_count))
			c.JSON(200, Results{results, count, total_count})
			// c.JSON(200, results)
		}
	}
}

func Get(key, bucket string, store gostore.Store, c *gin.Context, record interface{},
	unMarshalFn UnMarshalFn, onSuccess OnSuccess, onError OnError) {
	data, err := store.Get([]byte(key), bucket)
	if err != nil {
		//TODO: Does not exist error for store
		if onError != nil {
			onError(ErrorCtx{bucket, key, c}, err)
		}
		log.Error("Could not retrieve", "key", key, "from", bucket)
		c.JSON(404, gin.H{"msg": fmt.Sprintf("Not found")})
	} else {
		if unMarshalFn != nil {
			m, err := unMarshalFn(c, data[1])
			if m == nil {
				m = make(map[string]interface{})
			}
			m["key"] = string(data[0])
			if err != nil {
				c.JSON(500, err)
			} else {
				kk := string(data[0])
				if onSuccess != nil {
					ctx := SuccessCtx{bucket, kk, m, c}
					onSuccess(ctx)
				}
				c.JSON(200, m)
			}
		} else {
			_ = json.Unmarshal(data[1], record)
			m := structs.Map(record)
			kk := string(data[0])
			m["key"] = kk
			if onSuccess != nil {
				ctx := SuccessCtx{bucket, kk, m, c}
				onSuccess(ctx)
			}
			c.JSON(200, m)
		}
	}
}

func Post(bucket string, store gostore.Store, c *gin.Context,
	record interface{}, fn GetKey, marshalFn MarshalFn, onSuccess OnSuccess, onError OnError) {
	logger.Debug("Post")
	if marshalFn != nil {
		logger.Debug("Post::MarshalFn")
		obj, err := marshalFn(c)
		if err != nil {
			if onError != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			}
			if e, ok := err.(JSONError); ok {
				result := ErrorList{"Malformed data", e.Serialize()}
				c.JSON(400, result)
			} else {
				c.JSON(400, gin.H{"msg": err})
			}

		} else {
			key := fn(obj, c)
			if key == "" {
				c.JSON(500, err)
			} else {
				data, err := json.Marshal(obj)
				if err != nil {
					onError(ErrorCtx{Bucket: bucket}, err)
				} else {
					store.Save([]byte(key), data, bucket)
					if onSuccess != nil {

						logger.Debug("onSuccess", "bucket", bucket, "key", key, "onSuccess", GetFunctionName(onSuccess))
						ctx := SuccessCtx{bucket, key, obj, c}
						onSuccess(ctx)
					}
					obj["key"] = key
					c.JSON(200, obj)
				}
			}
		}

	} else {
		if b := c.Bind(record); b != false {
			m := structs.Map(record)
			data, err := json.Marshal(&record)
			key := fn(m, c)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
				c.JSON(500, gin.H{"msg": "An error occured and this item could not be saved"})
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				m["key"] = key
				logger.Debug("Successfully saved object", "bucket", bucket, "key", key)

				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, m, c}
					onSuccess(ctx)
				}
				c.JSON(200, m)
			}
		} else {
			c.JSON(400, gin.H{"msg": "Seems like the data submitted is not formatted properly"})
		}
	}
}

func Put(key, bucket string, store gostore.Store, c *gin.Context, record interface{},
	marshalFn MarshalFn, onSuccess OnSuccess, onError OnError) {
	if marshalFn != nil {
		obj, err := marshalFn(c)
		if err != nil {
			if onError != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			}
			if e, ok := err.(JSONError); ok {
				result := ErrorList{"Malformed data", e.Serialize()}
				c.JSON(400, result)
			} else {
				c.JSON(400, gin.H{"msg": err.Error()})
			}

		} else {
			data, err := json.Marshal(obj)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, obj, c}
					onSuccess(ctx)
				}
				c.JSON(200, obj)
			}
		}

	} else {
		if b := c.Bind(record); b != false {
			m := structs.Map(record)
			data, err := json.Marshal(&record)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
				c.JSON(500, gin.H{"msg": "An error occured and this item could not be saved"})
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				m["key"] = key

				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, m, c}
					onSuccess(ctx)
				}
				c.JSON(200, m)
			}
		} else {
			c.JSON(400, gin.H{"msg": "Seems like the data submitted is not formatted properly"})
		}
	}
}

func Delete(key, bucket string, store gostore.Store, c *gin.Context, onSuccess OnSuccess, onError OnError) {
	err := store.Delete([]byte(key), bucket)
	if err != nil {
		if onError != nil {
			onError(ErrorCtx{bucket, key, c}, err)
		}
		c.JSON(500, gin.H{"msg": "The item [" + key + "] was not deleted"})
	} else {
		if onSuccess != nil {
			ctx := SuccessCtx{Bucket: bucket, Key: key, GinCtx: c}
			onSuccess(ctx)
		}
		c.JSON(200, gin.H{"msg": "The item [" + key + "] was deleted"})
	}
}
<commit_msg>Add time tracking<commit_after>package gincrud

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/fatih/structs"
	"github.com/gin-gonic/gin"
	"github.com/mgutz/logxi/v1"
	"github.com/osiloke/gostore"
	"reflect"
	"runtime"
	"strconv"
	"time"
)

var logger = log.New("gincrud")

type ErrorList struct {
	Msg   string                 `json:"msg"`
	Error map[string]interface{} `json:"error"`
}

type ErrorCtx struct {
	Bucket string
	key    string
	GinCtx *gin.Context
}

type SuccessCtx struct {
	Bucket string
	Key    string
	Result map[string]interface{}
	GinCtx *gin.Context
}

type MarshalError struct {
	Data map[string]interface{}
}

type UnknownContent struct {
	S string `json:"msg"`
}

func (e *UnknownContent) Error() string {
	return e.S
}

type JSONError interface {
	Serialize() map[string]interface{} //serialize error to json
}

type ParsedContent map[string]interface{}

//Convert request json data to data and map, you can handle validation here
type MarshalFn func(ctx *gin.Context) (map[string]interface{}, error)
type UnMarshalFn func(*gin.Context, []byte) (map[string]interface{}, error)

//Get unique key from object and request
type GetKey func(interface{}, *gin.Context) string

//Called when a crud operation is successful
type OnSuccess func(ctx SuccessCtx) (string, error)

//Called when a crud operation fails
type OnError func(ctx interface{}, err error) error

type Results struct {
	Data       []map[string]interface{} `json:"data"`
	Count      int                      `json:"count,omitempty"`
	TotalCount int                      `json:"total_count,omitempty"`
}

const (
	FORM_CONTENT = "form"
	JSON_CONTENT = "json"
	XML_CONTENT  = "xml"
)

func timeTrack(start time.Time, name string) {
	elapsed := time.Since(start)
	log.Debug(fmt.Sprintf("%s took %s", name, elapsed))
}

func GetFunctionName(i interface{}) string {
	return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

func filterFlags(content string) string {
	for i, a := range content {
		if a == ' ' || a == ';' {
			return content[:i]
		}
	}
	return content
}
func Decode(c *gin.Context, obj interface{}) error {
	ctype := filterFlags(c.Request.Header.Get("Content-Type"))
	switch {
	case c.Request.Method == "GET" || ctype == gin.MIMEPOSTForm:
		return &UnknownContent{"unimplemented content-type: " + ctype}
	case ctype == gin.MIMEJSON:
		decoder := json.NewDecoder(c.Request.Body)
		if err := decoder.Decode(&obj); err == nil {
			return nil
		} else {
			return err
		}
	case ctype == gin.MIMEXML || ctype == gin.MIMEXML2:
		return &UnknownContent{"unimplemented content-type: " + ctype}
	default:
		err := &UnknownContent{"unknown content-type: " + ctype}
		return err
	}
}

func requestContent(c *gin.Context) (ParsedContent, error) {
	ctype := filterFlags(c.Request.Header.Get("Content-Type"))
	switch {
	case c.Request.Method == "GET" || ctype == gin.MIMEPOSTForm:
		return nil, errors.New("Unimplemented content-type: " + ctype)
	case ctype == gin.MIMEJSON:
		var obj ParsedContent
		decoder := json.NewDecoder(c.Request.Body)
		if err := decoder.Decode(obj); err == nil {
			return obj, err
		} else {
			return nil, err
		}
	case ctype == gin.MIMEXML || ctype == gin.MIMEXML2:
		return nil, errors.New("Unimplemented content-type: " + ctype)
	default:
		err := errors.New("unknown content-type: " + ctype)
		c.Fail(400, err)
		return nil, err
	}
}
func doUnmarshal(key, bucket string, data [][]byte, c *gin.Context, unMarshalFn UnMarshalFn, onSuccess OnSuccess, onError OnError) {

	defer timeTrack(time.Now(), "Do Unmarshal "+key+" from "+bucket)
	m, err := unMarshalFn(c, data[1])
	if m == nil {
		m = make(map[string]interface{})
	}
	m["key"] = string(data[0])
	if err != nil {
		c.JSON(500, err)
	} else {
		kk := string(data[0])
		if onSuccess != nil {
			ctx := SuccessCtx{bucket, kk, m, c}
			onSuccess(ctx)
		}
		c.JSON(200, m)
	}
}
func Get(key, bucket string, store gostore.Store, c *gin.Context, record interface{},
	unMarshalFn UnMarshalFn, onSuccess OnSuccess, onError OnError) {
	data, err := store.Get([]byte(key), bucket)
	if err != nil {
		//TODO: Does not exist error for store
		if onError != nil {
			onError(ErrorCtx{bucket, key, c}, err)
		}
		c.JSON(404, gin.H{"msg": fmt.Sprintf("%s Not found", key)})
	} else {
		if unMarshalFn != nil {
			doUnmarshal(key, bucket, data, c, unMarshalFn, onSuccess, onError)
		} else {
			_ = json.Unmarshal(data[1], record)
			m := structs.Map(record)
			kk := string(data[0])
			m["key"] = kk
			if onSuccess != nil {
				ctx := SuccessCtx{bucket, kk, m, c}
				onSuccess(ctx)
			}
			c.JSON(200, m)
		}
	}
}

//TODO: Extract core logic from each crud function i.e make doGetAll, doGet, ... they return data, err
func GetAll(bucket string, store gostore.Store, c *gin.Context, onSuccess OnSuccess, onError OnError) {
	var results []map[string]interface{}
	var err error

	count := 10
	q := c.Request.URL.Query()
	if val, ok := q["_perPage"]; ok {
		count, _ = strconv.Atoi(val[0])
	}
	var data [][][]byte

	if val, ok := q["afterKey"]; ok {
		data, err = store.GetAllAfter([]byte(val[0]), count+1, 0, bucket)
	} else if val, ok := q["beforeKey"]; ok {
		data, err = store.GetAllBefore([]byte(val[0]), count+1, 0, bucket)
	} else {
		data, err = store.GetAll(count+1, 0, bucket)
	}
	if err != nil {
		if onError != nil {
			onError(ErrorCtx{Bucket: bucket, GinCtx: c}, err)
		}
		c.JSON(200, []string{})
	} else {
		for _, element := range data {
			var result map[string]interface{}
			if err := json.Unmarshal(element[1], &result); err != nil {
				onError(ErrorCtx{Bucket: bucket, GinCtx: c}, err)
				c.JSON(500, gin.H{"msg": err})
			} else {
				if result == nil {
					result = make(map[string]interface{})
				}

				result["key"] = string(element[0])
				results = append(results, result)
			}
		}
		if len(results) == 0 {
			c.JSON(200, []string{})
		} else {
			if onSuccess != nil {
			}
			stats, _ := store.Stats(bucket)
			total_count := stats["KeyN"].(int)
			c.Writer.Header().Set("X-Total-Count", fmt.Sprintf("%d", total_count))
			c.JSON(200, Results{results, count, total_count})
			// c.JSON(200, results)
		}
	}
}

func Post(bucket string, store gostore.Store, c *gin.Context,
	record interface{}, fn GetKey, marshalFn MarshalFn, onSuccess OnSuccess, onError OnError) {

	defer func() {
		if r := recover(); r != nil {
			trace := make([]byte, 1024)
			runtime.Stack(trace, true)
			fmt.Printf("Stack: %s", trace)
			//				log.Error("Stack of %d bytes: %s", count, trace)
			//				fmt.Println("Defer Panic in auth middleware:", r)
			logger.Error("POST:", "err", string(trace))
			c.JSON(500, gin.H{"message": "Unable to edit item "})
			c.Abort()
		}
	}()
	if marshalFn != nil {
		logger.Debug("Post", "bucket", bucket, "marshalfn", GetFunctionName(marshalFn))
		obj, err := marshalFn(c)
		if err != nil {
			if onError != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			}
			if e, ok := err.(JSONError); ok {
				result := ErrorList{"Malformed data", e.Serialize()}
				c.JSON(400, result)
			} else {
				c.JSON(400, gin.H{"msg": err})
			}

		} else {
			key := fn(obj, c)
			if key == "" {
				c.JSON(500, err)
			} else {
				data, err := json.Marshal(obj)
				if err != nil {
					onError(ErrorCtx{Bucket: bucket}, err)
				} else {
					store.Save([]byte(key), data, bucket)
					if onSuccess != nil {

						logger.Debug("onSuccess", "bucket", bucket, "key", key, "onSuccess", GetFunctionName(onSuccess))
						ctx := SuccessCtx{bucket, key, obj, c}
						onSuccess(ctx)
					}
					obj["key"] = key
					c.JSON(200, obj)
				}
			}
		}

	} else {
		if b := c.Bind(record); b != false {
			m := structs.Map(record)
			data, err := json.Marshal(&record)
			key := fn(m, c)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
				c.JSON(500, gin.H{"msg": "An error occured and this item could not be saved"})
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				m["key"] = key
				logger.Debug("Successfully saved object", "bucket", bucket, "key", key)

				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, m, c}
					onSuccess(ctx)
				}
				c.JSON(200, m)
			}
		} else {
			c.JSON(400, gin.H{"msg": "Seems like the data submitted is not formatted properly"})
		}
	}
}

func Put(key, bucket string, store gostore.Store, c *gin.Context, record interface{},
	marshalFn MarshalFn, onSuccess OnSuccess, onError OnError) {
	defer func() {
		if r := recover(); r != nil {
			trace := make([]byte, 1024)
			runtime.Stack(trace, true)
			fmt.Printf("Stack: %s", trace)
			//				log.Error("Stack of %d bytes: %s", count, trace)
			//				fmt.Println("Defer Panic in auth middleware:", r)
			logger.Error("Defer Panic in Gincrud PUT:", "err", string(trace))
			c.JSON(500, gin.H{"message": "Unable to edit item "})
			c.Abort()
		}
	}()
	if marshalFn != nil {
		obj, err := marshalFn(c)
		if err != nil {
			if onError != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			}
			if e, ok := err.(JSONError); ok {
				result := ErrorList{"Malformed data", e.Serialize()}
				c.JSON(400, result)
			} else {
				c.JSON(400, gin.H{"msg": err.Error()})
			}

		} else {
			data, err := json.Marshal(obj)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, obj, c}
					onSuccess(ctx)
				}
				c.JSON(200, obj)
			}
		}

	} else {
		if b := c.Bind(record); b != false {
			m := structs.Map(record)
			data, err := json.Marshal(&record)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
				c.JSON(500, gin.H{"msg": "An error occured and this item could not be saved"})
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				m["key"] = key

				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, m, c}
					onSuccess(ctx)
				}
				c.JSON(200, m)
			}
		} else {
			c.JSON(400, gin.H{"msg": "Seems like the data submitted is not formatted properly"})
		}
	}
}

func Delete(key, bucket string, store gostore.Store, c *gin.Context, onSuccess OnSuccess, onError OnError) {
	err := store.Delete([]byte(key), bucket)
	if err != nil {
		if onError != nil {
			onError(ErrorCtx{bucket, key, c}, err)
		}
		c.JSON(500, gin.H{"msg": "The item [" + key + "] was not deleted"})
	} else {
		if onSuccess != nil {
			ctx := SuccessCtx{Bucket: bucket, Key: key, GinCtx: c}
			onSuccess(ctx)
		}
		c.JSON(200, gin.H{"msg": "The item [" + key + "] was deleted"})
	}
}
<|endoftext|>