<commit_before>package printer

import (
	"fmt"
	"text/template"

	"github.com/davyxu/tabtoy/v2/i18n"
	"github.com/davyxu/tabtoy/v2/model"
	"strings"
)

const csharpTemplate = `// Generated by github.com/davyxu/tabtoy
// Version: {{.ToolVersion}}
// DO NOT EDIT!!
using System.Collections.Generic;

namespace {{.Namespace}}{{$globalIndex:=.Indexes}}{{$verticalFields:=.VerticalFields}}
{
	{{range .Enums}}
	// Defined in table: {{.DefinedTable}}
	public enum {{.Name}}
	{
	{{range .Fields}}
		{{.Comment}}
		{{.FieldDescriptor.Name}} = {{.FieldDescriptor.EnumValue}}, {{.Alias}}
	{{end}}
	}
	{{end}}
	{{range .Classes}}

	// Defined in table: {{.DefinedTable}}
	{{.CSClassHeader}}
	public partial class {{.Name}}
	{
	{{if .IsCombine}}
		public tabtoy.Logger TableLogger = new tabtoy.Logger();
	{{end}}
	{{range .Fields}}
		{{.Comment}}
		{{.TypeCode}} {{.Alias}}
	{{end}}
	{{if .IsCombine}}
		#region Index code
	 	{{range $globalIndex}}Dictionary<{{.IndexType}}, {{.RowType}}> _{{.RowName}}By{{.IndexName}} = new Dictionary<{{.IndexType}}, {{.RowType}}>();
        public {{.RowType}} Get{{.RowName}}By{{.IndexName}}({{.IndexType}} {{.IndexName}}, {{.RowType}} def = default({{.RowType}}))
        {
            {{.RowType}} ret;
            if ( _{{.RowName}}By{{.IndexName}}.TryGetValue( {{.IndexName}}, out ret ) )
            {
                return ret;
            }

			if ( def == default({{.RowType}}) )
			{
				TableLogger.ErrorLine("Get{{.RowName}}By{{.IndexName}} failed, {{.IndexName}}: {0}", {{.IndexName}});
			}

            return def;
        }
		{{end}}
	{{range $verticalFields}}
		public {{.StructName}} Get{{.Name}}( )
		{
			return {{.Name}}[0];
		}
	{{end}}
		#endregion
		#region Deserialize code
		{{range $.Classes}}
		static tabtoy.DeserializeHandler<{{.Name}}> _{{.HeaderType}}DeserializeHandler;
		static tabtoy.DeserializeHandler<{{.Name}}> {{.HeaderType}}DeserializeHandler
		{
			get
			{
				if (_{{.Name}}DeserializeHandler == null )
				{
					_{{.Name}}DeserializeHandler = new tabtoy.DeserializeHandler<{{.HeaderType}}>(Deserialize);
				}

				return _{{.Name}}DeserializeHandler;
			}
		}
		public static void Deserialize( {{.Name}} ins, tabtoy.DataReader reader )
		{
			{{ if $.GenSerializeCode }}
 			int tag = -1;
            while ( -1 != (tag = reader.ReadTag()))
            {
                switch (tag)
                { {{range .Fields}}
                	case {{.Tag}}:
                	{
						{{.ReadCode}}
                	}
                	break; {{end}}
                }
             } {{end}}

			{{range $a, $row :=.IndexedFields}}
			// Build {{$row.FieldDescriptor.Name}} Index
			for( int i = 0;i< ins.{{$row.FieldDescriptor.Name}}.Count;i++)
			{
				var element = ins.{{$row.FieldDescriptor.Name}}[i];
				{{range $b, $key := .IndexKeys}}
				ins._{{$row.FieldDescriptor.Name}}By{{$key.HeaderType}}.Add(element.{{$key.HeaderType}}, element);
				{{end}}
			}
			{{end}}
		}{{end}}
		#endregion
	{{end}}

	} {{end}}

}
`

type indexField struct {
	TableIndex
}

func (self indexField) IndexName() string {
	return self.Index.Name
}

func (self indexField) RowType() string {
	return self.Row.Complex.Name
}

func (self indexField) RowName() string {
	return self.Row.Name
}

func (self indexField) IndexType() string {

	switch self.Index.Type {
	case model.FieldType_Int32:
		return "int"
	case model.FieldType_UInt32:
		return "uint"
	case model.FieldType_Int64:
		return "long"
	case model.FieldType_UInt64:
		return "ulong"
	case model.FieldType_String:
		return "string"
	case model.FieldType_Float:
		return "float"
	case model.FieldType_Bool:
		return "bool"
	case model.FieldType_Enum:

		return self.Index.Complex.Name
	default:
		log.Errorf("%s can not be index ", self.Index.String())
	}

	return "unknown"
}

type csharpField struct {
	*model.FieldDescriptor

	IndexKeys []*model.FieldDescriptor

	parentStruct *structModel
}

func (self csharpField) Alias() string {

	v := self.FieldDescriptor.Meta.GetString("Alias")
	if v == "" {
		return ""
	}

	return "// " + v
}

func (self csharpField) Comment() string {

	if self.FieldDescriptor.Comment == "" {
		return ""
	}

	// zjwps 建议修改
	return "/// <summary> \n		/// " + strings.Replace(self.FieldDescriptor.Comment, "\n", "\n		///", -1) + "\n		/// </summary>"
}

func (self csharpField) ReadCode() string {

	var baseType string

	var descHandlerCode string

	switch self.Type {
	case model.FieldType_Int32:
		baseType = "Int32"
	case model.FieldType_UInt32:
		baseType = "UInt32"
	case model.FieldType_Int64:
		baseType = "Int64"
	case model.FieldType_UInt64:
		baseType = "UInt64"
	case model.FieldType_String:
		baseType = "String"
	case model.FieldType_Float:
		baseType = "Float"
	case model.FieldType_Bool:
		baseType = "Bool"
	case model.FieldType_Enum:

		if self.Complex == nil {
			return "unknown"
		}

		baseType = fmt.Sprintf("Enum<%s>", self.Complex.Name)

	case model.FieldType_Struct:
		if self.Complex == nil {
			return "unknown"
		}

		baseType = fmt.Sprintf("Struct<%s>", self.Complex.Name)

	}

	if self.Type == model.FieldType_Struct {
		descHandlerCode = fmt.Sprintf("%sDeserializeHandler", self.Complex.Name)
	}

	if self.IsRepeated {
		return fmt.Sprintf("ins.%s.Add( reader.Read%s(%s) );", self.Name, baseType, descHandlerCode)
	} else {
		return fmt.Sprintf("ins.%s = reader.Read%s(%s);", self.Name, baseType, descHandlerCode)
	}

}

func (self csharpField) Tag() string {

	if self.parentStruct.IsCombine() {
		tag := model.MakeTag(int32(model.FieldType_Table), self.Order)

		return fmt.Sprintf("0x%x", tag)
	}

	return fmt.Sprintf("0x%x", self.FieldDescriptor.Tag())
}

func (self csharpField) StructName() string {
	if self.Complex == nil {
		return "[NotComplex]"
	}

	return self.Complex.Name
}

func (self csharpField) IsVerticalStruct() bool {
	if self.FieldDescriptor.Complex == nil {
		return false
	}

	return self.FieldDescriptor.Complex.File.Pragma.GetBool("Vertical")
}

func (self csharpField) TypeCode() string {

	var raw string

	switch self.Type {
	case model.FieldType_Int32:
		raw = "int"
	case model.FieldType_UInt32:
		raw = "uint"
	case model.FieldType_Int64:
		raw = "long"
	case model.FieldType_UInt64:
		raw = "ulong"
	case model.FieldType_String:
		raw = "string"
	case model.FieldType_Float:
		raw = "float"
	case model.FieldType_Bool:
		raw = "bool"
	case model.FieldType_Enum:
		if self.Complex == nil {
			log.Errorln("unknown enum type ", self.Type)
			return "unknown"
		}

		raw = self.Complex.Name
	case model.FieldType_Struct:
		if self.Complex == nil {
			log.Errorln("unknown struct type ", self.Type, self.FieldDescriptor.Name, self.FieldDescriptor.Parent.Name)
			return "unknown"
		}

		raw = self.Complex.Name

		// 非repeated的结构体
		if !self.IsRepeated {
			return fmt.Sprintf("public %s %s = new %s();", raw, self.Name, raw)
		}

	default:
		raw = "unknown"
	}

	if self.IsRepeated {
		return fmt.Sprintf("public List<%s> %s = new List<%s>();", raw, self.Name, raw)
	}

	return fmt.Sprintf("public %s %s = %s;", raw, self.Name, wrapCSharpDefaultValue(self.FieldDescriptor))
}

func wrapCSharpDefaultValue(fd *model.FieldDescriptor) string {
	switch fd.Type {
	case model.FieldType_Enum:
		return fmt.Sprintf("%s.%s", fd.Complex.Name, fd.DefaultValue())
	case model.FieldType_String:
		return fmt.Sprintf("\"%s\"", fd.DefaultValue())
	case model.FieldType_Float:
		return fmt.Sprintf("%sf", fd.DefaultValue())
	}

	return fd.DefaultValue()
}

type structModel struct {
	*model.Descriptor
	Fields        []csharpField
	IndexedFields []csharpField // 与csharpField.IndexKeys组成树状的索引层次
}

func (self *structModel) CSClassHeader() string {

	// zjwps 提供需求
	return self.File.Pragma.GetString("CSClassHeader")
}

func (self *structModel) DefinedTable() string {
	return self.File.Name
}

func (self *structModel) Name() string {
	return self.Descriptor.Name
}

func (self *structModel) IsCombine() bool {
	return self.Descriptor.Usage == model.DescriptorUsage_CombineStruct
}

type csharpFileModel struct {
	Namespace   string
	ToolVersion string
	Classes     []*structModel
	Enums       []*structModel
	Indexes     []indexField // 全局的索引

	VerticalFields []csharpField

	GenSerializeCode bool
}

type csharpPrinter struct {
}

func (self *csharpPrinter) Run(g *Globals) *Stream {

	tpl, err := template.New("csharp").Parse(csharpTemplate)
	if err != nil {
		log.Errorln(err)
		return nil
	}

	var m csharpFileModel

	if g.PackageName != "" {
		m.Namespace = g.PackageName
	} else {
		m.Namespace = g.FileDescriptor.Pragma.GetString("Package")
	}

	m.ToolVersion = g.Version
	m.GenSerializeCode = g.GenCSSerailizeCode

	// combinestruct的全局索引
	for _, ti := range g.GlobalIndexes {

		// 索引也限制
		if !ti.Index.Parent.File.MatchTag(".cs") {
			continue
		}

		m.Indexes = append(m.Indexes, indexField{TableIndex: ti})
	}

	// 遍历所有类型
	for _, d := range g.FileDescriptor.Descriptors {

		// 这给被限制输出
		if !d.File.MatchTag(".cs") {
			log.Infof("%s: %s", i18n.String(i18n.Printer_IgnoredByOutputTag), d.Name)
			continue
		}

		var sm structModel
		sm.Descriptor = d

		switch d.Kind {
		case model.DescriptorKind_Struct:
			m.Classes = append(m.Classes, &sm)
		case model.DescriptorKind_Enum:
			m.Enums = append(m.Enums, &sm)
		}

		// 遍历字段
		for _, fd := range d.Fields {

			// 对CombineStruct的XXDefine对应的字段
			if d.Usage == model.DescriptorUsage_CombineStruct {

				// 这个字段被限制输出
				if fd.Complex != nil && !fd.Complex.File.MatchTag(".cs") {
					continue
				}

				// 这个结构有索引才创建
				if fd.Complex != nil && len(fd.Complex.Indexes) > 0 {

					// 被索引的结构
					indexedField := csharpField{FieldDescriptor: fd, parentStruct: &sm}

					// 索引字段
					for _, key := range fd.Complex.Indexes {
						indexedField.IndexKeys = append(indexedField.IndexKeys, key)
					}

					sm.IndexedFields = append(sm.IndexedFields, indexedField)
				}

				if fd.Complex != nil && fd.Complex.File.Pragma.GetBool("Vertical") {
					m.VerticalFields = append(m.VerticalFields, csharpField{FieldDescriptor: fd, parentStruct: &sm})
				}

			}

			csField := csharpField{FieldDescriptor: fd, parentStruct: &sm}

			sm.Fields = append(sm.Fields, csField)

		}

	}

	bf := NewStream()

	err = tpl.Execute(bf.Buffer(), &m)
	if err != nil {
		log.Errorln(err)
		return nil
	}

	return bf
}

func init() {

	RegisterPrinter("cs", &csharpPrinter{})

}
<commit_msg>修复: C#导出错误<commit_after>package printer

import (
	"fmt"
	"text/template"

	"github.com/davyxu/tabtoy/v2/i18n"
	"github.com/davyxu/tabtoy/v2/model"
	"strings"
)

const csharpTemplate = `// Generated by github.com/davyxu/tabtoy
// Version: {{.ToolVersion}}
// DO NOT EDIT!!
using System.Collections.Generic;

namespace {{.Namespace}}{{$globalIndex:=.Indexes}}{{$verticalFields:=.VerticalFields}}
{
	{{range .Enums}}
	// Defined in table: {{.DefinedTable}}
	public enum {{.Name}}
	{
	{{range .Fields}}
		{{.Comment}}
		{{.FieldDescriptor.Name}} = {{.FieldDescriptor.EnumValue}}, {{.Alias}}
	{{end}}
	}
	{{end}}
	{{range .Classes}}

	// Defined in table: {{.DefinedTable}}
	{{.CSClassHeader}}
	public partial class {{.Name}}
	{
	{{if .IsCombine}}
		public tabtoy.Logger TableLogger = new tabtoy.Logger();
	{{end}}
	{{range .Fields}}
		{{.Comment}}
		{{.TypeCode}} {{.Alias}}
	{{end}}
	{{if .IsCombine}}
		#region Index code
	 	{{range $globalIndex}}Dictionary<{{.IndexType}}, {{.RowType}}> _{{.RowName}}By{{.IndexName}} = new Dictionary<{{.IndexType}}, {{.RowType}}>();
        public {{.RowType}} Get{{.RowName}}By{{.IndexName}}({{.IndexType}} {{.IndexName}}, {{.RowType}} def = default({{.RowType}}))
        {
            {{.RowType}} ret;
            if ( _{{.RowName}}By{{.IndexName}}.TryGetValue( {{.IndexName}}, out ret ) )
            {
                return ret;
            }

			if ( def == default({{.RowType}}) )
			{
				TableLogger.ErrorLine("Get{{.RowName}}By{{.IndexName}} failed, {{.IndexName}}: {0}", {{.IndexName}});
			}

            return def;
        }
		{{end}}
	{{range $verticalFields}}
		public {{.StructName}} Get{{.Name}}( )
		{
			return {{.Name}}[0];
		}
	{{end}}
		#endregion
		#region Deserialize code
		{{range $.Classes}}
		static tabtoy.DeserializeHandler<{{.Name}}> _{{.Name}}DeserializeHandler;
		static tabtoy.DeserializeHandler<{{.Name}}> {{.Name}}DeserializeHandler
		{
			get
			{
				if (_{{.Name}}DeserializeHandler == null )
				{
					_{{.Name}}DeserializeHandler = new tabtoy.DeserializeHandler<{{.Name}}>(Deserialize);
				}

				return _{{.Name}}DeserializeHandler;
			}
		}
		public static void Deserialize( {{.Name}} ins, tabtoy.DataReader reader )
		{
			{{ if $.GenSerializeCode }}
 			int tag = -1;
            while ( -1 != (tag = reader.ReadTag()))
            {
                switch (tag)
                { {{range .Fields}}
                	case {{.Tag}}:
                	{
						{{.ReadCode}}
                	}
                	break; {{end}}
                }
             } {{end}}

			{{range $a, $row :=.IndexedFields}}
			// Build {{$row.FieldDescriptor.Name}} Index
			for( int i = 0;i< ins.{{$row.FieldDescriptor.Name}}.Count;i++)
			{
				var element = ins.{{$row.FieldDescriptor.Name}}[i];
				{{range $b, $key := .IndexKeys}}
				ins._{{$row.FieldDescriptor.Name}}By{{$key.Name}}.Add(element.{{$key.Name}}, element);
				{{end}}
			}
			{{end}}
		}{{end}}
		#endregion
	{{end}}

	} {{end}}

}
`

type indexField struct {
	TableIndex
}

func (self indexField) IndexName() string {
	return self.Index.Name
}

func (self indexField) RowType() string {
	return self.Row.Complex.Name
}

func (self indexField) RowName() string {
	return self.Row.Name
}

func (self indexField) IndexType() string {

	switch self.Index.Type {
	case model.FieldType_Int32:
		return "int"
	case model.FieldType_UInt32:
		return "uint"
	case model.FieldType_Int64:
		return "long"
	case model.FieldType_UInt64:
		return "ulong"
	case model.FieldType_String:
		return "string"
	case model.FieldType_Float:
		return "float"
	case model.FieldType_Bool:
		return "bool"
	case model.FieldType_Enum:

		return self.Index.Complex.Name
	default:
		log.Errorf("%s can not be index ", self.Index.String())
	}

	return "unknown"
}

type csharpField struct {
	*model.FieldDescriptor

	IndexKeys []*model.FieldDescriptor

	parentStruct *structModel
}

func (self csharpField) Alias() string {

	v := self.FieldDescriptor.Meta.GetString("Alias")
	if v == "" {
		return ""
	}

	return "// " + v
}

func (self csharpField) Comment() string {

	if self.FieldDescriptor.Comment == "" {
		return ""
	}

	// zjwps 建议修改
	return "/// <summary> \n		/// " + strings.Replace(self.FieldDescriptor.Comment, "\n", "\n		///", -1) + "\n		/// </summary>"
}

func (self csharpField) ReadCode() string {

	var baseType string

	var descHandlerCode string

	switch self.Type {
	case model.FieldType_Int32:
		baseType = "Int32"
	case model.FieldType_UInt32:
		baseType = "UInt32"
	case model.FieldType_Int64:
		baseType = "Int64"
	case model.FieldType_UInt64:
		baseType = "UInt64"
	case model.FieldType_String:
		baseType = "String"
	case model.FieldType_Float:
		baseType = "Float"
	case model.FieldType_Bool:
		baseType = "Bool"
	case model.FieldType_Enum:

		if self.Complex == nil {
			return "unknown"
		}

		baseType = fmt.Sprintf("Enum<%s>", self.Complex.Name)

	case model.FieldType_Struct:
		if self.Complex == nil {
			return "unknown"
		}

		baseType = fmt.Sprintf("Struct<%s>", self.Complex.Name)

	}

	if self.Type == model.FieldType_Struct {
		descHandlerCode = fmt.Sprintf("%sDeserializeHandler", self.Complex.Name)
	}

	if self.IsRepeated {
		return fmt.Sprintf("ins.%s.Add( reader.Read%s(%s) );", self.Name, baseType, descHandlerCode)
	} else {
		return fmt.Sprintf("ins.%s = reader.Read%s(%s);", self.Name, baseType, descHandlerCode)
	}

}

func (self csharpField) Tag() string {

	if self.parentStruct.IsCombine() {
		tag := model.MakeTag(int32(model.FieldType_Table), self.Order)

		return fmt.Sprintf("0x%x", tag)
	}

	return fmt.Sprintf("0x%x", self.FieldDescriptor.Tag())
}

func (self csharpField) StructName() string {
	if self.Complex == nil {
		return "[NotComplex]"
	}

	return self.Complex.Name
}

func (self csharpField) IsVerticalStruct() bool {
	if self.FieldDescriptor.Complex == nil {
		return false
	}

	return self.FieldDescriptor.Complex.File.Pragma.GetBool("Vertical")
}

func (self csharpField) TypeCode() string {

	var raw string

	switch self.Type {
	case model.FieldType_Int32:
		raw = "int"
	case model.FieldType_UInt32:
		raw = "uint"
	case model.FieldType_Int64:
		raw = "long"
	case model.FieldType_UInt64:
		raw = "ulong"
	case model.FieldType_String:
		raw = "string"
	case model.FieldType_Float:
		raw = "float"
	case model.FieldType_Bool:
		raw = "bool"
	case model.FieldType_Enum:
		if self.Complex == nil {
			log.Errorln("unknown enum type ", self.Type)
			return "unknown"
		}

		raw = self.Complex.Name
	case model.FieldType_Struct:
		if self.Complex == nil {
			log.Errorln("unknown struct type ", self.Type, self.FieldDescriptor.Name, self.FieldDescriptor.Parent.Name)
			return "unknown"
		}

		raw = self.Complex.Name

		// 非repeated的结构体
		if !self.IsRepeated {
			return fmt.Sprintf("public %s %s = new %s();", raw, self.Name, raw)
		}

	default:
		raw = "unknown"
	}

	if self.IsRepeated {
		return fmt.Sprintf("public List<%s> %s = new List<%s>();", raw, self.Name, raw)
	}

	return fmt.Sprintf("public %s %s = %s;", raw, self.Name, wrapCSharpDefaultValue(self.FieldDescriptor))
}

func wrapCSharpDefaultValue(fd *model.FieldDescriptor) string {
	switch fd.Type {
	case model.FieldType_Enum:
		return fmt.Sprintf("%s.%s", fd.Complex.Name, fd.DefaultValue())
	case model.FieldType_String:
		return fmt.Sprintf("\"%s\"", fd.DefaultValue())
	case model.FieldType_Float:
		return fmt.Sprintf("%sf", fd.DefaultValue())
	}

	return fd.DefaultValue()
}

type structModel struct {
	*model.Descriptor
	Fields        []csharpField
	IndexedFields []csharpField // 与csharpField.IndexKeys组成树状的索引层次
}

func (self *structModel) CSClassHeader() string {

	// zjwps 提供需求
	return self.File.Pragma.GetString("CSClassHeader")
}

func (self *structModel) DefinedTable() string {
	return self.File.Name
}

func (self *structModel) Name() string {
	return self.Descriptor.Name
}

func (self *structModel) IsCombine() bool {
	return self.Descriptor.Usage == model.DescriptorUsage_CombineStruct
}

type csharpFileModel struct {
	Namespace   string
	ToolVersion string
	Classes     []*structModel
	Enums       []*structModel
	Indexes     []indexField // 全局的索引

	VerticalFields []csharpField

	GenSerializeCode bool
}

type csharpPrinter struct {
}

func (self *csharpPrinter) Run(g *Globals) *Stream {

	tpl, err := template.New("csharp").Parse(csharpTemplate)
	if err != nil {
		log.Errorln(err)
		return nil
	}

	var m csharpFileModel

	if g.PackageName != "" {
		m.Namespace = g.PackageName
	} else {
		m.Namespace = g.FileDescriptor.Pragma.GetString("Package")
	}

	m.ToolVersion = g.Version
	m.GenSerializeCode = g.GenCSSerailizeCode

	// combinestruct的全局索引
	for _, ti := range g.GlobalIndexes {

		// 索引也限制
		if !ti.Index.Parent.File.MatchTag(".cs") {
			continue
		}

		m.Indexes = append(m.Indexes, indexField{TableIndex: ti})
	}

	// 遍历所有类型
	for _, d := range g.FileDescriptor.Descriptors {

		// 这给被限制输出
		if !d.File.MatchTag(".cs") {
			log.Infof("%s: %s", i18n.String(i18n.Printer_IgnoredByOutputTag), d.Name)
			continue
		}

		var sm structModel
		sm.Descriptor = d

		switch d.Kind {
		case model.DescriptorKind_Struct:
			m.Classes = append(m.Classes, &sm)
		case model.DescriptorKind_Enum:
			m.Enums = append(m.Enums, &sm)
		}

		// 遍历字段
		for _, fd := range d.Fields {

			// 对CombineStruct的XXDefine对应的字段
			if d.Usage == model.DescriptorUsage_CombineStruct {

				// 这个字段被限制输出
				if fd.Complex != nil && !fd.Complex.File.MatchTag(".cs") {
					continue
				}

				// 这个结构有索引才创建
				if fd.Complex != nil && len(fd.Complex.Indexes) > 0 {

					// 被索引的结构
					indexedField := csharpField{FieldDescriptor: fd, parentStruct: &sm}

					// 索引字段
					for _, key := range fd.Complex.Indexes {
						indexedField.IndexKeys = append(indexedField.IndexKeys, key)
					}

					sm.IndexedFields = append(sm.IndexedFields, indexedField)
				}

				if fd.Complex != nil && fd.Complex.File.Pragma.GetBool("Vertical") {
					m.VerticalFields = append(m.VerticalFields, csharpField{FieldDescriptor: fd, parentStruct: &sm})
				}

			}

			csField := csharpField{FieldDescriptor: fd, parentStruct: &sm}

			sm.Fields = append(sm.Fields, csField)

		}

	}

	bf := NewStream()

	err = tpl.Execute(bf.Buffer(), &m)
	if err != nil {
		log.Errorln(err)
		return nil
	}

	return bf
}

func init() {

	RegisterPrinter("cs", &csharpPrinter{})

}
<|endoftext|>


<commit_before>// +build linux

package libcontainer

import "testing"

func TestStateStatus(t *testing.T) {
	states := map[containerState]Status{
		&stoppedState{}:  Stopped,
		&runningState{}:  Running,
		&restoredState{}: Running,
		&pausedState{}:   Paused,
		&createdState{}:  Created,
	}
	for s, status := range states {
		if s.status() != status {
			t.Fatalf("state returned %s but expected %s", s.status(), status)
		}
	}
}

func isStateTransitionError(err error) bool {
	_, ok := err.(*stateTransitionError)
	return ok
}

func TestStoppedStateTransition(t *testing.T) {
	s := &stoppedState{c: &linuxContainer{}}
	valid := []containerState{
		&stoppedState{},
		&runningState{},
		&restoredState{},
	}
	for _, v := range valid {
		if err := s.transition(v); err != nil {
			t.Fatal(err)
		}
	}
	err := s.transition(&pausedState{})
	if err == nil {
		t.Fatal("transition to paused state should fail")
	}
	if !isStateTransitionError(err) {
		t.Fatal("expected stateTransitionError")
	}
}

func TestPausedStateTransition(t *testing.T) {
	s := &pausedState{c: &linuxContainer{}}
	valid := []containerState{
		&pausedState{},
		&runningState{},
		&stoppedState{},
	}
	for _, v := range valid {
		if err := s.transition(v); err != nil {
			t.Fatal(err)
		}
	}
}

func TestRestoredStateTransition(t *testing.T) {
	s := &restoredState{c: &linuxContainer{}}
	valid := []containerState{
		&stoppedState{},
		&runningState{},
	}
	for _, v := range valid {
		if err := s.transition(v); err != nil {
			t.Fatal(err)
		}
	}
	err := s.transition(&createdState{})
	if err == nil {
		t.Fatal("transition to created state should fail")
	}
	if !isStateTransitionError(err) {
		t.Fatal("expected stateTransitionError")
	}
}
<commit_msg>add createdState and runningState status testcase<commit_after>// +build linux

package libcontainer

import "testing"

func TestStateStatus(t *testing.T) {
	states := map[containerState]Status{
		&stoppedState{}:  Stopped,
		&runningState{}:  Running,
		&restoredState{}: Running,
		&pausedState{}:   Paused,
		&createdState{}:  Created,
	}
	for s, status := range states {
		if s.status() != status {
			t.Fatalf("state returned %s but expected %s", s.status(), status)
		}
	}
}

func isStateTransitionError(err error) bool {
	_, ok := err.(*stateTransitionError)
	return ok
}

func TestStoppedStateTransition(t *testing.T) {
	s := &stoppedState{c: &linuxContainer{}}
	valid := []containerState{
		&stoppedState{},
		&runningState{},
		&restoredState{},
	}
	for _, v := range valid {
		if err := s.transition(v); err != nil {
			t.Fatal(err)
		}
	}
	err := s.transition(&pausedState{})
	if err == nil {
		t.Fatal("transition to paused state should fail")
	}
	if !isStateTransitionError(err) {
		t.Fatal("expected stateTransitionError")
	}
}

func TestPausedStateTransition(t *testing.T) {
	s := &pausedState{c: &linuxContainer{}}
	valid := []containerState{
		&pausedState{},
		&runningState{},
		&stoppedState{},
	}
	for _, v := range valid {
		if err := s.transition(v); err != nil {
			t.Fatal(err)
		}
	}
}

func TestRestoredStateTransition(t *testing.T) {
	s := &restoredState{c: &linuxContainer{}}
	valid := []containerState{
		&stoppedState{},
		&runningState{},
	}
	for _, v := range valid {
		if err := s.transition(v); err != nil {
			t.Fatal(err)
		}
	}
	err := s.transition(&createdState{})
	if err == nil {
		t.Fatal("transition to created state should fail")
	}
	if !isStateTransitionError(err) {
		t.Fatal("expected stateTransitionError")
	}
}

func TestRunningStateTransition(t *testing.T) {
	s := &runningState{c: &linuxContainer{}}
	valid := []containerState{
		&stoppedState{},
		&pausedState{},
		&runningState{},
	}
	for _, v := range valid {
		if err := s.transition(v); err != nil {
			t.Fatal(err)
		}
	}

	err := s.transition(&createdState{})
	if err == nil {
		t.Fatal("transition to created state should fail")
	}
	if !isStateTransitionError(err) {
		t.Fatal("expected stateTransitionError")
	}
}

func TestCreatedStateTransition(t *testing.T) {
	s := &createdState{c: &linuxContainer{}}
	valid := []containerState{
		&stoppedState{},
		&pausedState{},
		&runningState{},
		&createdState{},
	}
	for _, v := range valid {
		if err := s.transition(v); err != nil {
			t.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>// +build cgo,linux cgo,freebsd

package system

/*
#include <unistd.h>
#include <limits.h>

int GetLongBit() {
#ifdef _SC_LONG_BIT
    int longbits;

    longbits = sysconf(_SC_LONG_BIT);
    if (longbits <  0) {
        longbits = (CHAR_BIT * sizeof(long));
    }
    return longbits;
#else
    return (CHAR_BIT * sizeof(long));
#endif
}
*/
import "C"

func GetClockTicks() int {
	return int(C.sysconf(C._SC_CLK_TCK))
}

func GetLongBit() int {
	return int(C.GetLongBit())
}
<commit_msg>Remove unused GetLongBit() function.<commit_after>// +build cgo,linux cgo,freebsd

package system

/*
#include <unistd.h>
*/
import "C"

func GetClockTicks() int {
	return int(C.sysconf(C._SC_CLK_TCK))
}
<|endoftext|>


<commit_before>package s3

import (
	"fmt"
	"io"
	stdlog "log"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/sirupsen/logrus"

	"github.com/pachyderm/pachyderm/src/client"
)

func attachBucketRoutes(router *mux.Router, handler bucketHandler) {
	router.Methods("GET", "HEAD").Queries("location", "").HandlerFunc(handler.location)
	router.Methods("GET", "HEAD").HandlerFunc(handler.get)
	router.Methods("PUT").HandlerFunc(handler.put)
	router.Methods("DELETE").HandlerFunc(handler.del)
}

// Server runs an HTTP server with an S3-like API for PFS. This allows you to
// use s3 clients to acccess PFS contents.
//
// This returns an `http.Server` instance. It is the responsibility of the
// caller to:
// 1) start the returned server
// 2) close `errLogWriter`
// 3) remove `multipartDir`, unless you want to persist in-flight multipart
//    contents between server runs
// Furthermore, it's possible for the caller to gracefully shutdown the server
// if desired; see the `http` package for details.
//
// If `multipartDir` is an empty string, multipart uploads are disabled.
//
// Bucket names correspond to repo names, and files are accessible via the s3
// key pattern "<branch>/<filepath>". For example, to get the file "a/b/c.txt"
// on the "foo" repo's "master" branch, you'd making an s3 get request with
// bucket = "foo", key = "master/a/b/c.txt".
//
// Note: in s3, bucket names are constrained by IETF RFC 1123, (and its
// predecessor RFC 952) but pachyderm's repo naming constraints are slightly
// more liberal. While the bucket name validation in this server is looser
// than s3's, it still doesn't support all possible pachyderm repo names,
// which means some pachyderm repos will not be serviceable by this.
//
// Note: In `s3cmd`, you must set the access key and secret key, even though
// this API will ignore them - otherwise, you'll get an opaque config error:
// https://github.com/s3tools/s3cmd/issues/845#issuecomment-464885959
func Server(pc *client.APIClient, port uint16, errLogWriter io.Writer, multipartDir string) *http.Server {
	router := mux.NewRouter()
	router.Handle(`/`, newRootHandler(pc)).Methods("GET", "HEAD")

	// Bucket-related routes. Repo validation regex is the same that the aws
	// cli uses. There's two routers - one with a trailing a slash and one
	// without. Both route to the same handlers, i.e. a request to `/foo` is
	// the same as `/foo/`. This is used instead of mux's builtin "strict
	// slash" functionality, because that uses redirects which doesn't always
	// play nice with s3 clients.
	bucketHandler := newBucketHandler(pc)
	trailingSlashBucketRouter := router.Path(`/{bucket:[a-zA-Z0-9.\-_]{1,255}}/`).Subrouter()
	attachBucketRoutes(trailingSlashBucketRouter, bucketHandler)
	bucketRouter := router.Path(`/{bucket:[a-zA-Z0-9.\-_]{1,255}}`).Subrouter()
	attachBucketRoutes(bucketRouter, bucketHandler)

	// object-related routes
	objectRouter := router.Path(`/{bucket:[a-zA-Z0-9.\-_]{1,255}}/{file:.+}`).Subrouter()
	if multipartDir != "" {
		// Multipart handlers are only registered if a root dir is specified
		multipartHandler := newMultipartHandler(pc, multipartDir)
		objectRouter.Methods("GET", "HEAD").Queries("uploadId", "").HandlerFunc(multipartHandler.list)
		objectRouter.Methods("POST").Queries("uploads", "").HandlerFunc(multipartHandler.init)
		objectRouter.Methods("POST").Queries("uploadId", "").HandlerFunc(multipartHandler.complete)
		objectRouter.Methods("PUT").Queries("uploadId", "").HandlerFunc(multipartHandler.put)
		objectRouter.Methods("DELETE").Queries("uploadId", "").HandlerFunc(multipartHandler.del)
	}
	objectHandler := newObjectHandler(pc)
	objectRouter.Methods("GET", "HEAD").HandlerFunc(objectHandler.get)
	objectRouter.Methods("PUT").HandlerFunc(objectHandler.put)
	objectRouter.Methods("DELETE").HandlerFunc(objectHandler.del)

	// TODO: this will trigger for paths that are not valid utf-8 strings, giving the incorrect error message. See:
	// ./etc/testing/s3gateway/conformance.py --nose-args 's3tests.functional.test_s3:test_object_create_unreadable' --no-persist
	router.NotFoundHandler = http.HandlerFunc(invalidBucketNameError)
	router.MethodNotAllowedHandler = http.HandlerFunc(methodNotAllowedError)

	return &http.Server{
		Addr: fmt.Sprintf(":%d", port),
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// TODO: reduce log level
			logrus.Infof("s3gateway: http request: %s %s", r.Method, r.RequestURI)
			router.ServeHTTP(w, r)
		}),
		ErrorLog: stdlog.New(errLogWriter, "s3gateway: ", 0),
	}
}
<commit_msg>Handle non-implemented bucket operations<commit_after>package s3

import (
	"fmt"
	"io"
	stdlog "log"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/sirupsen/logrus"

	"github.com/pachyderm/pachyderm/src/client"
)

func attachBucketRoutes(router *mux.Router, handler bucketHandler) {
	router.Methods("GET", "PUT").Queries("accelerate", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT").Queries("acl", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT", "DELETE").Queries("analytics", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT", "DELETE").Queries("cors", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT", "DELETE").Queries("encryption", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT", "DELETE").Queries("inventory", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT", "DELETE").Queries("lifecycle", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT").Queries("logging", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT", "DELETE").Queries("metrics", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT").Queries("notification", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT").Queries("object-lock", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT", "DELETE").Queries("policy", "").HandlerFunc(notImplementedError)
	router.Methods("GET").Queries("policyStatus", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT", "DELETE").Queries("publicAccessBlock", "").HandlerFunc(notImplementedError)
	router.Methods("PUT", "DELETE").Queries("replication", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT").Queries("requestPayment", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT", "DELETE").Queries("tagging", "").HandlerFunc(notImplementedError)
	router.Methods("GET").Queries("uploads", "").HandlerFunc(notImplementedError) // maybe worth implementing at some point
	router.Methods("GET", "PUT").Queries("versioning", "").HandlerFunc(notImplementedError)
	router.Methods("GET").Queries("versions", "").HandlerFunc(notImplementedError)
	router.Methods("GET", "PUT", "DELETE").Queries("website", "").HandlerFunc(notImplementedError)

	router.Methods("GET", "HEAD").Queries("location", "").HandlerFunc(handler.location)
	router.Methods("GET", "HEAD").HandlerFunc(handler.get)
	router.Methods("PUT").HandlerFunc(handler.put)
	router.Methods("DELETE").HandlerFunc(handler.del)
}

// Server runs an HTTP server with an S3-like API for PFS. This allows you to
// use s3 clients to acccess PFS contents.
//
// This returns an `http.Server` instance. It is the responsibility of the
// caller to:
// 1) start the returned server
// 2) close `errLogWriter`
// 3) remove `multipartDir`, unless you want to persist in-flight multipart
//    contents between server runs
// Furthermore, it's possible for the caller to gracefully shutdown the server
// if desired; see the `http` package for details.
//
// If `multipartDir` is an empty string, multipart uploads are disabled.
//
// Bucket names correspond to repo names, and files are accessible via the s3
// key pattern "<branch>/<filepath>". For example, to get the file "a/b/c.txt"
// on the "foo" repo's "master" branch, you'd making an s3 get request with
// bucket = "foo", key = "master/a/b/c.txt".
//
// Note: in s3, bucket names are constrained by IETF RFC 1123, (and its
// predecessor RFC 952) but pachyderm's repo naming constraints are slightly
// more liberal. While the bucket name validation in this server is looser
// than s3's, it still doesn't support all possible pachyderm repo names,
// which means some pachyderm repos will not be serviceable by this.
//
// Note: In `s3cmd`, you must set the access key and secret key, even though
// this API will ignore them - otherwise, you'll get an opaque config error:
// https://github.com/s3tools/s3cmd/issues/845#issuecomment-464885959
func Server(pc *client.APIClient, port uint16, errLogWriter io.Writer, multipartDir string) *http.Server {
	router := mux.NewRouter()
	router.Handle(`/`, newRootHandler(pc)).Methods("GET", "HEAD")

	// Bucket-related routes. Repo validation regex is the same that the aws
	// cli uses. There's two routers - one with a trailing a slash and one
	// without. Both route to the same handlers, i.e. a request to `/foo` is
	// the same as `/foo/`. This is used instead of mux's builtin "strict
	// slash" functionality, because that uses redirects which doesn't always
	// play nice with s3 clients.
	bucketHandler := newBucketHandler(pc)
	trailingSlashBucketRouter := router.Path(`/{bucket:[a-zA-Z0-9.\-_]{1,255}}/`).Subrouter()
	attachBucketRoutes(trailingSlashBucketRouter, bucketHandler)
	bucketRouter := router.Path(`/{bucket:[a-zA-Z0-9.\-_]{1,255}}`).Subrouter()
	attachBucketRoutes(bucketRouter, bucketHandler)

	// object-related routes
	objectRouter := router.Path(`/{bucket:[a-zA-Z0-9.\-_]{1,255}}/{file:.+}`).Subrouter()
	if multipartDir != "" {
		// Multipart handlers are only registered if a root dir is specified
		multipartHandler := newMultipartHandler(pc, multipartDir)
		objectRouter.Methods("GET", "HEAD").Queries("uploadId", "").HandlerFunc(multipartHandler.list)
		objectRouter.Methods("POST").Queries("uploads", "").HandlerFunc(multipartHandler.init)
		objectRouter.Methods("POST").Queries("uploadId", "").HandlerFunc(multipartHandler.complete)
		objectRouter.Methods("PUT").Queries("uploadId", "").HandlerFunc(multipartHandler.put)
		objectRouter.Methods("DELETE").Queries("uploadId", "").HandlerFunc(multipartHandler.del)
	}
	objectHandler := newObjectHandler(pc)
	objectRouter.Methods("GET", "HEAD").HandlerFunc(objectHandler.get)
	objectRouter.Methods("PUT").HandlerFunc(objectHandler.put)
	objectRouter.Methods("DELETE").HandlerFunc(objectHandler.del)

	// TODO: this will trigger for paths that are not valid utf-8 strings, giving the incorrect error message. See:
	// ./etc/testing/s3gateway/conformance.py --nose-args 's3tests.functional.test_s3:test_object_create_unreadable' --no-persist
	router.NotFoundHandler = http.HandlerFunc(invalidBucketNameError)
	router.MethodNotAllowedHandler = http.HandlerFunc(methodNotAllowedError)

	return &http.Server{
		Addr: fmt.Sprintf(":%d", port),
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// TODO: reduce log level
			logrus.Infof("s3gateway: http request: %s %s", r.Method, r.RequestURI)
			router.ServeHTTP(w, r)
		}),
		ErrorLog: stdlog.New(errLogWriter, "s3gateway: ", 0),
	}
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package backend

import (
	"bytes"
	"fmt"
	"io"
	"sort"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/transport"

	"crypto/ecdsa"
	crand "crypto/rand"
	"math/big"

	"crypto"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/asn1"
	"encoding/gob"

	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric/orderer/rawledger"
	"github.com/hyperledger/fabric/orderer/sbft/connection"
	"github.com/hyperledger/fabric/orderer/sbft/persist"
	s "github.com/hyperledger/fabric/orderer/sbft/simplebft"
	cb "github.com/hyperledger/fabric/protos/common"
	ab "github.com/hyperledger/fabric/protos/orderer"
	"github.com/op/go-logging"
)

const headerIndex = 0
const signaturesIndex = 1
const metadataLen = 2

var logger = logging.MustGetLogger("backend")

type Backend struct {
	consensus s.Receiver
	conn      *connection.Manager

	lock  sync.Mutex
	peers map[uint64]chan<- *s.Msg

	self     *PeerInfo
	peerInfo map[string]*PeerInfo

	queue chan Executable

	persistence *persist.Persist
	ledger      rawledger.ReadWriter
}

type consensusConn Backend

type PeerInfo struct {
	info connection.PeerInfo
	id   uint64
}

type peerInfoSlice []*PeerInfo

func (pi peerInfoSlice) Len() int {
	return len(pi)
}

func (pi peerInfoSlice) Less(i, j int) bool {
	return strings.Compare(pi[i].info.Fingerprint(), pi[j].info.Fingerprint()) == -1
}

func (pi peerInfoSlice) Swap(i, j int) {
	pi[i], pi[j] = pi[j], pi[i]
}

func NewBackend(peers map[string][]byte, conn *connection.Manager, rl rawledger.ReadWriter, persist *persist.Persist) (*Backend, error) {
	c := &Backend{
		conn:     conn,
		peers:    make(map[uint64]chan<- *s.Msg),
		peerInfo: make(map[string]*PeerInfo),
		ledger:   rl,
	}

	var peerInfo []*PeerInfo
	for addr, cert := range peers {
		pi, err := connection.NewPeerInfo(addr, cert)
		if err != nil {
			return nil, err
		}
		cpi := &PeerInfo{info: pi}
		if pi.Fingerprint() == conn.Self.Fingerprint() {
			c.self = cpi
		}
		peerInfo = append(peerInfo, cpi)
		c.peerInfo[pi.Fingerprint()] = cpi
	}

	sort.Sort(peerInfoSlice(peerInfo))
	for i, pi := range peerInfo {
		pi.id = uint64(i)
		logger.Infof("replica %d: %s", i, pi.info.Fingerprint())
	}

	if c.self == nil {
		return nil, fmt.Errorf("peer list does not contain local node")
	}

	logger.Infof("we are replica %d (%s)", c.self.id, c.self.info)

	for _, peer := range c.peerInfo {
		if peer == c.self {
			continue
		}
		go c.connectWorker(peer)
	}
	RegisterConsensusServer(conn.Server, (*consensusConn)(c))
	c.persistence = persist
	c.queue = make(chan Executable)
	go c.run()
	return c, nil
}

func (c *Backend) GetMyId() uint64 {
	return c.self.id
}

func (c *Backend) connectWorker(peer *PeerInfo) {
	timeout := 1 * time.Second

	delay := time.After(0)
	for {
		// pace reconnect attempts
		<-delay

		// set up for next
		delay = time.After(timeout)

		logger.Infof("connecting to replica %d (%s)", peer.id, peer.info)
		conn, err := c.conn.DialPeer(peer.info, grpc.WithBlock(), grpc.WithTimeout(timeout))
		if err != nil {
			logger.Warningf("could not connect to replica %d (%s): %s", peer.id, peer.info, err)
			continue
		}

		ctx := context.TODO()

		client := NewConsensusClient(conn)
		consensus, err := client.Consensus(ctx, &Handshake{})
		if err != nil {
			logger.Warningf("could not establish consensus stream with replica %d (%s): %s", peer.id, peer.info, err)
			continue
		}
		logger.Noticef("connection to replica %d (%s) established", peer.id, peer.info)

		for {
			msg, err := consensus.Recv()
			if err == io.EOF || err == transport.ErrConnClosing {
				break
			}
			if err != nil {
				logger.Warningf("consensus stream with replica %d (%s) broke: %v", peer.id, peer.info, err)
				break
			}
			c.enqueueForReceive(msg, peer.id)
		}
	}
}

func (b *Backend) enqueueConnection(peerid uint64) {
	go func() {
		b.queue <- &connectionEvent{peerid: peerid}
	}()
}

func (b *Backend) enqueueRequest(request []byte) {
	go func() {
		b.queue <- &requestEvent{req: request}
	}()
}

func (b *Backend) enqueueForReceive(msg *s.Msg, src uint64) {
	go func() {
		b.queue <- &msgEvent{msg: msg, src: src}
	}()
}

func (b *Backend) initTimer(t *Timer, d time.Duration) {
	send := func() {
		if t.execute {
			b.queue <- t
		}
	}
	time.AfterFunc(d, send)
}

func (b *Backend) run() {
	for {
		e := <-b.queue
		e.Execute(b)
	}
}

// gRPC interface
func (c *consensusConn) Consensus(_ *Handshake, srv Consensus_ConsensusServer) error {
	pi := connection.GetPeerInfo(srv)
	peer, ok := c.peerInfo[pi.Fingerprint()]

	if !ok || !peer.info.Cert().Equal(pi.Cert()) {
		logger.Infof("rejecting connection from unknown replica %s", pi)
		return fmt.Errorf("unknown peer certificate")
	}
	logger.Infof("connection from replica %d (%s)", peer.id, pi)

	ch := make(chan *s.Msg)
	c.lock.Lock()
	if oldch, ok := c.peers[peer.id]; ok {
		logger.Debugf("replacing connection from replica %d", peer.id)
		close(oldch)
	}
	c.peers[peer.id] = ch
	c.lock.Unlock()
	((*Backend)(c)).enqueueConnection(peer.id)

	var err error
	for msg := range ch {
		err = srv.Send(msg)
		if err != nil {
			c.lock.Lock()
			delete(c.peers, peer.id)
			c.lock.Unlock()

			logger.Infof("lost connection from replica %d (%s): %s", peer.id, pi, err)
		}
	}

	return err
}

func (c *Backend) Broadcast(msg *s.Msg) error {
	c.lock.Lock()
	for _, ch := range c.peers {
		ch <- msg
	}
	c.lock.Unlock()
	return nil
}

func (c *Backend) Unicast(msg *s.Msg, dest uint64) error {
	c.lock.Lock()
	ch, ok := c.peers[dest]
	c.lock.Unlock()

	if !ok {
		err := fmt.Errorf("peer not found: %v", dest)
		logger.Debug(err)
		return err
	}
	ch <- msg
	return nil
}

func (t *Backend) SetReceiver(recv s.Receiver) {
	t.consensus = recv
}

func (t *Backend) Send(msg *s.Msg, dest uint64) {
	if dest == t.self.id {
		t.enqueueForReceive(msg, t.self.id)
		return
	}
	t.Unicast(msg, dest)
}

func (t *Backend) Timer(d time.Duration, tf func()) s.Canceller {
	tm := &Timer{tf: tf, execute: true}
	t.initTimer(tm, d)
	return tm
}

// Deliver writes the ledger
func (t *Backend) Deliver(batch *s.Batch) {
	blockContents := make([]*cb.Envelope, 0, len(batch.Payloads))
	for _, p := range batch.Payloads {
		envelope := &cb.Envelope{}
		err := proto.Unmarshal(p, envelope)
		if err == nil {
			blockContents = append(blockContents, envelope)
		} else {
			logger.Warningf("Payload cannot be unmarshalled.")
		}
	}
	block := rawledger.CreateNextBlock(t.ledger, blockContents)
	// This a quick and dirty solution to make it work.
	// SBFT needs to use Rawledger's structures and signatures over the Block.
	block.Metadata.Metadata[headerIndex] = batch.Header
	block.Metadata.Metadata[signaturesIndex] = encodeSignatures(batch.Signatures)
	t.ledger.Append(block)
}

func (t *Backend) Persist(key string, data proto.Message) {
	if data == nil {
		t.persistence.DelState(key)
	} else {
		bytes, err := proto.Marshal(data)
		if err != nil {
			panic(err)
		}
		t.persistence.StoreState(key, bytes)
	}
}

func (t *Backend) Restore(key string, out proto.Message) bool {
	val, err := t.persistence.ReadState(key)
	if err != nil {
		return false
	}
	err = proto.Unmarshal(val, out)
	return (err == nil)
}

func (t *Backend) LastBatch() *s.Batch {
	it, _ := t.ledger.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Newest{}})
	block, status := it.Next()
	data := block.Data.Data
	if status != cb.Status_SUCCESS {
		panic("Fatal ledger error: unable to get last block.")
	}
	header := getHeader(block.Metadata)
	sgns := decodeSignatures(getEncodedSignatures(block.Metadata))
	batch := s.Batch{Header: header, Payloads: data, Signatures: sgns}
	return &batch
}

func (t *Backend) Sign(data []byte) []byte {
	return Sign(t.conn.Cert.PrivateKey, data)
}

func (t *Backend) CheckSig(data []byte, src uint64, sig []byte) error {
	leaf := t.conn.Cert.Leaf
	if leaf == nil {
		panic("No public key found: certificate leaf is nil.")
	}
	return CheckSig(leaf.PublicKey, data, sig)
}

func (t *Backend) Reconnect(replica uint64) {
	t.enqueueConnection(replica)
}

func Sign(privateKey crypto.PrivateKey, data []byte) []byte {
	var err error
	var encsig []byte
	hash := sha256.Sum256(data)
	switch pvk := privateKey.(type) {
	case *rsa.PrivateKey:
		encsig, err = pvk.Sign(crand.Reader, hash[:], crypto.SHA256)
		if err != nil {
			panic(err)
		}
	case *ecdsa.PrivateKey:
		r, s, err := ecdsa.Sign(crand.Reader, pvk, hash[:])
		if err != nil {
			panic(err)
		}
		encsig, err = asn1.Marshal(struct{ R, S *big.Int }{r, s})
	default:
		panic("Unsupported private key type given.")
	}
	if err != nil {
		panic(err)
	}
	return encsig
}

func CheckSig(publicKey crypto.PublicKey, data []byte, sig []byte) error {
	hash := sha256.Sum256(data)
	switch p := publicKey.(type) {
	case *ecdsa.PublicKey:
		s := struct{ R, S *big.Int }{}
		rest, err := asn1.Unmarshal(sig, &s)
		if err != nil {
			return err
		}
		if len(rest) != 0 {
			return fmt.Errorf("invalid signature (problem with asn unmarshalling for ECDSA)")
		}
		ok := ecdsa.Verify(p, hash[:], s.R, s.S)
		if !ok {
			return fmt.Errorf("invalid signature (problem with verification)")
		}
		return nil
	case *rsa.PublicKey:
		err := rsa.VerifyPKCS1v15(p, crypto.SHA256, hash[:], sig)
		return err
	default:
		return fmt.Errorf("Unsupported public key type.")
	}
}

func getHeader(metadata *cb.BlockMetadata) []byte {
	if metadata == nil || len(metadata.Metadata) < metadataLen {
		return nil
	}
	return metadata.Metadata[headerIndex]
}

func getEncodedSignatures(metadata *cb.BlockMetadata) []byte {
	if metadata == nil || len(metadata.Metadata) < metadataLen {
		return nil
	}
	return metadata.Metadata[signaturesIndex]
}

func encodeSignatures(signatures map[uint64][]byte) []byte {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(signatures)
	if err != nil {
		panic(err)
	}
	return buf.Bytes()
}

func decodeSignatures(encodedSignatures []byte) map[uint64][]byte {
	if len(encodedSignatures) == 0 {
		return nil
	}
	buf := bytes.NewBuffer(encodedSignatures)
	var r map[uint64][]byte
	dec := gob.NewDecoder(buf)
	err := dec.Decode(&r)
	if err != nil {
		panic(err)
	}
	return r
}
<commit_msg>Use a local copy of last batch<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package backend

import (
	"bytes"
	"fmt"
	"io"
	"sort"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/transport"

	"crypto/ecdsa"
	crand "crypto/rand"
	"math/big"

	"crypto"
	"crypto/rsa"
	"crypto/sha256"
	"encoding/asn1"
	"encoding/gob"

	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric/orderer/rawledger"
	"github.com/hyperledger/fabric/orderer/sbft/connection"
	"github.com/hyperledger/fabric/orderer/sbft/persist"
	s "github.com/hyperledger/fabric/orderer/sbft/simplebft"
	cb "github.com/hyperledger/fabric/protos/common"
	"github.com/op/go-logging"
)

const headerIndex = 0
const signaturesIndex = 1
const metadataLen = 2

var logger = logging.MustGetLogger("backend")

type Backend struct {
	consensus s.Receiver
	conn      *connection.Manager

	lock  sync.Mutex
	peers map[uint64]chan<- *s.Msg

	self     *PeerInfo
	peerInfo map[string]*PeerInfo

	queue chan Executable

	persistence *persist.Persist
	ledger      rawledger.ReadWriter

	lastBatch *s.Batch
}

type consensusConn Backend

type PeerInfo struct {
	info connection.PeerInfo
	id   uint64
}

type peerInfoSlice []*PeerInfo

func (pi peerInfoSlice) Len() int {
	return len(pi)
}

func (pi peerInfoSlice) Less(i, j int) bool {
	return strings.Compare(pi[i].info.Fingerprint(), pi[j].info.Fingerprint()) == -1
}

func (pi peerInfoSlice) Swap(i, j int) {
	pi[i], pi[j] = pi[j], pi[i]
}

func NewBackend(peers map[string][]byte, conn *connection.Manager, rl rawledger.ReadWriter, persist *persist.Persist) (*Backend, error) {
	c := &Backend{
		conn:     conn,
		peers:    make(map[uint64]chan<- *s.Msg),
		peerInfo: make(map[string]*PeerInfo),
		ledger:   rl,
	}

	var peerInfo []*PeerInfo
	for addr, cert := range peers {
		pi, err := connection.NewPeerInfo(addr, cert)
		if err != nil {
			return nil, err
		}
		cpi := &PeerInfo{info: pi}
		if pi.Fingerprint() == conn.Self.Fingerprint() {
			c.self = cpi
		}
		peerInfo = append(peerInfo, cpi)
		c.peerInfo[pi.Fingerprint()] = cpi
	}

	sort.Sort(peerInfoSlice(peerInfo))
	for i, pi := range peerInfo {
		pi.id = uint64(i)
		logger.Infof("replica %d: %s", i, pi.info.Fingerprint())
	}

	if c.self == nil {
		return nil, fmt.Errorf("peer list does not contain local node")
	}

	logger.Infof("we are replica %d (%s)", c.self.id, c.self.info)

	for _, peer := range c.peerInfo {
		if peer == c.self {
			continue
		}
		go c.connectWorker(peer)
	}
	RegisterConsensusServer(conn.Server, (*consensusConn)(c))
	c.persistence = persist
	c.lastBatch = &s.Batch{Header: nil, Signatures: nil, Payloads: [][]byte{}}
	c.queue = make(chan Executable)
	go c.run()
	return c, nil
}

func (c *Backend) GetMyId() uint64 {
	return c.self.id
}

func (c *Backend) connectWorker(peer *PeerInfo) {
	timeout := 1 * time.Second

	delay := time.After(0)
	for {
		// pace reconnect attempts
		<-delay

		// set up for next
		delay = time.After(timeout)

		logger.Infof("connecting to replica %d (%s)", peer.id, peer.info)
		conn, err := c.conn.DialPeer(peer.info, grpc.WithBlock(), grpc.WithTimeout(timeout))
		if err != nil {
			logger.Warningf("could not connect to replica %d (%s): %s", peer.id, peer.info, err)
			continue
		}

		ctx := context.TODO()

		client := NewConsensusClient(conn)
		consensus, err := client.Consensus(ctx, &Handshake{})
		if err != nil {
			logger.Warningf("could not establish consensus stream with replica %d (%s): %s", peer.id, peer.info, err)
			continue
		}
		logger.Noticef("connection to replica %d (%s) established", peer.id, peer.info)

		for {
			msg, err := consensus.Recv()
			if err == io.EOF || err == transport.ErrConnClosing {
				break
			}
			if err != nil {
				logger.Warningf("consensus stream with replica %d (%s) broke: %v", peer.id, peer.info, err)
				break
			}
			c.enqueueForReceive(msg, peer.id)
		}
	}
}

func (b *Backend) enqueueConnection(peerid uint64) {
	go func() {
		b.queue <- &connectionEvent{peerid: peerid}
	}()
}

func (b *Backend) enqueueRequest(request []byte) {
	go func() {
		b.queue <- &requestEvent{req: request}
	}()
}

func (b *Backend) enqueueForReceive(msg *s.Msg, src uint64) {
	go func() {
		b.queue <- &msgEvent{msg: msg, src: src}
	}()
}

func (b *Backend) initTimer(t *Timer, d time.Duration) {
	send := func() {
		if t.execute {
			b.queue <- t
		}
	}
	time.AfterFunc(d, send)
}

func (b *Backend) run() {
	for {
		e := <-b.queue
		e.Execute(b)
	}
}

// gRPC interface
func (c *consensusConn) Consensus(_ *Handshake, srv Consensus_ConsensusServer) error {
	pi := connection.GetPeerInfo(srv)
	peer, ok := c.peerInfo[pi.Fingerprint()]

	if !ok || !peer.info.Cert().Equal(pi.Cert()) {
		logger.Infof("rejecting connection from unknown replica %s", pi)
		return fmt.Errorf("unknown peer certificate")
	}
	logger.Infof("connection from replica %d (%s)", peer.id, pi)

	ch := make(chan *s.Msg)
	c.lock.Lock()
	if oldch, ok := c.peers[peer.id]; ok {
		logger.Debugf("replacing connection from replica %d", peer.id)
		close(oldch)
	}
	c.peers[peer.id] = ch
	c.lock.Unlock()
	((*Backend)(c)).enqueueConnection(peer.id)

	var err error
	for msg := range ch {
		err = srv.Send(msg)
		if err != nil {
			c.lock.Lock()
			delete(c.peers, peer.id)
			c.lock.Unlock()

			logger.Infof("lost connection from replica %d (%s): %s", peer.id, pi, err)
		}
	}

	return err
}

func (c *Backend) Broadcast(msg *s.Msg) error {
	c.lock.Lock()
	for _, ch := range c.peers {
		ch <- msg
	}
	c.lock.Unlock()
	return nil
}

func (c *Backend) Unicast(msg *s.Msg, dest uint64) error {
	c.lock.Lock()
	ch, ok := c.peers[dest]
	c.lock.Unlock()

	if !ok {
		err := fmt.Errorf("peer not found: %v", dest)
		logger.Debug(err)
		return err
	}
	ch <- msg
	return nil
}

func (t *Backend) SetReceiver(recv s.Receiver) {
	t.consensus = recv
}

func (t *Backend) Send(msg *s.Msg, dest uint64) {
	if dest == t.self.id {
		t.enqueueForReceive(msg, t.self.id)
		return
	}
	t.Unicast(msg, dest)
}

func (t *Backend) Timer(d time.Duration, tf func()) s.Canceller {
	tm := &Timer{tf: tf, execute: true}
	t.initTimer(tm, d)
	return tm
}

// Deliver writes the ledger
func (t *Backend) Deliver(batch *s.Batch) {
	blockContents := make([]*cb.Envelope, 0, len(batch.Payloads))
	for _, p := range batch.Payloads {
		envelope := &cb.Envelope{}
		err := proto.Unmarshal(p, envelope)
		if err == nil {
			blockContents = append(blockContents, envelope)
		} else {
			logger.Warningf("Payload cannot be unmarshalled.")
		}
	}
	block := rawledger.CreateNextBlock(t.ledger, blockContents)
	// This a quick and dirty solution to make it work.
	// SBFT needs to use Rawledger's structures and signatures over the Block.
	block.Metadata.Metadata[headerIndex] = batch.Header
	block.Metadata.Metadata[signaturesIndex] = encodeSignatures(batch.Signatures)
	t.lastBatch = batch
	t.ledger.Append(block)
}

func (t *Backend) Persist(key string, data proto.Message) {
	if data == nil {
		t.persistence.DelState(key)
	} else {
		bytes, err := proto.Marshal(data)
		if err != nil {
			panic(err)
		}
		t.persistence.StoreState(key, bytes)
	}
}

func (t *Backend) Restore(key string, out proto.Message) bool {
	val, err := t.persistence.ReadState(key)
	if err != nil {
		return false
	}
	err = proto.Unmarshal(val, out)
	return (err == nil)
}

func (t *Backend) LastBatch() *s.Batch {
	return t.lastBatch
}

func (t *Backend) Sign(data []byte) []byte {
	return Sign(t.conn.Cert.PrivateKey, data)
}

func (t *Backend) CheckSig(data []byte, src uint64, sig []byte) error {
	leaf := t.conn.Cert.Leaf
	if leaf == nil {
		panic("No public key found: certificate leaf is nil.")
	}
	return CheckSig(leaf.PublicKey, data, sig)
}

func (t *Backend) Reconnect(replica uint64) {
	t.enqueueConnection(replica)
}

func Sign(privateKey crypto.PrivateKey, data []byte) []byte {
	var err error
	var encsig []byte
	hash := sha256.Sum256(data)
	switch pvk := privateKey.(type) {
	case *rsa.PrivateKey:
		encsig, err = pvk.Sign(crand.Reader, hash[:], crypto.SHA256)
		if err != nil {
			panic(err)
		}
	case *ecdsa.PrivateKey:
		r, s, err := ecdsa.Sign(crand.Reader, pvk, hash[:])
		if err != nil {
			panic(err)
		}
		encsig, err = asn1.Marshal(struct{ R, S *big.Int }{r, s})
	default:
		panic("Unsupported private key type given.")
	}
	if err != nil {
		panic(err)
	}
	return encsig
}

func CheckSig(publicKey crypto.PublicKey, data []byte, sig []byte) error {
	hash := sha256.Sum256(data)
	switch p := publicKey.(type) {
	case *ecdsa.PublicKey:
		s := struct{ R, S *big.Int }{}
		rest, err := asn1.Unmarshal(sig, &s)
		if err != nil {
			return err
		}
		if len(rest) != 0 {
			return fmt.Errorf("invalid signature (problem with asn unmarshalling for ECDSA)")
		}
		ok := ecdsa.Verify(p, hash[:], s.R, s.S)
		if !ok {
			return fmt.Errorf("invalid signature (problem with verification)")
		}
		return nil
	case *rsa.PublicKey:
		err := rsa.VerifyPKCS1v15(p, crypto.SHA256, hash[:], sig)
		return err
	default:
		return fmt.Errorf("Unsupported public key type.")
	}
}

func getHeader(metadata *cb.BlockMetadata) []byte {
	if metadata == nil || len(metadata.Metadata) < metadataLen {
		return nil
	}
	return metadata.Metadata[headerIndex]
}

func getEncodedSignatures(metadata *cb.BlockMetadata) []byte {
	if metadata == nil || len(metadata.Metadata) < metadataLen {
		return nil
	}
	return metadata.Metadata[signaturesIndex]
}

func encodeSignatures(signatures map[uint64][]byte) []byte {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(signatures)
	if err != nil {
		panic(err)
	}
	return buf.Bytes()
}

func decodeSignatures(encodedSignatures []byte) map[uint64][]byte {
	if len(encodedSignatures) == 0 {
		return nil
	}
	buf := bytes.NewBuffer(encodedSignatures)
	var r map[uint64][]byte
	dec := gob.NewDecoder(buf)
	err := dec.Decode(&r)
	if err != nil {
		panic(err)
	}
	return r
}
<|endoftext|>


<commit_before>package network

import (
	"net"

	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

// Type represents an LXD network driver type.
type Type interface {
	FillConfig(config map[string]string) error
	Info() Info
	ValidateName(name string) error
	Type() string
	DBType() db.NetworkType
}

// Network represents an instantiated LXD network.
type Network interface {
	Type

	// Load.
	init(state *state.State, id int64, projectName string, netInfo *api.Network, netNodes map[int64]db.NetworkNode)

	// Config.
	Validate(config map[string]string) error
	ID() int64
	Name() string
	Description() string
	Status() string
	LocalStatus() string
	Config() map[string]string
	IsUsed() (bool, error)
	DHCPv4Subnet() *net.IPNet
	DHCPv6Subnet() *net.IPNet
	DHCPv4Ranges() []shared.IPRange
	DHCPv6Ranges() []shared.IPRange

	// Actions.
	Create(clientType cluster.ClientType) error
	Start() error
	Stop() error
	Rename(name string) error
	Update(newNetwork api.NetworkPut, targetNode string, clientType cluster.ClientType) error
	HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error
	Delete(clientType cluster.ClientType) error
}
<commit_msg>lxd/network/network/interface: Adds IsManaged function<commit_after>package network

import (
	"net"

	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

// Type represents an LXD network driver type.
type Type interface {
	FillConfig(config map[string]string) error
	Info() Info
	ValidateName(name string) error
	Type() string
	DBType() db.NetworkType
}

// Network represents an instantiated LXD network.
type Network interface {
	Type

	// Load.
	init(state *state.State, id int64, projectName string, netInfo *api.Network, netNodes map[int64]db.NetworkNode)

	// Config.
	Validate(config map[string]string) error
	ID() int64
	Name() string
	Description() string
	Status() string
	LocalStatus() string
	Config() map[string]string
	IsUsed() (bool, error)
	IsManaged() bool
	DHCPv4Subnet() *net.IPNet
	DHCPv6Subnet() *net.IPNet
	DHCPv4Ranges() []shared.IPRange
	DHCPv6Ranges() []shared.IPRange

	// Actions.
	Create(clientType cluster.ClientType) error
	Start() error
	Stop() error
	Rename(name string) error
	Update(newNetwork api.NetworkPut, targetNode string, clientType cluster.ClientType) error
	HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error
	Delete(clientType cluster.ClientType) error
}
<|endoftext|>


<commit_before>package merger

import (
	"encoding/json"
	"fmt"
	"testing"
)

const (
	intoJSONStructConst = "{\"UserID\":\"\",\"UserName\":\"959dfbb6-f143-468c-85e0-997b753f79a5@iprg.co.za\",\"Password\":\"\""
	fromJSONStructConst = "{\"UserID\":\":19b90b42-786d-44bf-be74-6f6d54edfe02\",\"UserName\":\"959dfbb6-f143-468c-85e0-997b753f79a5@iprg.co.za\",\"Password\":\"\""
)

var intoJSONStruct, fromJSONStruct string

/*
TestMergeMasterJSON ...
*/
func TestMergeMasterJSON(t *testing.T) {
	var new string
	var err error
	intoJSONStruct = intoJSONStructConst
	fromJSONStruct = fromJSONStructConst
	if new, err = MergeMasterJSON(intoJSONStruct, fromJSONStruct); err != nil {
		fmt.Println(err)
		t.FailNow()
	}

	var x map[string]interface{}
	if err = json.Unmarshal([]byte(new), &x); err != nil {
		fmt.Println(err)
		t.FailNow()
	}

	if x["UserID"] == nil && x["UserID"] == "" {
		fmt.Println("UserID NILL !1!")
		t.FailNow()
	}

}
<commit_msg>Test Fix<commit_after>package merger

import (
	"encoding/json"
	"fmt"
	"testing"
)

const (
	intoJSONStructConst = "{\"UserID\":\"\",\"UserName\":\"test@example.com\",\"Password\":\"PASS\"}"
	fromJSONStructConst = "{\"UserID\":\"19b90b42-786d-44bf-be74-6f6d54edfe02\",\"UserName\":\"test@example.com\",\"Password\":\"\"}"
)

var intoJSONStruct, fromJSONStruct string

/*
TestMergeMasterJSON ...
*/
func TestMergeMasterJSON(t *testing.T) {
	var new string
	var err error
	intoJSONStruct = intoJSONStructConst
	fromJSONStruct = fromJSONStructConst
	if new, err = MergeMasterJSON(intoJSONStruct, fromJSONStruct); err != nil {
		fmt.Println(err)
		t.FailNow()
	}

	var x map[string]interface{}
	if err = json.Unmarshal([]byte(new), &x); err != nil {
		fmt.Println(err)
		t.FailNow()
	}

	if x["UserID"] == nil && x["UserID"] == "" {
		fmt.Println("UserID == NILL !1!")
		t.FailNow()
	}

	if x["UserName"] != "test@example.com" {
		fmt.Println("UserName Was changed")
		t.FailNow()
	}

	if x["Password"] != "PASS" {
		fmt.Println("Password Was Changed")
		t.FailNow()
	}

	fmt.Println(x["UserID"])

}

func BenchmarkMergeMasterJSON(b *testing.B) {
	for i := 0; i < b.N; i++ {
		var new string
		var err error
		intoJSONStruct = intoJSONStructConst
		fromJSONStruct = fromJSONStructConst
		if new, err = MergeMasterJSON(intoJSONStruct, fromJSONStruct); err != nil {
			fmt.Println(err)
			b.FailNow()
		}

		var x map[string]interface{}
		if err = json.Unmarshal([]byte(new), &x); err != nil {
			fmt.Println(err)
			b.FailNow()
		}

		if x["UserID"] == nil && x["UserID"] == "" {
			fmt.Println("UserID NILL !1!")
			b.FailNow()
		}
	}
}
<|endoftext|>


<commit_before>package test

import (
	"reflect"
	"testing"
	"unsafe"

	"github.com/coredns/coredns/core/dnsserver"

	"github.com/miekg/dns"
)

// Start 2 tests server, server A will proxy to B, server B is an CH server.
func TestProxyToChaosServer(t *testing.T) {
	t.Parallel()
	corefile := `.:0 {
		chaos CoreDNS-001 miek@miek.nl
	}`

	chaos, udpChaos, _, err := CoreDNSServerAndPorts(corefile)
	if err != nil {
		t.Fatalf("Could not get CoreDNS serving instance: %s", err)
	}

	defer chaos.Stop()

	corefileProxy := `.:0 {
		forward . ` + udpChaos + `
	}`

	proxy, udp, _, err := CoreDNSServerAndPorts(corefileProxy)
	if err != nil {
		t.Fatalf("Could not get CoreDNS serving instance")
	}
	defer proxy.Stop()

	chaosTest(t, udpChaos)

	chaosTest(t, udp)
	// chaosTest(t, tcp, "tcp"), commented out because we use the original transport to reach the
	// proxy and we only forward to the udp port.
}

func chaosTest(t *testing.T, server string) {
	m := new(dns.Msg)
	m.Question = make([]dns.Question, 1)
	m.Question[0] = dns.Question{Qclass: dns.ClassCHAOS, Name: "version.bind.", Qtype: dns.TypeTXT}

	r, err := dns.Exchange(m, server)
	if err != nil {
		t.Fatalf("Could not send message: %s", err)
	}
	if r.Rcode != dns.RcodeSuccess || len(r.Answer) == 0 {
		t.Fatalf("Expected successful reply, got %s", dns.RcodeToString[r.Rcode])
	}
	if r.Answer[0].String() != `version.bind.	0	CH	TXT	"CoreDNS-001"` {
		t.Fatalf("Expected version.bind. reply, got %s", r.Answer[0].String())
	}
}

func TestReverseExpansion(t *testing.T) {
	// this test needs a fixed port, because with :0 the expanded reverse zone will listen on different
	// addresses and we can't check which ones...
	corefile := `10.0.0.0/15:5053 {
		whoami
	}`

	server, udp, _, err := CoreDNSServerAndPorts(corefile)
	if err != nil {
		t.Fatalf("Could not get CoreDNS serving instance: %s", err)
	}

	defer server.Stop()

	m := new(dns.Msg)
	m.SetQuestion("whoami.0.10.in-addr.arpa.", dns.TypeA)

	r, err := dns.Exchange(m, udp)
	if err != nil {
		t.Fatalf("Could not send message: %s", err)
	}
	if r.Rcode != dns.RcodeSuccess {
		t.Errorf("Expected NOERROR, got %d", r.Rcode)
	}
	if len(r.Extra) != 2 {
		t.Errorf("Expected 2 RRs in additional section, got %d", len(r.Extra))
	}

	m.SetQuestion("whoami.1.10.in-addr.arpa.", dns.TypeA)
	r, err = dns.Exchange(m, udp)
	if err != nil {
		t.Fatalf("Could not send message: %s", err)
	}
	if r.Rcode != dns.RcodeSuccess {
		t.Errorf("Expected NOERROR, got %d", r.Rcode)
	}
	if len(r.Extra) != 2 {
		t.Errorf("Expected 2 RRs in additional section, got %d", len(r.Extra))
	}

	// should be refused
	m.SetQuestion("whoami.2.10.in-addr.arpa.", dns.TypeA)
	r, err = dns.Exchange(m, udp)
	if err != nil {
		t.Fatalf("Could not send message: %s", err)
	}
	if r.Rcode != dns.RcodeRefused {
		t.Errorf("Expected REFUSED, got %d", r.Rcode)
	}
	if len(r.Extra) != 0 {
		t.Errorf("Expected 0 RRs in additional section, got %d", len(r.Extra))
	}
}

func TestMultiZoneBlockConfigs(t *testing.T) {
	corefile := `.:40000 .:40001 .:40002 {
		debug
	}`

	server, err := CoreDNSServer(corefile)
	defer server.Stop()

	if err != nil {
		t.Fatalf("Could not get CoreDNS serving instance: %s", err)
	}

	// unsafe reflection to read unexported fields "context" and "configs" within context
	ctxVal := reflect.ValueOf(server).Elem().FieldByName("context")
	ctxVal2 := reflect.NewAt(ctxVal.Type(), unsafe.Pointer(ctxVal.UnsafeAddr())).Elem()
	configs := reflect.ValueOf(ctxVal2.Interface()).Elem().FieldByName("configs")
	configs2 := reflect.NewAt(configs.Type(), unsafe.Pointer(configs.UnsafeAddr())).Elem()

	for i := 0; i < 3; i++ {
		v := configs2.Index(i)
		config := v.Interface().(*dnsserver.Config)
		if !config.Debug {
			t.Fatalf("Debug was not set for %s://%s:%s", config.Transport, config.Zone, config.Port)
		}
	}
}
<commit_msg>deflake TestMultiZoneBlockConfigs (#4751)<commit_after>package test

import (
	"fmt"
	"math/rand"
	"reflect"
	"testing"
	"unsafe"

	"github.com/coredns/caddy"
	"github.com/coredns/coredns/core/dnsserver"

	"github.com/miekg/dns"
)

// Start 2 tests server, server A will proxy to B, server B is an CH server.
func TestProxyToChaosServer(t *testing.T) {
	t.Parallel()
	corefile := `.:0 {
		chaos CoreDNS-001 miek@miek.nl
	}`

	chaos, udpChaos, _, err := CoreDNSServerAndPorts(corefile)
	if err != nil {
		t.Fatalf("Could not get CoreDNS serving instance: %s", err)
	}

	defer chaos.Stop()

	corefileProxy := `.:0 {
		forward . ` + udpChaos + `
	}`

	proxy, udp, _, err := CoreDNSServerAndPorts(corefileProxy)
	if err != nil {
		t.Fatalf("Could not get CoreDNS serving instance")
	}
	defer proxy.Stop()

	chaosTest(t, udpChaos)

	chaosTest(t, udp)
	// chaosTest(t, tcp, "tcp"), commented out because we use the original transport to reach the
	// proxy and we only forward to the udp port.
}

func chaosTest(t *testing.T, server string) {
	m := new(dns.Msg)
	m.Question = make([]dns.Question, 1)
	m.Question[0] = dns.Question{Qclass: dns.ClassCHAOS, Name: "version.bind.", Qtype: dns.TypeTXT}

	r, err := dns.Exchange(m, server)
	if err != nil {
		t.Fatalf("Could not send message: %s", err)
	}
	if r.Rcode != dns.RcodeSuccess || len(r.Answer) == 0 {
		t.Fatalf("Expected successful reply, got %s", dns.RcodeToString[r.Rcode])
	}
	if r.Answer[0].String() != `version.bind.	0	CH	TXT	"CoreDNS-001"` {
		t.Fatalf("Expected version.bind. reply, got %s", r.Answer[0].String())
	}
}

func TestReverseExpansion(t *testing.T) {
	// this test needs a fixed port, because with :0 the expanded reverse zone will listen on different
	// addresses and we can't check which ones...
	corefile := `10.0.0.0/15:5053 {
		whoami
	}`

	server, udp, _, err := CoreDNSServerAndPorts(corefile)
	if err != nil {
		t.Fatalf("Could not get CoreDNS serving instance: %s", err)
	}

	defer server.Stop()

	m := new(dns.Msg)
	m.SetQuestion("whoami.0.10.in-addr.arpa.", dns.TypeA)

	r, err := dns.Exchange(m, udp)
	if err != nil {
		t.Fatalf("Could not send message: %s", err)
	}
	if r.Rcode != dns.RcodeSuccess {
		t.Errorf("Expected NOERROR, got %d", r.Rcode)
	}
	if len(r.Extra) != 2 {
		t.Errorf("Expected 2 RRs in additional section, got %d", len(r.Extra))
	}

	m.SetQuestion("whoami.1.10.in-addr.arpa.", dns.TypeA)
	r, err = dns.Exchange(m, udp)
	if err != nil {
		t.Fatalf("Could not send message: %s", err)
	}
	if r.Rcode != dns.RcodeSuccess {
		t.Errorf("Expected NOERROR, got %d", r.Rcode)
	}
	if len(r.Extra) != 2 {
		t.Errorf("Expected 2 RRs in additional section, got %d", len(r.Extra))
	}

	// should be refused
	m.SetQuestion("whoami.2.10.in-addr.arpa.", dns.TypeA)
	r, err = dns.Exchange(m, udp)
	if err != nil {
		t.Fatalf("Could not send message: %s", err)
	}
	if r.Rcode != dns.RcodeRefused {
		t.Errorf("Expected REFUSED, got %d", r.Rcode)
	}
	if len(r.Extra) != 0 {
		t.Errorf("Expected 0 RRs in additional section, got %d", len(r.Extra))
	}
}

func TestMultiZoneBlockConfigs(t *testing.T) {
	// We need fixed port numbers here to have multiple serving instances, using ".:0" wont work because that
	// leads to a 'duplicate server instances' because '0' is used literary (only the kernel knows what port will
	// be assigned).
	//
	// This makes the test flaky because we don't know if there are in-use or not. We add a random number to each base and
	// retry when we fail to get a serving instance (up to 3 times).

	var (
		server *caddy.Instance
		err    error
	)
	for j := 0; j < 3; j++ {
		corefile := `.:%d .:%d .:%d {
		debug
	}`
		corefile = fmt.Sprintf(corefile, 40000+rand.Intn(9000), 50000+rand.Intn(9000), 60000+rand.Intn(9000))

		if server, err = CoreDNSServer(corefile); err != nil {
			continue
		}
		t.Logf("Got running CoreDNS serving instance, after %d tries", j+1)
		break // success
	}
	if server == nil {
		t.Fatalf("Could not get CoreDNS serving instance: %s", err)
	}
	defer server.Stop()

	// unsafe reflection to read unexported fields "context" and "configs" within context
	ctxVal := reflect.ValueOf(server).Elem().FieldByName("context")
	ctxVal2 := reflect.NewAt(ctxVal.Type(), unsafe.Pointer(ctxVal.UnsafeAddr())).Elem()
	configs := reflect.ValueOf(ctxVal2.Interface()).Elem().FieldByName("configs")
	configs2 := reflect.NewAt(configs.Type(), unsafe.Pointer(configs.UnsafeAddr())).Elem()

	for i := 0; i < 3; i++ {
		v := configs2.Index(i)
		config := v.Interface().(*dnsserver.Config)
		if !config.Debug {
			t.Fatalf("Debug was not set for %s://%s:%s", config.Transport, config.Zone, config.Port)
		}
	}
}
<|endoftext|>


<commit_before>package lion

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

	"github.com/fatih/color"

	"golang.org/x/net/context"
)

var red = color.New(color.FgRed).SprintFunc()
var yellow = color.New(color.FgYellow).SprintFunc()
var magenta = color.New(color.FgHiMagenta).SprintFunc()
var blue = color.New(color.FgBlue).SprintFunc()
var green = color.New(color.FgGreen).SprintFunc()
var cyan = color.New(color.FgCyan).SprintFunc()
var hiBlue = color.New(color.FgHiBlue).SprintFunc()
var lionColor = color.New(color.Italic, color.FgHiGreen).SprintFunc()

// Classic creates a new router with some handy middlewares.
func Classic() *Router {
	return New(NewRecovery(), RealIP(), NewLogger(), NewStatic(http.Dir("public")))
}

var lionLogger = log.New(os.Stdout, lionColor("[lion]")+" ", log.Ldate|log.Ltime)

// Logger is a middlewares that logs incomming http requests
type Logger struct {
	*log.Logger
}

// NewLogger creates a new Logger
func NewLogger() *Logger {
	return &Logger{
		Logger: lionLogger,
	}
}

// ServeNext implements the Middleware interface for Logger.
// It wraps the corresponding http.ResponseWriter and saves statistics about the status code returned, the number of bytes written and the time that requests took.
func (l *Logger) ServeNext(next Handler) Handler {

	return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {

		res := WrapResponseWriter(w)
		start := time.Now()

		next.ServeHTTPC(c, res, r)

		l.Printf("%s %s | %s | %dB in %v from %s", magenta(r.Method), hiBlue(r.URL.Path), statusColor(res.Status()), res.BytesWritten(), timeColor(time.Since(start)), r.RemoteAddr)
	})
}

func statusColor(status int) string {
	msg := fmt.Sprintf("%d %s", status, http.StatusText(status))
	switch {
	case status < 200:
		return blue(msg)
	case status < 300:
		return green(msg)
	case status < 400:
		return cyan(msg)
	case status < 500:
		return yellow(msg)
	default:
		return red(msg)
	}
}

func timeColor(dur time.Duration) string {
	switch {
	case dur < 500*time.Millisecond:
		return green(dur)
	case dur < 5*time.Second:
		return yellow(dur)
	default:
		return red(dur)
	}
}

// Recovery is a middleware that recovers from panics
// Taken from https://github.com/codegangsta/negroni/blob/master/recovery.go
type Recovery struct {
	Logger     *log.Logger
	PrintStack bool
	StackAll   bool
	StackSize  int
}

// NewRecovery creates a new Recovery instance
func NewRecovery() *Recovery {
	return &Recovery{
		Logger:     lionLogger,
		PrintStack: false,
		StackAll:   false,
		StackSize:  1024 * 8,
	}
}

// ServeNext is the method responsible for recovering from a panic
func (rec *Recovery) ServeNext(next Handler) Handler {
	return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				stack := make([]byte, rec.StackSize)
				stack = stack[:runtime.Stack(stack, rec.StackAll)]

				f := "PANIC: %s\n%s"
				rec.Logger.Printf(f, err, stack)

				if rec.PrintStack {
					fmt.Fprintf(w, f, err, stack)
				}

			}
		}()

		next.ServeHTTPC(c, w, r)
	})
}

func defaultPanicHandler() Handler {
	return HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		log.Println(fmt.Sprintf("%v", ctx.Value("panic")))
		http.Error(w, fmt.Sprintf("%v", ctx.Value("panic")), http.StatusInternalServerError)
	})
}

// Static is a middleware handler that serves static files in the given directory/filesystem.
// Taken from https://github.com/codegangsta/negroni/blob/master/static.go
type Static struct {
	// Dir is the directory to serve static files from
	Dir http.FileSystem
	// Prefix is the optional prefix used to serve the static directory content
	Prefix string
	// IndexFile defines which file to serve as index if it exists.
	IndexFile string
}

// NewStatic returns a new instance of Static
func NewStatic(directory http.FileSystem) *Static {
	return &Static{
		Dir:       directory,
		Prefix:    "",
		IndexFile: "index.html",
	}
}

// ServeNext tries to find a file in the directory
func (s *Static) ServeNext(next Handler) Handler {
	return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {

		if r.Method != "GET" && r.Method != "HEAD" {
			next.ServeHTTPC(c, w, r)
			return
		}

		file := r.URL.Path
		// if we have a prefix, filter requests by stripping the prefix
		if s.Prefix != "" {
			if !strings.HasPrefix(file, s.Prefix) {
				next.ServeHTTPC(c, w, r)
				return
			}
			file = file[len(s.Prefix):]
			if file != "" && file[0] != '/' {
				next.ServeHTTPC(c, w, r)
				return
			}
		}
		f, err := s.Dir.Open(file)
		if err != nil {
			// discard the error?
			next.ServeHTTPC(c, w, r)
			return
		}
		defer f.Close()

		fi, err := f.Stat()
		if err != nil {
			next.ServeHTTPC(c, w, r)
			return
		}

		// try to serve index file
		if fi.IsDir() {
			// redirect if missing trailing slash
			if !strings.HasSuffix(r.URL.Path, "/") {
				http.Redirect(w, r, r.URL.Path+"/", http.StatusFound)
				return
			}

			file = path.Join(file, s.IndexFile)
			f, err = s.Dir.Open(file)
			if err != nil {
				next.ServeHTTPC(c, w, r)
				return
			}
			defer f.Close()

			fi, err = f.Stat()
			if err != nil || fi.IsDir() {
				next.ServeHTTPC(c, w, r)
				return
			}
		}

		http.ServeContent(w, r, file, fi.ModTime(), f)
	})
}

// MaxAge is a middleware that defines the max duration headers
func MaxAge(dur time.Duration) Middleware {
	return MaxAgeWithFilter(dur, func(c context.Context, w http.ResponseWriter, r *http.Request) bool { return true })
}

// MaxAgeWithFilter is a middleware that defines the max duration headers with a filter function.
// If the filter returns true then the headers will be set. Otherwise, if it returns false the headers will not be set.
func MaxAgeWithFilter(dur time.Duration, filter func(c context.Context, w http.ResponseWriter, r *http.Request) bool) Middleware {
	if filter == nil {
		filter = func(c context.Context, w http.ResponseWriter, r *http.Request) bool { return false }
	}
	return MiddlewareFunc(func(next Handler) Handler {
		return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
			if !filter(c, w, r) {
				return
			}
			w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d, public, must-revalidate, proxy-revalidate", int(dur.Seconds())))
			next.ServeHTTPC(c, w, r)
		})
	})
}

// NoCache middleware sets headers to disable browser caching.
// Inspired by https://github.com/mytrile/nocache
func NoCache() Middleware {
	var epoch = time.Unix(0, 0).Format(time.RFC1123)

	return noCache{
		responseHeaders: map[string]string{
			"Expires":         epoch,
			"Cache-Control":   "no-cache, private, must-revalidate, max-age=0",
			"Pragma":          "no-cache",
			"X-Accel-Expires": "0",
		},
		etagHeaders: []string{
			"ETag",
			"If-Modified-Since",
			"If-Match",
			"If-Note-Match",
			"If-Range",
			"If-Unmodified-Since",
		},
	}
}

type noCache struct {
	responseHeaders map[string]string
	etagHeaders     []string
}

func (n noCache) ServeNext(next Handler) Handler {
	return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
		// Delete ETag headers
		for _, v := range n.etagHeaders {
			if r.Header.Get(v) != "" {
				r.Header.Del(v)
			}
		}

		// Add nocache headers
		for k, v := range n.responseHeaders {
			w.Header().Set(k, v)
		}

	})
}

var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
var xRealIP = http.CanonicalHeaderKey("X-Real-IP")

// RealIP is a middleware that sets a http.Request's RemoteAddr to the results
// of parsing either the X-Forwarded-For header or the X-Real-IP header (in that
// order).
//
// This middleware should be inserted fairly early in the middleware stack to
// ensure that subsequent layers (e.g., request loggers) which examine the
// RemoteAddr will see the intended value.
//
// You should only use this middleware if you can trust the headers passed to
// you (in particular, the two headers this middleware uses), for example
// because you have placed a reverse proxy like HAProxy or nginx in front of
// Goji. If your reverse proxies are configured to pass along arbitrary header
// values from the client, or if you use this middleware without a reverse
// proxy, malicious clients will be able to make you very sad (or, depending on
// how you're using RemoteAddr, vulnerable to an attack of some sort).
// Taken from https://github.com/zenazn/goji/blob/master/web/middleware/realip.go
func RealIP() Middleware {
	return MiddlewareFunc(func(next Handler) Handler {
		return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
			if rip := realIP(r); rip != "" {
				r.RemoteAddr = rip
			}
			next.ServeHTTPC(c, w, r)
		})
	})
}

func realIP(r *http.Request) string {
	var ip string

	if xff := r.Header.Get(xForwardedFor); xff != "" {
		i := strings.Index(xff, ", ")
		if i == -1 {
			i = len(xff)
		}
		ip = xff[:i]
	} else if xrip := r.Header.Get(xRealIP); xrip != "" {
		ip = xrip
	}

	return ip
}
<commit_msg>Improve documentation for Classic()<commit_after>package lion

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

	"github.com/fatih/color"

	"golang.org/x/net/context"
)

var red = color.New(color.FgRed).SprintFunc()
var yellow = color.New(color.FgYellow).SprintFunc()
var magenta = color.New(color.FgHiMagenta).SprintFunc()
var blue = color.New(color.FgBlue).SprintFunc()
var green = color.New(color.FgGreen).SprintFunc()
var cyan = color.New(color.FgCyan).SprintFunc()
var hiBlue = color.New(color.FgHiBlue).SprintFunc()
var lionColor = color.New(color.Italic, color.FgHiGreen).SprintFunc()

// Classic creates a new router instance with default middlewares: Recovery, RealIP, Logger and Static.
// The static middleware instance is initiated with a directory named "public" located relatively to the current working directory.
func Classic() *Router {
	return New(NewRecovery(), RealIP(), NewLogger(), NewStatic(http.Dir("public")))
}

var lionLogger = log.New(os.Stdout, lionColor("[lion]")+" ", log.Ldate|log.Ltime)

// Logger is a middlewares that logs incomming http requests
type Logger struct {
	*log.Logger
}

// NewLogger creates a new Logger
func NewLogger() *Logger {
	return &Logger{
		Logger: lionLogger,
	}
}

// ServeNext implements the Middleware interface for Logger.
// It wraps the corresponding http.ResponseWriter and saves statistics about the status code returned, the number of bytes written and the time that requests took.
func (l *Logger) ServeNext(next Handler) Handler {

	return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {

		res := WrapResponseWriter(w)
		start := time.Now()

		next.ServeHTTPC(c, res, r)

		l.Printf("%s %s | %s | %dB in %v from %s", magenta(r.Method), hiBlue(r.URL.Path), statusColor(res.Status()), res.BytesWritten(), timeColor(time.Since(start)), r.RemoteAddr)
	})
}

func statusColor(status int) string {
	msg := fmt.Sprintf("%d %s", status, http.StatusText(status))
	switch {
	case status < 200:
		return blue(msg)
	case status < 300:
		return green(msg)
	case status < 400:
		return cyan(msg)
	case status < 500:
		return yellow(msg)
	default:
		return red(msg)
	}
}

func timeColor(dur time.Duration) string {
	switch {
	case dur < 500*time.Millisecond:
		return green(dur)
	case dur < 5*time.Second:
		return yellow(dur)
	default:
		return red(dur)
	}
}

// Recovery is a middleware that recovers from panics
// Taken from https://github.com/codegangsta/negroni/blob/master/recovery.go
type Recovery struct {
	Logger     *log.Logger
	PrintStack bool
	StackAll   bool
	StackSize  int
}

// NewRecovery creates a new Recovery instance
func NewRecovery() *Recovery {
	return &Recovery{
		Logger:     lionLogger,
		PrintStack: false,
		StackAll:   false,
		StackSize:  1024 * 8,
	}
}

// ServeNext is the method responsible for recovering from a panic
func (rec *Recovery) ServeNext(next Handler) Handler {
	return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				stack := make([]byte, rec.StackSize)
				stack = stack[:runtime.Stack(stack, rec.StackAll)]

				f := "PANIC: %s\n%s"
				rec.Logger.Printf(f, err, stack)

				if rec.PrintStack {
					fmt.Fprintf(w, f, err, stack)
				}

			}
		}()

		next.ServeHTTPC(c, w, r)
	})
}

func defaultPanicHandler() Handler {
	return HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		log.Println(fmt.Sprintf("%v", ctx.Value("panic")))
		http.Error(w, fmt.Sprintf("%v", ctx.Value("panic")), http.StatusInternalServerError)
	})
}

// Static is a middleware handler that serves static files in the given directory/filesystem.
// Taken from https://github.com/codegangsta/negroni/blob/master/static.go
type Static struct {
	// Dir is the directory to serve static files from
	Dir http.FileSystem
	// Prefix is the optional prefix used to serve the static directory content
	Prefix string
	// IndexFile defines which file to serve as index if it exists.
	IndexFile string
}

// NewStatic returns a new instance of Static
func NewStatic(directory http.FileSystem) *Static {
	return &Static{
		Dir:       directory,
		Prefix:    "",
		IndexFile: "index.html",
	}
}

// ServeNext tries to find a file in the directory
func (s *Static) ServeNext(next Handler) Handler {
	return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {

		if r.Method != "GET" && r.Method != "HEAD" {
			next.ServeHTTPC(c, w, r)
			return
		}

		file := r.URL.Path
		// if we have a prefix, filter requests by stripping the prefix
		if s.Prefix != "" {
			if !strings.HasPrefix(file, s.Prefix) {
				next.ServeHTTPC(c, w, r)
				return
			}
			file = file[len(s.Prefix):]
			if file != "" && file[0] != '/' {
				next.ServeHTTPC(c, w, r)
				return
			}
		}
		f, err := s.Dir.Open(file)
		if err != nil {
			// discard the error?
			next.ServeHTTPC(c, w, r)
			return
		}
		defer f.Close()

		fi, err := f.Stat()
		if err != nil {
			next.ServeHTTPC(c, w, r)
			return
		}

		// try to serve index file
		if fi.IsDir() {
			// redirect if missing trailing slash
			if !strings.HasSuffix(r.URL.Path, "/") {
				http.Redirect(w, r, r.URL.Path+"/", http.StatusFound)
				return
			}

			file = path.Join(file, s.IndexFile)
			f, err = s.Dir.Open(file)
			if err != nil {
				next.ServeHTTPC(c, w, r)
				return
			}
			defer f.Close()

			fi, err = f.Stat()
			if err != nil || fi.IsDir() {
				next.ServeHTTPC(c, w, r)
				return
			}
		}

		http.ServeContent(w, r, file, fi.ModTime(), f)
	})
}

// MaxAge is a middleware that defines the max duration headers
func MaxAge(dur time.Duration) Middleware {
	return MaxAgeWithFilter(dur, func(c context.Context, w http.ResponseWriter, r *http.Request) bool { return true })
}

// MaxAgeWithFilter is a middleware that defines the max duration headers with a filter function.
// If the filter returns true then the headers will be set. Otherwise, if it returns false the headers will not be set.
func MaxAgeWithFilter(dur time.Duration, filter func(c context.Context, w http.ResponseWriter, r *http.Request) bool) Middleware {
	if filter == nil {
		filter = func(c context.Context, w http.ResponseWriter, r *http.Request) bool { return false }
	}
	return MiddlewareFunc(func(next Handler) Handler {
		return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
			if !filter(c, w, r) {
				return
			}
			w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d, public, must-revalidate, proxy-revalidate", int(dur.Seconds())))
			next.ServeHTTPC(c, w, r)
		})
	})
}

// NoCache middleware sets headers to disable browser caching.
// Inspired by https://github.com/mytrile/nocache
func NoCache() Middleware {
	var epoch = time.Unix(0, 0).Format(time.RFC1123)

	return noCache{
		responseHeaders: map[string]string{
			"Expires":         epoch,
			"Cache-Control":   "no-cache, private, must-revalidate, max-age=0",
			"Pragma":          "no-cache",
			"X-Accel-Expires": "0",
		},
		etagHeaders: []string{
			"ETag",
			"If-Modified-Since",
			"If-Match",
			"If-Note-Match",
			"If-Range",
			"If-Unmodified-Since",
		},
	}
}

type noCache struct {
	responseHeaders map[string]string
	etagHeaders     []string
}

func (n noCache) ServeNext(next Handler) Handler {
	return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
		// Delete ETag headers
		for _, v := range n.etagHeaders {
			if r.Header.Get(v) != "" {
				r.Header.Del(v)
			}
		}

		// Add nocache headers
		for k, v := range n.responseHeaders {
			w.Header().Set(k, v)
		}

	})
}

var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
var xRealIP = http.CanonicalHeaderKey("X-Real-IP")

// RealIP is a middleware that sets a http.Request's RemoteAddr to the results
// of parsing either the X-Forwarded-For header or the X-Real-IP header (in that
// order).
//
// This middleware should be inserted fairly early in the middleware stack to
// ensure that subsequent layers (e.g., request loggers) which examine the
// RemoteAddr will see the intended value.
//
// You should only use this middleware if you can trust the headers passed to
// you (in particular, the two headers this middleware uses), for example
// because you have placed a reverse proxy like HAProxy or nginx in front of
// Goji. If your reverse proxies are configured to pass along arbitrary header
// values from the client, or if you use this middleware without a reverse
// proxy, malicious clients will be able to make you very sad (or, depending on
// how you're using RemoteAddr, vulnerable to an attack of some sort).
// Taken from https://github.com/zenazn/goji/blob/master/web/middleware/realip.go
func RealIP() Middleware {
	return MiddlewareFunc(func(next Handler) Handler {
		return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
			if rip := realIP(r); rip != "" {
				r.RemoteAddr = rip
			}
			next.ServeHTTPC(c, w, r)
		})
	})
}

func realIP(r *http.Request) string {
	var ip string

	if xff := r.Header.Get(xForwardedFor); xff != "" {
		i := strings.Index(xff, ", ")
		if i == -1 {
			i = len(xff)
		}
		ip = xff[:i]
	} else if xrip := r.Header.Get(xRealIP); xrip != "" {
		ip = xrip
	}

	return ip
}
<|endoftext|>


<commit_before>// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package miner

import (
	"sync"

	"sync/atomic"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/pow"
)

type CpuAgent struct {
	mu sync.Mutex

	workCh        chan *Work
	quit          chan struct{}
	quitCurrentOp chan struct{}
	returnCh      chan<- *Result

	index int
	pow   pow.PoW

	isMining int32 // isMining indicates whether the agent is currently mining
}

func NewCpuAgent(index int, pow pow.PoW) *CpuAgent {
	miner := &CpuAgent{
		pow:   pow,
		index: index,
	}

	return miner
}

func (self *CpuAgent) Work() chan<- *Work            { return self.workCh }
func (self *CpuAgent) Pow() pow.PoW                  { return self.pow }
func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch }

func (self *CpuAgent) Stop() {
	self.mu.Lock()
	defer self.mu.Unlock()

	close(self.quit)
}

func (self *CpuAgent) Start() {
	self.mu.Lock()
	defer self.mu.Unlock()

	if !atomic.CompareAndSwapInt32(&self.isMining, 0, 1) {
		return // agent already started
	}

	self.quit = make(chan struct{})
	// creating current op ch makes sure we're not closing a nil ch
	// later on
	self.workCh = make(chan *Work, 1)

	go self.update()
}

func (self *CpuAgent) update() {
out:
	for {
		select {
		case work := <-self.workCh:
			self.mu.Lock()
			if self.quitCurrentOp != nil {
				close(self.quitCurrentOp)
			}
			self.quitCurrentOp = make(chan struct{})
			go self.mine(work, self.quitCurrentOp)
			self.mu.Unlock()
		case <-self.quit:
			self.mu.Lock()
			if self.quitCurrentOp != nil {
				close(self.quitCurrentOp)
				self.quitCurrentOp = nil
			}
			self.mu.Unlock()
			break out
		}
	}

done:
	// Empty work channel
	for {
		select {
		case <-self.workCh:
		default:
			close(self.workCh)
			break done
		}
	}

	atomic.StoreInt32(&self.isMining, 0)
}

func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) {
	glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index)

	// Mine
	nonce, mixDigest := self.pow.Search(work.Block, stop, self.index)
	if nonce != 0 {
		block := work.Block.WithMiningResult(nonce, common.BytesToHash(mixDigest))
		self.returnCh <- &Result{work, block}
	} else {
		self.returnCh <- nil
	}
}

func (self *CpuAgent) GetHashRate() int64 {
	return self.pow.GetHashrate()
}
<commit_msg>miner: Move CpuAgent channel creation from Start() to initialization (fixes #2948)<commit_after>// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package miner

import (
	"sync"

	"sync/atomic"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
	"github.com/ethereum/go-ethereum/pow"
)

type CpuAgent struct {
	mu sync.Mutex

	workCh        chan *Work
	quit          chan struct{}
	quitCurrentOp chan struct{}
	returnCh      chan<- *Result

	index int
	pow   pow.PoW

	isMining int32 // isMining indicates whether the agent is currently mining
}

func NewCpuAgent(index int, pow pow.PoW) *CpuAgent {
	miner := &CpuAgent{
		pow:    pow,
		index:  index,
		quit:   make(chan struct{}),
		workCh: make(chan *Work, 1),
	}

	return miner
}

func (self *CpuAgent) Work() chan<- *Work            { return self.workCh }
func (self *CpuAgent) Pow() pow.PoW                  { return self.pow }
func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch }

func (self *CpuAgent) Stop() {
	close(self.quit)
}

func (self *CpuAgent) Start() {

	if !atomic.CompareAndSwapInt32(&self.isMining, 0, 1) {
		return // agent already started
	}

	go self.update()
}

func (self *CpuAgent) update() {
out:
	for {
		select {
		case work := <-self.workCh:
			self.mu.Lock()
			if self.quitCurrentOp != nil {
				close(self.quitCurrentOp)
			}
			self.quitCurrentOp = make(chan struct{})
			go self.mine(work, self.quitCurrentOp)
			self.mu.Unlock()
		case <-self.quit:
			self.mu.Lock()
			if self.quitCurrentOp != nil {
				close(self.quitCurrentOp)
				self.quitCurrentOp = nil
			}
			self.mu.Unlock()
			break out
		}
	}

done:
	// Empty work channel
	for {
		select {
		case <-self.workCh:
		default:
			close(self.workCh)
			break done
		}
	}

	atomic.StoreInt32(&self.isMining, 0)
}

func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) {
	glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index)

	// Mine
	nonce, mixDigest := self.pow.Search(work.Block, stop, self.index)
	if nonce != 0 {
		block := work.Block.WithMiningResult(nonce, common.BytesToHash(mixDigest))
		self.returnCh <- &Result{work, block}
	} else {
		self.returnCh <- nil
	}
}

func (self *CpuAgent) GetHashRate() int64 {
	return self.pow.GetHashrate()
}
<|endoftext|>