<commit_before>package lexer

import (
  // "fmt"
  "testing"
  "github.com/stretchr/testify/assert"
)

func noMoreTokens(t *testing.T, l *Lexer) {
  for tok := range l.Items {
    t.Errorf("Unexpected token received '%s'", tok)
  }
}

func TestAssignBoolean(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestAssignBoolean", `hello = true`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(BOOLEAN, token.ItemType)
  assert.Equal("true", token.ItemValue)

  noMoreTokens(t, l)
}

func TestAssignInteger(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestAssignInteger", `hello = 10`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("10", token.ItemValue)

  noMoreTokens(t, l)
}

func TestAssignFloat(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestAssignFloat", `hello = 1.0`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("1.0", token.ItemValue)

  noMoreTokens(t, l)
}

func TestAssignString(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestAssignString", `hello = "world"`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"world"`, token.ItemValue)

  noMoreTokens(t, l)
}

func TestCommentAfterAssignBoolean(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentAfterAssignBoolean", `hello = false // this is a bool`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(BOOLEAN, token.ItemType)
  assert.Equal("false", token.ItemValue)

  token = <-l.Items
  assert.Equal(COMMENT, token.ItemType)
  assert.Equal("// this is a bool", token.ItemValue)

  noMoreTokens(t, l)
}

func TestCommentAfterAssignInteger(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentAfterAssignInteger", `hello = 10 // this is an int`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("10", token.ItemValue)

  token = <-l.Items
  assert.Equal(COMMENT, token.ItemType)
  assert.Equal("// this is an int", token.ItemValue)

  noMoreTokens(t, l)
}

func TestCommentAfterAssignFloat(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentAfterAssignFloat", `hello = 1.0 // this is a float`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("1.0", token.ItemValue)

  token = <-l.Items
  assert.Equal(COMMENT, token.ItemType)
  assert.Equal("// this is a float", token.ItemValue)

  noMoreTokens(t, l)
}

func TestCommentAfterAssignString(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentAfterAssignFloat", `hello = "string"// this is a string`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"string"`, token.ItemValue)

  token = <-l.Items
  assert.Equal(COMMENT, token.ItemType)
  assert.Equal("// this is a string", token.ItemValue)

  noMoreTokens(t, l)
}

func TestQuotesInStringValue(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentAfterAssignFloat", `hello = "st\"r\"ing"`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"st\"r\"ing"`, token.ItemValue)

  noMoreTokens(t, l)
}

func TestSingleLineListInts(t *testing.T) {
    t.Parallel()
    assert := assert.New(t)

    l := Lex("TestSingleLineListInts", "list = [1, 2, 3, 4]")

    token := <-l.Items
    assert.Equal(VARIABLE, token.ItemType)
    assert.Equal("list", token.ItemValue)

    token = <-l.Items
    assert.Equal(ASSIGNMENT, token.ItemType)
    assert.Equal("=", token.ItemValue)

    token = <-l.Items
    assert.Equal(LIST_START, token.ItemType)
    assert.Equal("[", token.ItemValue)

    token = <-l.Items
    assert.Equal(NUMBER, token.ItemType)
    assert.Equal("1", token.ItemValue)

    token = <-l.Items
    assert.Equal(NUMBER, token.ItemType)
    assert.Equal("2", token.ItemValue)

    token = <-l.Items
    assert.Equal(NUMBER, token.ItemType)
    assert.Equal("3", token.ItemValue)

    token = <-l.Items
    assert.Equal(NUMBER, token.ItemType)
    assert.Equal("4", token.ItemValue)

    token = <-l.Items
    assert.Equal(LIST_END, token.ItemType)
    assert.Equal("]", token.ItemValue)

    noMoreTokens(t, l)
}

func TestSingleLineListStrings(t *testing.T) {
    t.Parallel()
    assert := assert.New(t)

    l := Lex("TestSingleLineListStrings", `list = ["12", "23", "34", "45"]`)

    token := <-l.Items
    assert.Equal(VARIABLE, token.ItemType)
    assert.Equal("list", token.ItemValue)

    token = <-l.Items
    assert.Equal(ASSIGNMENT, token.ItemType)
    assert.Equal("=", token.ItemValue)

    token = <-l.Items
    assert.Equal(LIST_START, token.ItemType)
    assert.Equal("[", token.ItemValue)

    token = <-l.Items
    assert.Equal(STRING, token.ItemType)
    assert.Equal(`"12"`, token.ItemValue)

    token = <-l.Items
    assert.Equal(STRING, token.ItemType)
    assert.Equal(`"23"`, token.ItemValue)

    token = <-l.Items
    assert.Equal(STRING, token.ItemType)
    assert.Equal(`"34"`, token.ItemValue)

    token = <-l.Items
    assert.Equal(STRING, token.ItemType)
    assert.Equal(`"45"`, token.ItemValue)

    token = <-l.Items
    assert.Equal(LIST_END, token.ItemType)
    assert.Equal("]", token.ItemValue)

    noMoreTokens(t, l)
}

func TestUnterminatedString(t *testing.T) {
    t.Parallel()
    assert := assert.New(t)

    l := Lex("TestUnterminatedString", `hello = "unterminated string`)

    token := <-l.Items
    assert.Equal(VARIABLE, token.ItemType)
    assert.Equal("hello", token.ItemValue)

    token = <-l.Items
    assert.Equal(ASSIGNMENT, token.ItemType)
    assert.Equal("=", token.ItemValue)

    token = <-l.Items
    assert.Equal(ERROR, token.ItemType)

    noMoreTokens(t, l)
}

func TestEmptyLines(t *testing.T) {
    t.Parallel()
    assert := assert.New(t)

    l := Lex("TestEmptyLines", `hello = "string"

      whosits = 1234
      `)

    token := <-l.Items
    assert.Equal(VARIABLE, token.ItemType)
    assert.Equal("hello", token.ItemValue)

    token = <-l.Items
    assert.Equal(ASSIGNMENT, token.ItemType)
    assert.Equal("=", token.ItemValue)

    token = <-l.Items
    assert.Equal(STRING, token.ItemType)
    assert.Equal(`"string"`, token.ItemValue)

    token = <-l.Items
    assert.Equal(VARIABLE, token.ItemType)
    assert.Equal("whosits", token.ItemValue)

    token = <-l.Items
    assert.Equal(ASSIGNMENT, token.ItemType)
    assert.Equal("=", token.ItemValue)

    token = <-l.Items
    assert.Equal(NUMBER, token.ItemType)
    assert.Equal("1234", token.ItemValue)

    noMoreTokens(t, l)
}

func TestCommentOnly(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentOnly", "// hello world")

  token := <-l.Items
  assert.Equal(COMMENT, token.ItemType)
  assert.Equal("// hello world", token.ItemValue)

  noMoreTokens(t, l)
}
<commit_msg>Normalize indentation to 2 spaces<commit_after>package lexer

import (
  // "fmt"
  "testing"
  "github.com/stretchr/testify/assert"
)

func noMoreTokens(t *testing.T, l *Lexer) {
  for tok := range l.Items {
    t.Errorf("Unexpected token received '%s'", tok)
  }
}

func TestAssignBoolean(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestAssignBoolean", `hello = true`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(BOOLEAN, token.ItemType)
  assert.Equal("true", token.ItemValue)

  noMoreTokens(t, l)
}

func TestAssignInteger(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestAssignInteger", `hello = 10`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("10", token.ItemValue)

  noMoreTokens(t, l)
}

func TestAssignFloat(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestAssignFloat", `hello = 1.0`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("1.0", token.ItemValue)

  noMoreTokens(t, l)
}

func TestAssignString(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestAssignString", `hello = "world"`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"world"`, token.ItemValue)

  noMoreTokens(t, l)
}

func TestCommentAfterAssignBoolean(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentAfterAssignBoolean", `hello = false // this is a bool`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(BOOLEAN, token.ItemType)
  assert.Equal("false", token.ItemValue)

  token = <-l.Items
  assert.Equal(COMMENT, token.ItemType)
  assert.Equal("// this is a bool", token.ItemValue)

  noMoreTokens(t, l)
}

func TestCommentAfterAssignInteger(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentAfterAssignInteger", `hello = 10 // this is an int`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("10", token.ItemValue)

  token = <-l.Items
  assert.Equal(COMMENT, token.ItemType)
  assert.Equal("// this is an int", token.ItemValue)

  noMoreTokens(t, l)
}

func TestCommentAfterAssignFloat(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentAfterAssignFloat", `hello = 1.0 // this is a float`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("1.0", token.ItemValue)

  token = <-l.Items
  assert.Equal(COMMENT, token.ItemType)
  assert.Equal("// this is a float", token.ItemValue)

  noMoreTokens(t, l)
}

func TestCommentAfterAssignString(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentAfterAssignFloat", `hello = "string"// this is a string`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"string"`, token.ItemValue)

  token = <-l.Items
  assert.Equal(COMMENT, token.ItemType)
  assert.Equal("// this is a string", token.ItemValue)

  noMoreTokens(t, l)
}

func TestQuotesInStringValue(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentAfterAssignFloat", `hello = "st\"r\"ing"`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"st\"r\"ing"`, token.ItemValue)

  noMoreTokens(t, l)
}

func TestSingleLineListInts(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestSingleLineListInts", "list = [1, 2, 3, 4]")

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("list", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(LIST_START, token.ItemType)
  assert.Equal("[", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("1", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("2", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("3", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("4", token.ItemValue)

  token = <-l.Items
  assert.Equal(LIST_END, token.ItemType)
  assert.Equal("]", token.ItemValue)

  noMoreTokens(t, l)
}

func TestSingleLineListStrings(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestSingleLineListStrings", `list = ["12", "23", "34", "45"]`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("list", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(LIST_START, token.ItemType)
  assert.Equal("[", token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"12"`, token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"23"`, token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"34"`, token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"45"`, token.ItemValue)

  token = <-l.Items
  assert.Equal(LIST_END, token.ItemType)
  assert.Equal("]", token.ItemValue)

  noMoreTokens(t, l)
}

func TestUnterminatedString(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestUnterminatedString", `hello = "unterminated string`)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(ERROR, token.ItemType)

  noMoreTokens(t, l)
}

func TestEmptyLines(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestEmptyLines", `hello = "string"

    whosits = 1234
  `)

  token := <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("hello", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(STRING, token.ItemType)
  assert.Equal(`"string"`, token.ItemValue)

  token = <-l.Items
  assert.Equal(VARIABLE, token.ItemType)
  assert.Equal("whosits", token.ItemValue)

  token = <-l.Items
  assert.Equal(ASSIGNMENT, token.ItemType)
  assert.Equal("=", token.ItemValue)

  token = <-l.Items
  assert.Equal(NUMBER, token.ItemType)
  assert.Equal("1234", token.ItemValue)

  noMoreTokens(t, l)
}

func TestCommentOnly(t *testing.T) {
  t.Parallel()
  assert := assert.New(t)

  l := Lex("TestCommentOnly", "// hello world")

  token := <-l.Items
  assert.Equal(COMMENT, token.ItemType)
  assert.Equal("// hello world", token.ItemValue)

  noMoreTokens(t, l)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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 annotations

import (
	"fmt"
	"strings"

	"github.com/onsi/ginkgo"

	"k8s.io/ingress-nginx/test/e2e/framework"
)

var _ = framework.DescribeAnnotation("client-body-buffer-size", func() {
	f := framework.NewDefaultFramework("clientbodybuffersize")

	ginkgo.BeforeEach(func() {
		f.NewEchoDeployment()
	})

	ginkgo.It("should set client_body_buffer_size to 1000", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1000"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})
	})

	ginkgo.It("should set client_body_buffer_size to 1K", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1K"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})
	})

	ginkgo.It("should set client_body_buffer_size to 1k", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1k"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})
	})

	ginkgo.It("should set client_body_buffer_size to 1m", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1m"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})
	})

	ginkgo.It("should set client_body_buffer_size to 1M", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1M"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})
	})

	ginkgo.It("should not set client_body_buffer_size to invalid 1b", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1b"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return !strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})
	})
})
<commit_msg>added checks to verify backend works with the given configs (#7415)<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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 annotations

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/onsi/ginkgo"
	"k8s.io/ingress-nginx/test/e2e/framework"
)

var _ = framework.DescribeAnnotation("client-body-buffer-size", func() {
	f := framework.NewDefaultFramework("clientbodybuffersize")

	ginkgo.BeforeEach(func() {
		f.NewEchoDeployment()
	})

	ginkgo.It("should set client_body_buffer_size to 1000", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1000"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})

		f.HTTPTestClient().
			GET("/").
			WithHeader("Host", host).
			Expect().
			Status(http.StatusOK)
	})

	ginkgo.It("should set client_body_buffer_size to 1K", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1K"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})

		f.HTTPTestClient().
			GET("/").
			WithHeader("Host", host).
			Expect().
			Status(http.StatusOK)
	})

	ginkgo.It("should set client_body_buffer_size to 1k", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1k"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})

		f.HTTPTestClient().
			GET("/").
			WithHeader("Host", host).
			Expect().
			Status(http.StatusOK)
	})

	ginkgo.It("should set client_body_buffer_size to 1m", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1m"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})

		f.HTTPTestClient().
			GET("/").
			WithHeader("Host", host).
			Expect().
			Status(http.StatusOK)
	})

	ginkgo.It("should set client_body_buffer_size to 1M", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1M"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})

		f.HTTPTestClient().
			GET("/").
			WithHeader("Host", host).
			Expect().
			Status(http.StatusOK)
	})

	ginkgo.It("should not set client_body_buffer_size to invalid 1b", func() {
		host := "client-body-buffer-size.com"

		clientBodyBufferSize := "1b"
		annotations := make(map[string]string)
		annotations["nginx.ingress.kubernetes.io/client-body-buffer-size"] = clientBodyBufferSize

		ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
		f.EnsureIngress(ing)

		f.WaitForNginxServer(host,
			func(server string) bool {
				return !strings.Contains(server, fmt.Sprintf("client_body_buffer_size %s;", clientBodyBufferSize))
			})

		f.HTTPTestClient().
			GET("/").
			WithHeader("Host", host).
			Expect().
			Status(http.StatusOK)
	})
})
<|endoftext|>


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

package serial

/*

// MSDN article on Serial Communications:
// http://msdn.microsoft.com/en-us/library/ff802693.aspx

// Arduino Playground article on serial communication with Windows API:
// http://playground.arduino.cc/Interfacing/CPPWindows

#include <stdlib.h>
#include <windows.h>

//HANDLE invalid = INVALID_HANDLE_VALUE;

HKEY INVALID_PORT_LIST = 0;

HKEY openPortList() {
	HKEY handle;
	LPCSTR lpSubKey = "HARDWARE\\DEVICEMAP\\SERIALCOMM\\";
	DWORD res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, &handle);
	if (res != ERROR_SUCCESS)
		return INVALID_PORT_LIST;
	else
		return handle;
}

int countPortList(HKEY handle) {
	int count = 0;
	for (;;) {
		char name[256];
		DWORD nameSize = 256;
		DWORD res = RegEnumValueA(handle, count, name, &nameSize, NULL, NULL, NULL, NULL);
		if (res != ERROR_SUCCESS)
			return count;
		count++;
	}
}

char *getInPortList(HKEY handle, int i) {
	byte *data = (byte *) malloc(256);
	DWORD dataSize = 256;
	char name[256];
	DWORD nameSize = 256;
	DWORD res = RegEnumValueA(handle, i, name, &nameSize, NULL, NULL, data, &dataSize);
	if (res != ERROR_SUCCESS) {
		free(data);
		return NULL;
	}
	return data;
}

void closePortList(HKEY handle) {
	CloseHandle(handle);
}

*/
import "C"
import "syscall"
import "unsafe"

// opaque type that implements SerialPort interface for Windows
type windowsSerialPort struct {
	Handle syscall.Handle
}

func GetPortsList() ([]string, error) {
	portList := C.openPortList()
	if portList == C.INVALID_PORT_LIST {
		return nil, &SerialPortError{code: ERROR_ENUMERATING_PORTS}
	}
	n := C.countPortList(portList)

	list := make([]string, n)
	for i := range list {
		portName := C.getInPortList(portList, C.int(i))
		list[i] = C.GoString(portName)
		C.free(unsafe.Pointer(portName))
	}

	C.closePortList(portList)
	return list, nil
}

func (port windowsSerialPort) Close() error {
	return syscall.CloseHandle(port.Handle)
}

func (port windowsSerialPort) Read(p []byte) (int, error) {
	var readed uint32
	params := &DCB{}
	for {
		if err := syscall.ReadFile(port.Handle, p, &readed, nil); err != nil {
			return int(readed), err
		}
		if readed > 0 {
			return int(readed), nil
		}

		// At the moment it seems that the only reliable way to check if
		// a serial port is alive in Windows is to check if the SetCommState
		// function fails.

		GetCommState(port.Handle, params)
		if err := SetCommState(port.Handle, params); err != nil {
			port.Close()
			return 0, err
		}
	}
}

func (port windowsSerialPort) Write(p []byte) (int, error) {
	var writed uint32
	err := syscall.WriteFile(port.Handle, p, &writed, nil)
	return int(writed), err
}

const (
	DCB_BINARY                   = 0x00000001
	DCB_PARITY                   = 0x00000002
	DCB_OUT_X_CTS_FLOW           = 0x00000004
	DCB_OUT_X_DSR_FLOW           = 0x00000008
	DCB_DTR_CONTROL_DISABLE_MASK = ^0x00000030
	DCB_DTR_CONTROL_ENABLE       = 0x00000010
	DCB_DTR_CONTROL_HANDSHAKE    = 0x00000020
	DCB_DSR_SENSITIVITY          = 0x00000040
	DCB_TX_CONTINUE_ON_XOFF      = 0x00000080
	DCB_OUT_X                    = 0x00000100
	DCB_IN_X                     = 0x00000200
	DCB_ERROR_CHAR               = 0x00000400
	DCB_NULL                     = 0x00000800
	DCB_RTS_CONTROL_DISABLE_MASK = ^0x00003000
	DCB_RTS_CONTROL_ENABLE       = 0x00001000
	DCB_RTS_CONTROL_HANDSHAKE    = 0x00002000
	DCB_RTS_CONTROL_TOGGLE       = 0x00003000
	DCB_ABORT_ON_ERROR           = 0x00004000
)

type DCB struct {
	DCBlength uint32
	BaudRate  uint32

	// Flags field is a bitfield
	//  fBinary            :1
	//  fParity            :1
	//  fOutxCtsFlow       :1
	//  fOutxDsrFlow       :1
	//  fDtrControl        :2
	//  fDsrSensitivity    :1
	//  fTXContinueOnXoff  :1
	//  fOutX              :1
	//  fInX               :1
	//  fErrorChar         :1
	//  fNull              :1
	//  fRtsControl        :2
	//  fAbortOnError      :1
	//  fDummy2            :17
	Flags uint32

	wReserved  uint16
	XonLim     uint16
	XoffLim    uint16
	ByteSize   byte
	Parity     byte
	StopBits   byte
	XonChar    byte
	XoffChar   byte
	ErrorChar  byte
	EofChar    byte
	EvtChar    byte
	wReserved1 uint16
}

type COMMTIMEOUTS struct {
	ReadIntervalTimeout         uint32
	ReadTotalTimeoutMultiplier  uint32
	ReadTotalTimeoutConstant    uint32
	WriteTotalTimeoutMultiplier uint32
	WriteTotalTimeoutConstant   uint32
}

//sys GetCommState(handle syscall.Handle, dcb *DCB) (err error)
//sys SetCommState(handle syscall.Handle, dcb *DCB) (err error)
//sys SetCommTimeouts(handle syscall.Handle, timeouts *COMMTIMEOUTS) (err error)

const (
	NOPARITY    = 0
	ODDPARITY   = 1
	EVENPARITY  = 2
	MARKPARITY  = 3
	SPACEPARITY = 4
)

const (
	ONESTOPBIT   = 0
	ONE5STOPBITS = 1
	TWOSTOPBITS  = 2
)

func (port windowsSerialPort) SetMode(mode *Mode) error {
	params := DCB{}
	if err := GetCommState(port.Handle, &params); err != nil {
		port.Close()
		return &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}
	if mode.Baudrate == 0 {
		params.BaudRate = 9600 // Default to 9600
	} else {
		params.BaudRate = uint32(mode.BaudRate)
	}
	if mode.DataBits == 0 {
		params.ByteSize = 8 // Default to 8 bits
	} else {
		params.ByteSize = mode.DataBits
	}
	params.StopBits = mode.StopBits
	params.Parity = mode.Parity
	if err := SetCommState(port.Handle, &params); err != nil {
		port.Close()
		return &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}
	return nil
}

func OpenPort(portName string, mode Mode) (SerialPort, error) {
	portName = "\\\\.\\" + portName
	path, err := syscall.UTF16PtrFromString(portName)
	if err != nil {
		return nil, err
	}
	handle, err := syscall.CreateFile(
		path,
		syscall.GENERIC_READ|syscall.GENERIC_WRITE,
		0, nil,
		syscall.OPEN_EXISTING,
		0, //syscall.FILE_FLAG_OVERLAPPED,
		0)
	if err != nil {
		switch err {
		case syscall.ERROR_ACCESS_DENIED:
			return nil, &SerialPortError{code: ERROR_PORT_BUSY}
		case syscall.ERROR_FILE_NOT_FOUND:
			return nil, &SerialPortError{code: ERROR_PORT_NOT_FOUND}
		}
		return nil, err
	}
	// Create the serial port
	port := &windowsSerialPort{
		Handle: handle,
	}

	// Set port parameters
	if err := port.SetMode(mode); err != nil {
		port.Close()
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}

	params := &DCB{}
	err = GetCommState(port.Handle, params)
	if err != nil {
		port.Close()
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}
	params.Flags |= DCB_RTS_CONTROL_ENABLE | DCB_DTR_CONTROL_ENABLE
	params.Flags &= ^uint32(DCB_OUT_X_CTS_FLOW)
	params.Flags &= ^uint32(DCB_OUT_X_DSR_FLOW)
	params.Flags &= ^uint32(DCB_DSR_SENSITIVITY)
	params.Flags |= DCB_TX_CONTINUE_ON_XOFF
	params.Flags &= ^uint32(DCB_IN_X | DCB_OUT_X)
	params.Flags &= ^uint32(DCB_ERROR_CHAR)
	params.Flags &= ^uint32(DCB_NULL)
	params.Flags &= ^uint32(DCB_ABORT_ON_ERROR)
	params.XonLim = 2048
	params.XoffLim = 512
	params.XonChar = 17  // DC1
	params.XoffChar = 19 // C3
	err = SetCommState(port.Handle, params)
	if err != nil {
		port.Close()
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}

	// Set timeouts to 1 second
	timeouts := &COMMTIMEOUTS{
		ReadIntervalTimeout:         0xFFFFFFFF,
		ReadTotalTimeoutMultiplier:  0xFFFFFFFF,
		ReadTotalTimeoutConstant:    1000, // 1 sec
		WriteTotalTimeoutConstant:   0,
		WriteTotalTimeoutMultiplier: 0,
	}
	if err := SetCommTimeouts(port.Handle, timeouts); err != nil {
		port.Close()
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}

	return port, nil
}

// vi:ts=2
<commit_msg>Small fix to win API<commit_after>//
// Copyright 2014 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package serial

/*

// MSDN article on Serial Communications:
// http://msdn.microsoft.com/en-us/library/ff802693.aspx

// Arduino Playground article on serial communication with Windows API:
// http://playground.arduino.cc/Interfacing/CPPWindows

#include <stdlib.h>
#include <windows.h>

//HANDLE invalid = INVALID_HANDLE_VALUE;

HKEY INVALID_PORT_LIST = 0;

HKEY openPortList() {
	HKEY handle;
	LPCSTR lpSubKey = "HARDWARE\\DEVICEMAP\\SERIALCOMM\\";
	DWORD res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, &handle);
	if (res != ERROR_SUCCESS)
		return INVALID_PORT_LIST;
	else
		return handle;
}

int countPortList(HKEY handle) {
	int count = 0;
	for (;;) {
		char name[256];
		DWORD nameSize = 256;
		DWORD res = RegEnumValueA(handle, count, name, &nameSize, NULL, NULL, NULL, NULL);
		if (res != ERROR_SUCCESS)
			return count;
		count++;
	}
}

char *getInPortList(HKEY handle, int i) {
	byte *data = (byte *) malloc(256);
	DWORD dataSize = 256;
	char name[256];
	DWORD nameSize = 256;
	DWORD res = RegEnumValueA(handle, i, name, &nameSize, NULL, NULL, data, &dataSize);
	if (res != ERROR_SUCCESS) {
		free(data);
		return NULL;
	}
	return data;
}

void closePortList(HKEY handle) {
	CloseHandle(handle);
}

*/
import "C"
import "syscall"
import "unsafe"

// opaque type that implements SerialPort interface for Windows
type windowsSerialPort struct {
	Handle syscall.Handle
}

func GetPortsList() ([]string, error) {
	portList := C.openPortList()
	if portList == C.INVALID_PORT_LIST {
		return nil, &SerialPortError{code: ERROR_ENUMERATING_PORTS}
	}
	n := C.countPortList(portList)

	list := make([]string, n)
	for i := range list {
		portName := C.getInPortList(portList, C.int(i))
		list[i] = C.GoString(portName)
		C.free(unsafe.Pointer(portName))
	}

	C.closePortList(portList)
	return list, nil
}

func (port windowsSerialPort) Close() error {
	return syscall.CloseHandle(port.Handle)
}

func (port windowsSerialPort) Read(p []byte) (int, error) {
	var readed uint32
	params := &DCB{}
	for {
		if err := syscall.ReadFile(port.Handle, p, &readed, nil); err != nil {
			return int(readed), err
		}
		if readed > 0 {
			return int(readed), nil
		}

		// At the moment it seems that the only reliable way to check if
		// a serial port is alive in Windows is to check if the SetCommState
		// function fails.

		GetCommState(port.Handle, params)
		if err := SetCommState(port.Handle, params); err != nil {
			port.Close()
			return 0, err
		}
	}
}

func (port windowsSerialPort) Write(p []byte) (int, error) {
	var writed uint32
	err := syscall.WriteFile(port.Handle, p, &writed, nil)
	return int(writed), err
}

const (
	DCB_BINARY                   = 0x00000001
	DCB_PARITY                   = 0x00000002
	DCB_OUT_X_CTS_FLOW           = 0x00000004
	DCB_OUT_X_DSR_FLOW           = 0x00000008
	DCB_DTR_CONTROL_DISABLE_MASK = ^0x00000030
	DCB_DTR_CONTROL_ENABLE       = 0x00000010
	DCB_DTR_CONTROL_HANDSHAKE    = 0x00000020
	DCB_DSR_SENSITIVITY          = 0x00000040
	DCB_TX_CONTINUE_ON_XOFF      = 0x00000080
	DCB_OUT_X                    = 0x00000100
	DCB_IN_X                     = 0x00000200
	DCB_ERROR_CHAR               = 0x00000400
	DCB_NULL                     = 0x00000800
	DCB_RTS_CONTROL_DISABLE_MASK = ^0x00003000
	DCB_RTS_CONTROL_ENABLE       = 0x00001000
	DCB_RTS_CONTROL_HANDSHAKE    = 0x00002000
	DCB_RTS_CONTROL_TOGGLE       = 0x00003000
	DCB_ABORT_ON_ERROR           = 0x00004000
)

type DCB struct {
	DCBlength uint32
	BaudRate  uint32

	// Flags field is a bitfield
	//  fBinary            :1
	//  fParity            :1
	//  fOutxCtsFlow       :1
	//  fOutxDsrFlow       :1
	//  fDtrControl        :2
	//  fDsrSensitivity    :1
	//  fTXContinueOnXoff  :1
	//  fOutX              :1
	//  fInX               :1
	//  fErrorChar         :1
	//  fNull              :1
	//  fRtsControl        :2
	//  fAbortOnError      :1
	//  fDummy2            :17
	Flags uint32

	wReserved  uint16
	XonLim     uint16
	XoffLim    uint16
	ByteSize   byte
	Parity     byte
	StopBits   byte
	XonChar    byte
	XoffChar   byte
	ErrorChar  byte
	EofChar    byte
	EvtChar    byte
	wReserved1 uint16
}

type COMMTIMEOUTS struct {
	ReadIntervalTimeout         uint32
	ReadTotalTimeoutMultiplier  uint32
	ReadTotalTimeoutConstant    uint32
	WriteTotalTimeoutMultiplier uint32
	WriteTotalTimeoutConstant   uint32
}

//sys GetCommState(handle syscall.Handle, dcb *DCB) (err error)
//sys SetCommState(handle syscall.Handle, dcb *DCB) (err error)
//sys SetCommTimeouts(handle syscall.Handle, timeouts *COMMTIMEOUTS) (err error)

const (
	NOPARITY    = 0 // Default
	ODDPARITY   = 1
	EVENPARITY  = 2
	MARKPARITY  = 3
	SPACEPARITY = 4
)

const (
	ONESTOPBIT   = 0 // Default
	ONE5STOPBITS = 1
	TWOSTOPBITS  = 2
)

func (port windowsSerialPort) SetMode(mode *Mode) error {
	params := DCB{}
	if err := GetCommState(port.Handle, &params); err != nil {
		port.Close()
		return &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}
	if mode.BaudRate == 0 {
		params.BaudRate = 9600 // Default to 9600
	} else {
		params.BaudRate = uint32(mode.BaudRate)
	}
	if mode.DataBits == 0 {
		params.ByteSize = 8 // Default to 8 bits
	} else {
		params.ByteSize = byte(mode.DataBits)
	}
	params.StopBits = byte(mode.StopBits)
	params.Parity = byte(mode.Parity)
	if err := SetCommState(port.Handle, &params); err != nil {
		port.Close()
		return &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}
	return nil
}

func OpenPort(portName string, mode *Mode) (SerialPort, error) {
	portName = "\\\\.\\" + portName
	path, err := syscall.UTF16PtrFromString(portName)
	if err != nil {
		return nil, err
	}
	handle, err := syscall.CreateFile(
		path,
		syscall.GENERIC_READ|syscall.GENERIC_WRITE,
		0, nil,
		syscall.OPEN_EXISTING,
		0, //syscall.FILE_FLAG_OVERLAPPED,
		0)
	if err != nil {
		switch err {
		case syscall.ERROR_ACCESS_DENIED:
			return nil, &SerialPortError{code: ERROR_PORT_BUSY}
		case syscall.ERROR_FILE_NOT_FOUND:
			return nil, &SerialPortError{code: ERROR_PORT_NOT_FOUND}
		}
		return nil, err
	}
	// Create the serial port
	port := &windowsSerialPort{
		Handle: handle,
	}

	// Set port parameters
	if err := port.SetMode(mode); err != nil {
		port.Close()
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}

	params := &DCB{}
	err = GetCommState(port.Handle, params)
	if err != nil {
		port.Close()
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}
	params.Flags |= DCB_RTS_CONTROL_ENABLE | DCB_DTR_CONTROL_ENABLE
	params.Flags &= ^uint32(DCB_OUT_X_CTS_FLOW)
	params.Flags &= ^uint32(DCB_OUT_X_DSR_FLOW)
	params.Flags &= ^uint32(DCB_DSR_SENSITIVITY)
	params.Flags |= DCB_TX_CONTINUE_ON_XOFF
	params.Flags &= ^uint32(DCB_IN_X | DCB_OUT_X)
	params.Flags &= ^uint32(DCB_ERROR_CHAR)
	params.Flags &= ^uint32(DCB_NULL)
	params.Flags &= ^uint32(DCB_ABORT_ON_ERROR)
	params.XonLim = 2048
	params.XoffLim = 512
	params.XonChar = 17  // DC1
	params.XoffChar = 19 // C3
	err = SetCommState(port.Handle, params)
	if err != nil {
		port.Close()
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}

	// Set timeouts to 1 second
	timeouts := &COMMTIMEOUTS{
		ReadIntervalTimeout:         0xFFFFFFFF,
		ReadTotalTimeoutMultiplier:  0xFFFFFFFF,
		ReadTotalTimeoutConstant:    1000, // 1 sec
		WriteTotalTimeoutConstant:   0,
		WriteTotalTimeoutMultiplier: 0,
	}
	if err := SetCommTimeouts(port.Handle, timeouts); err != nil {
		port.Close()
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}

	return port, nil
}

// vi:ts=2
<|endoftext|>


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

SPDX-License-Identifier: Apache-2.0
*/

package localconfig

import (
	"fmt"

	"strings"
	"time"

	"github.com/hyperledger/fabric/common/flogging"
	"github.com/hyperledger/fabric/common/policies"
	"github.com/hyperledger/fabric/common/viperutil"
	logging "github.com/op/go-logging"

	"github.com/spf13/viper"

	"path/filepath"

	cf "github.com/hyperledger/fabric/core/config"
	"github.com/hyperledger/fabric/msp"
)

const (
	pkgLogID = "common/tools/configtxgen/localconfig"

	// Prefix identifies the prefix for the configtxgen-related ENV vars.
	Prefix string = "CONFIGTX"
)

var (
	logger *logging.Logger

	configName string
)

func init() {
	logger = flogging.MustGetLogger(pkgLogID)
	flogging.SetModuleLevel(pkgLogID, "error")

	configName = strings.ToLower(Prefix)
}

const (
	// TestChainID is the channel name used for testing purposes when one is not given
	TestChainID = "testchainid"

	// SampleInsecureSoloProfile references the sample profile which does not include any MSPs and uses solo for ordering.
	SampleInsecureSoloProfile = "SampleInsecureSolo"
	// SampleDevModeSoloProfile references the sample profile which requires only basic membership for admin privileges and uses solo for ordering.
	SampleDevModeSoloProfile = "SampleDevModeSolo"
	// SampleSingleMSPSoloProfile references the sample profile which includes only the sample MSP and uses solo for ordering.
	SampleSingleMSPSoloProfile = "SampleSingleMSPSolo"

	// SampleInsecureKafkaProfile references the sample profile which does not include any MSPs and uses Kafka for ordering.
	SampleInsecureKafkaProfile = "SampleInsecureKafka"
	// SampleDevModeKafkaProfile references the sample profile which requires only basic membership for admin privileges and uses Kafka for ordering.
	SampleDevModeKafkaProfile = "SampleDevModeKafka"
	// SampleSingleMSPKafkaProfile references the sample profile which includes only the sample MSP and uses Kafka for ordering.
	SampleSingleMSPKafkaProfile = "SampleSingleMSPKafka"

	// SampleSingleMSPChannelProfile references the sample profile which includes only the sample MSP and is used to create a channel
	SampleSingleMSPChannelProfile = "SampleSingleMSPChannel"

	// SampleConsortiumName is the sample consortium from the sample configtx.yaml
	SampleConsortiumName = "SampleConsortium"
	// SampleOrgName is the name of the sample org in the sample profiles
	SampleOrgName = "SampleOrg"

	// AdminRoleAdminPrincipal is set as AdminRole to cause the MSP role of type Admin to be used as the admin principal default
	AdminRoleAdminPrincipal = "Role.ADMIN"
	// MemberRoleAdminPrincipal is set as AdminRole to cause the MSP role of type Member to be used as the admin principal default
	MemberRoleAdminPrincipal = "Role.MEMBER"
)

// TopLevel consists of the structs used by the configtxgen tool.
type TopLevel struct {
	Profiles      map[string]*Profile        `yaml:"Profiles"`
	Organizations []*Organization            `yaml:"Organizations"`
	Channel       *Profile                   `yaml:"Channel"`
	Application   *Application               `yaml:"Application"`
	Orderer       *Orderer                   `yaml:"Orderer"`
	Capabilities  map[string]map[string]bool `yaml:"Capabilities"`
	Resources     *Resources                 `yaml:"Resources"`
}

// Profile encodes orderer/application configuration combinations for the configtxgen tool.
type Profile struct {
	Consortium   string                 `yaml:"Consortium"`
	Application  *Application           `yaml:"Application"`
	Orderer      *Orderer               `yaml:"Orderer"`
	Consortiums  map[string]*Consortium `yaml:"Consortiums"`
	Capabilities map[string]bool        `yaml:"Capabilities"`
	Policies     map[string]*Policy     `yaml:"Policies"`
}

// Policy encodes a channel config policy
type Policy struct {
	Type string `yaml:"Type"`
	Rule string `yaml:"Rule"`
}

// Consortium represents a group of organizations which may create channels with eachother
type Consortium struct {
	Organizations []*Organization `yaml:"Organizations"`
}

// Application encodes the application-level configuration needed in config transactions.
type Application struct {
	Organizations []*Organization    `yaml:"Organizations"`
	Capabilities  map[string]bool    `yaml:"Capabilities"`
	Resources     *Resources         `yaml:"Resources"`
	Policies      map[string]*Policy `yaml:"Policies"`
	ACLs          map[string]string  `yaml:"ACLs"`
}

// Resouces encodes the application-level resources configuration needed to seed the resource tree
type Resources struct {
	DefaultModPolicy string
}

// Organization encodes the organization-level configuration needed in config transactions.
type Organization struct {
	Name     string             `yaml:"Name"`
	ID       string             `yaml:"ID"`
	MSPDir   string             `yaml:"MSPDir"`
	MSPType  string             `yaml:"MSPType"`
	Policies map[string]*Policy `yaml:"Policies"`

	// Note: Viper deserialization does not seem to care for
	// embedding of types, so we use one organization struct
	// for both orderers and applications.
	AnchorPeers []*AnchorPeer `yaml:"AnchorPeers"`

	// AdminPrincipal is deprecated and may be removed in a future release
	// it was used for modifying the default policy generation, but policies
	// may now be specified explicitly so it is redundant and unnecessary
	AdminPrincipal string `yaml:"AdminPrincipal"`
}

// AnchorPeer encodes the necessary fields to identify an anchor peer.
type AnchorPeer struct {
	Host string `yaml:"Host"`
	Port int    `yaml:"Port"`
}

// Orderer contains configuration which is used for the
// bootstrapping of an orderer by the provisional bootstrapper.
type Orderer struct {
	OrdererType   string             `yaml:"OrdererType"`
	Addresses     []string           `yaml:"Addresses"`
	BatchTimeout  time.Duration      `yaml:"BatchTimeout"`
	BatchSize     BatchSize          `yaml:"BatchSize"`
	Kafka         Kafka              `yaml:"Kafka"`
	Organizations []*Organization    `yaml:"Organizations"`
	MaxChannels   uint64             `yaml:"MaxChannels"`
	Capabilities  map[string]bool    `yaml:"Capabilities"`
	Policies      map[string]*Policy `yaml:"Policies"`
}

// BatchSize contains configuration affecting the size of batches.
type BatchSize struct {
	MaxMessageCount   uint32 `yaml:"MaxMessageSize"`
	AbsoluteMaxBytes  uint32 `yaml:"AbsoluteMaxBytes"`
	PreferredMaxBytes uint32 `yaml:"PreferredMaxBytes"`
}

// Kafka contains configuration for the Kafka-based orderer.
type Kafka struct {
	Brokers []string `yaml:"Brokers"`
}

var genesisDefaults = TopLevel{
	Orderer: &Orderer{
		OrdererType:  "solo",
		Addresses:    []string{"127.0.0.1:7050"},
		BatchTimeout: 2 * time.Second,
		BatchSize: BatchSize{
			MaxMessageCount:   10,
			AbsoluteMaxBytes:  10 * 1024 * 1024,
			PreferredMaxBytes: 512 * 1024,
		},
		Kafka: Kafka{
			Brokers: []string{"127.0.0.1:9092"},
		},
	},
}

// LoadTopLevel simply loads the configtx.yaml file into the structs above
// and completes their initialization.  Note, for environment overrides to work properly
// within a profile, Load(profile string) should be called when attempting to work within
// a particular profile.
func LoadTopLevel() *TopLevel {
	config := viper.New()
	cf.InitViper(config, configName)

	// For environment variables
	config.SetEnvPrefix(Prefix)
	config.AutomaticEnv()

	replacer := strings.NewReplacer(".", "_")
	config.SetEnvKeyReplacer(replacer)

	err := config.ReadInConfig()
	if err != nil {
		logger.Panic("Error reading configuration: ", err)
	}
	logger.Debugf("Using config file: %s", config.ConfigFileUsed())

	var uconf TopLevel
	err = viperutil.EnhancedExactUnmarshal(config, &uconf)
	if err != nil {
		logger.Panic("Error unmarshaling config into struct: ", err)
	}

	(&uconf).completeInitialization(filepath.Dir(config.ConfigFileUsed()))

	logger.Infof("Loaded configuration: %s", config.ConfigFileUsed())

	return &uconf
}

// Load returns the orderer/application config combination that corresponds to a given profile.
func Load(profile string) *Profile {
	config := viper.New()
	cf.InitViper(config, configName)

	// For environment variables
	config.SetEnvPrefix(Prefix)
	config.AutomaticEnv()

	// This replacer allows substitution within the particular profile without having to fully qualify the name
	replacer := strings.NewReplacer(strings.ToUpper(fmt.Sprintf("profiles.%s.", profile)), "", ".", "_")
	config.SetEnvKeyReplacer(replacer)

	err := config.ReadInConfig()
	if err != nil {
		logger.Panic("Error reading configuration: ", err)
	}
	logger.Debugf("Using config file: %s", config.ConfigFileUsed())

	var uconf TopLevel
	err = viperutil.EnhancedExactUnmarshal(config, &uconf)
	if err != nil {
		logger.Panic("Error unmarshaling config into struct: ", err)
	}

	result, ok := uconf.Profiles[profile]
	if !ok {
		logger.Panic("Could not find profile: ", profile)
	}

	result.completeInitialization(filepath.Dir(config.ConfigFileUsed()))

	logger.Infof("Loaded configuration: %s", config.ConfigFileUsed())

	return result
}

func (t *TopLevel) completeInitialization(configDir string) {
	for _, org := range t.Organizations {
		org.completeInitialization(configDir)
	}

	if t.Orderer != nil {
		t.Orderer.completeInitialization()
	}
}

func (p *Profile) completeInitialization(configDir string) {
	if p.Orderer != nil {
		for _, org := range p.Orderer.Organizations {
			org.completeInitialization(configDir)
		}
	}

	if p.Application != nil {
		for _, org := range p.Application.Organizations {
			org.completeInitialization(configDir)
		}
		if p.Application.Resources != nil {
			p.Application.Resources.completeInitialization()
		}
	}

	if p.Consortiums != nil {
		for _, consortium := range p.Consortiums {
			for _, org := range consortium.Organizations {
				org.completeInitialization(configDir)
			}
		}
	}

	// Some profiles will not define orderer parameters
	if p.Orderer != nil {
		p.Orderer.completeInitialization()
	}
}

func (r *Resources) completeInitialization() {
	for {
		switch {
		case r.DefaultModPolicy == "":
			r.DefaultModPolicy = policies.ChannelApplicationAdmins
		default:
			return
		}
	}
}

func (org *Organization) completeInitialization(configDir string) {
	// set the MSP type; if none is specified we assume BCCSP
	if org.MSPType == "" {
		org.MSPType = msp.ProviderTypeToString(msp.FABRIC)
	}

	if org.AdminPrincipal == "" {
		org.AdminPrincipal = AdminRoleAdminPrincipal
	}
	translatePaths(configDir, org)
}

func (oc *Orderer) completeInitialization() {
	for {
		switch {
		case oc.OrdererType == "":
			logger.Infof("Orderer.OrdererType unset, setting to %s", genesisDefaults.Orderer.OrdererType)
			oc.OrdererType = genesisDefaults.Orderer.OrdererType
		case oc.Addresses == nil:
			logger.Infof("Orderer.Addresses unset, setting to %s", genesisDefaults.Orderer.Addresses)
			oc.Addresses = genesisDefaults.Orderer.Addresses
		case oc.BatchTimeout == 0:
			logger.Infof("Orderer.BatchTimeout unset, setting to %s", genesisDefaults.Orderer.BatchTimeout)
			oc.BatchTimeout = genesisDefaults.Orderer.BatchTimeout
		case oc.BatchSize.MaxMessageCount == 0:
			logger.Infof("Orderer.BatchSize.MaxMessageCount unset, setting to %s", genesisDefaults.Orderer.BatchSize.MaxMessageCount)
			oc.BatchSize.MaxMessageCount = genesisDefaults.Orderer.BatchSize.MaxMessageCount
		case oc.BatchSize.AbsoluteMaxBytes == 0:
			logger.Infof("Orderer.BatchSize.AbsoluteMaxBytes unset, setting to %s", genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes)
			oc.BatchSize.AbsoluteMaxBytes = genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes
		case oc.BatchSize.PreferredMaxBytes == 0:
			logger.Infof("Orderer.BatchSize.PreferredMaxBytes unset, setting to %s", genesisDefaults.Orderer.BatchSize.PreferredMaxBytes)
			oc.BatchSize.PreferredMaxBytes = genesisDefaults.Orderer.BatchSize.PreferredMaxBytes
		case oc.Kafka.Brokers == nil:
			logger.Infof("Orderer.Kafka.Brokers unset, setting to %v", genesisDefaults.Orderer.Kafka.Brokers)
			oc.Kafka.Brokers = genesisDefaults.Orderer.Kafka.Brokers
		default:
			return
		}
	}
}

func translatePaths(configDir string, org *Organization) {
	cf.TranslatePathInPlace(configDir, &org.MSPDir)
}
<commit_msg>[FAB-9510] Mismatch of yaml label<commit_after>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package localconfig

import (
	"fmt"

	"strings"
	"time"

	"github.com/hyperledger/fabric/common/flogging"
	"github.com/hyperledger/fabric/common/policies"
	"github.com/hyperledger/fabric/common/viperutil"
	logging "github.com/op/go-logging"

	"github.com/spf13/viper"

	"path/filepath"

	cf "github.com/hyperledger/fabric/core/config"
	"github.com/hyperledger/fabric/msp"
)

const (
	pkgLogID = "common/tools/configtxgen/localconfig"

	// Prefix identifies the prefix for the configtxgen-related ENV vars.
	Prefix string = "CONFIGTX"
)

var (
	logger *logging.Logger

	configName string
)

func init() {
	logger = flogging.MustGetLogger(pkgLogID)
	flogging.SetModuleLevel(pkgLogID, "error")

	configName = strings.ToLower(Prefix)
}

const (
	// TestChainID is the channel name used for testing purposes when one is not given
	TestChainID = "testchainid"

	// SampleInsecureSoloProfile references the sample profile which does not include any MSPs and uses solo for ordering.
	SampleInsecureSoloProfile = "SampleInsecureSolo"
	// SampleDevModeSoloProfile references the sample profile which requires only basic membership for admin privileges and uses solo for ordering.
	SampleDevModeSoloProfile = "SampleDevModeSolo"
	// SampleSingleMSPSoloProfile references the sample profile which includes only the sample MSP and uses solo for ordering.
	SampleSingleMSPSoloProfile = "SampleSingleMSPSolo"

	// SampleInsecureKafkaProfile references the sample profile which does not include any MSPs and uses Kafka for ordering.
	SampleInsecureKafkaProfile = "SampleInsecureKafka"
	// SampleDevModeKafkaProfile references the sample profile which requires only basic membership for admin privileges and uses Kafka for ordering.
	SampleDevModeKafkaProfile = "SampleDevModeKafka"
	// SampleSingleMSPKafkaProfile references the sample profile which includes only the sample MSP and uses Kafka for ordering.
	SampleSingleMSPKafkaProfile = "SampleSingleMSPKafka"

	// SampleSingleMSPChannelProfile references the sample profile which includes only the sample MSP and is used to create a channel
	SampleSingleMSPChannelProfile = "SampleSingleMSPChannel"

	// SampleConsortiumName is the sample consortium from the sample configtx.yaml
	SampleConsortiumName = "SampleConsortium"
	// SampleOrgName is the name of the sample org in the sample profiles
	SampleOrgName = "SampleOrg"

	// AdminRoleAdminPrincipal is set as AdminRole to cause the MSP role of type Admin to be used as the admin principal default
	AdminRoleAdminPrincipal = "Role.ADMIN"
	// MemberRoleAdminPrincipal is set as AdminRole to cause the MSP role of type Member to be used as the admin principal default
	MemberRoleAdminPrincipal = "Role.MEMBER"
)

// TopLevel consists of the structs used by the configtxgen tool.
type TopLevel struct {
	Profiles      map[string]*Profile        `yaml:"Profiles"`
	Organizations []*Organization            `yaml:"Organizations"`
	Channel       *Profile                   `yaml:"Channel"`
	Application   *Application               `yaml:"Application"`
	Orderer       *Orderer                   `yaml:"Orderer"`
	Capabilities  map[string]map[string]bool `yaml:"Capabilities"`
	Resources     *Resources                 `yaml:"Resources"`
}

// Profile encodes orderer/application configuration combinations for the configtxgen tool.
type Profile struct {
	Consortium   string                 `yaml:"Consortium"`
	Application  *Application           `yaml:"Application"`
	Orderer      *Orderer               `yaml:"Orderer"`
	Consortiums  map[string]*Consortium `yaml:"Consortiums"`
	Capabilities map[string]bool        `yaml:"Capabilities"`
	Policies     map[string]*Policy     `yaml:"Policies"`
}

// Policy encodes a channel config policy
type Policy struct {
	Type string `yaml:"Type"`
	Rule string `yaml:"Rule"`
}

// Consortium represents a group of organizations which may create channels with eachother
type Consortium struct {
	Organizations []*Organization `yaml:"Organizations"`
}

// Application encodes the application-level configuration needed in config transactions.
type Application struct {
	Organizations []*Organization    `yaml:"Organizations"`
	Capabilities  map[string]bool    `yaml:"Capabilities"`
	Resources     *Resources         `yaml:"Resources"`
	Policies      map[string]*Policy `yaml:"Policies"`
	ACLs          map[string]string  `yaml:"ACLs"`
}

// Resouces encodes the application-level resources configuration needed to seed the resource tree
type Resources struct {
	DefaultModPolicy string
}

// Organization encodes the organization-level configuration needed in config transactions.
type Organization struct {
	Name     string             `yaml:"Name"`
	ID       string             `yaml:"ID"`
	MSPDir   string             `yaml:"MSPDir"`
	MSPType  string             `yaml:"MSPType"`
	Policies map[string]*Policy `yaml:"Policies"`

	// Note: Viper deserialization does not seem to care for
	// embedding of types, so we use one organization struct
	// for both orderers and applications.
	AnchorPeers []*AnchorPeer `yaml:"AnchorPeers"`

	// AdminPrincipal is deprecated and may be removed in a future release
	// it was used for modifying the default policy generation, but policies
	// may now be specified explicitly so it is redundant and unnecessary
	AdminPrincipal string `yaml:"AdminPrincipal"`
}

// AnchorPeer encodes the necessary fields to identify an anchor peer.
type AnchorPeer struct {
	Host string `yaml:"Host"`
	Port int    `yaml:"Port"`
}

// Orderer contains configuration which is used for the
// bootstrapping of an orderer by the provisional bootstrapper.
type Orderer struct {
	OrdererType   string             `yaml:"OrdererType"`
	Addresses     []string           `yaml:"Addresses"`
	BatchTimeout  time.Duration      `yaml:"BatchTimeout"`
	BatchSize     BatchSize          `yaml:"BatchSize"`
	Kafka         Kafka              `yaml:"Kafka"`
	Organizations []*Organization    `yaml:"Organizations"`
	MaxChannels   uint64             `yaml:"MaxChannels"`
	Capabilities  map[string]bool    `yaml:"Capabilities"`
	Policies      map[string]*Policy `yaml:"Policies"`
}

// BatchSize contains configuration affecting the size of batches.
type BatchSize struct {
	MaxMessageCount   uint32 `yaml:"MaxMessageCount"`
	AbsoluteMaxBytes  uint32 `yaml:"AbsoluteMaxBytes"`
	PreferredMaxBytes uint32 `yaml:"PreferredMaxBytes"`
}

// Kafka contains configuration for the Kafka-based orderer.
type Kafka struct {
	Brokers []string `yaml:"Brokers"`
}

var genesisDefaults = TopLevel{
	Orderer: &Orderer{
		OrdererType:  "solo",
		Addresses:    []string{"127.0.0.1:7050"},
		BatchTimeout: 2 * time.Second,
		BatchSize: BatchSize{
			MaxMessageCount:   10,
			AbsoluteMaxBytes:  10 * 1024 * 1024,
			PreferredMaxBytes: 512 * 1024,
		},
		Kafka: Kafka{
			Brokers: []string{"127.0.0.1:9092"},
		},
	},
}

// LoadTopLevel simply loads the configtx.yaml file into the structs above
// and completes their initialization.  Note, for environment overrides to work properly
// within a profile, Load(profile string) should be called when attempting to work within
// a particular profile.
func LoadTopLevel() *TopLevel {
	config := viper.New()
	cf.InitViper(config, configName)

	// For environment variables
	config.SetEnvPrefix(Prefix)
	config.AutomaticEnv()

	replacer := strings.NewReplacer(".", "_")
	config.SetEnvKeyReplacer(replacer)

	err := config.ReadInConfig()
	if err != nil {
		logger.Panic("Error reading configuration: ", err)
	}
	logger.Debugf("Using config file: %s", config.ConfigFileUsed())

	var uconf TopLevel
	err = viperutil.EnhancedExactUnmarshal(config, &uconf)
	if err != nil {
		logger.Panic("Error unmarshaling config into struct: ", err)
	}

	(&uconf).completeInitialization(filepath.Dir(config.ConfigFileUsed()))

	logger.Infof("Loaded configuration: %s", config.ConfigFileUsed())

	return &uconf
}

// Load returns the orderer/application config combination that corresponds to a given profile.
func Load(profile string) *Profile {
	config := viper.New()
	cf.InitViper(config, configName)

	// For environment variables
	config.SetEnvPrefix(Prefix)
	config.AutomaticEnv()

	// This replacer allows substitution within the particular profile without having to fully qualify the name
	replacer := strings.NewReplacer(strings.ToUpper(fmt.Sprintf("profiles.%s.", profile)), "", ".", "_")
	config.SetEnvKeyReplacer(replacer)

	err := config.ReadInConfig()
	if err != nil {
		logger.Panic("Error reading configuration: ", err)
	}
	logger.Debugf("Using config file: %s", config.ConfigFileUsed())

	var uconf TopLevel
	err = viperutil.EnhancedExactUnmarshal(config, &uconf)
	if err != nil {
		logger.Panic("Error unmarshaling config into struct: ", err)
	}

	result, ok := uconf.Profiles[profile]
	if !ok {
		logger.Panic("Could not find profile: ", profile)
	}

	result.completeInitialization(filepath.Dir(config.ConfigFileUsed()))

	logger.Infof("Loaded configuration: %s", config.ConfigFileUsed())

	return result
}

func (t *TopLevel) completeInitialization(configDir string) {
	for _, org := range t.Organizations {
		org.completeInitialization(configDir)
	}

	if t.Orderer != nil {
		t.Orderer.completeInitialization()
	}
}

func (p *Profile) completeInitialization(configDir string) {
	if p.Orderer != nil {
		for _, org := range p.Orderer.Organizations {
			org.completeInitialization(configDir)
		}
	}

	if p.Application != nil {
		for _, org := range p.Application.Organizations {
			org.completeInitialization(configDir)
		}
		if p.Application.Resources != nil {
			p.Application.Resources.completeInitialization()
		}
	}

	if p.Consortiums != nil {
		for _, consortium := range p.Consortiums {
			for _, org := range consortium.Organizations {
				org.completeInitialization(configDir)
			}
		}
	}

	// Some profiles will not define orderer parameters
	if p.Orderer != nil {
		p.Orderer.completeInitialization()
	}
}

func (r *Resources) completeInitialization() {
	for {
		switch {
		case r.DefaultModPolicy == "":
			r.DefaultModPolicy = policies.ChannelApplicationAdmins
		default:
			return
		}
	}
}

func (org *Organization) completeInitialization(configDir string) {
	// set the MSP type; if none is specified we assume BCCSP
	if org.MSPType == "" {
		org.MSPType = msp.ProviderTypeToString(msp.FABRIC)
	}

	if org.AdminPrincipal == "" {
		org.AdminPrincipal = AdminRoleAdminPrincipal
	}
	translatePaths(configDir, org)
}

func (oc *Orderer) completeInitialization() {
	for {
		switch {
		case oc.OrdererType == "":
			logger.Infof("Orderer.OrdererType unset, setting to %s", genesisDefaults.Orderer.OrdererType)
			oc.OrdererType = genesisDefaults.Orderer.OrdererType
		case oc.Addresses == nil:
			logger.Infof("Orderer.Addresses unset, setting to %s", genesisDefaults.Orderer.Addresses)
			oc.Addresses = genesisDefaults.Orderer.Addresses
		case oc.BatchTimeout == 0:
			logger.Infof("Orderer.BatchTimeout unset, setting to %s", genesisDefaults.Orderer.BatchTimeout)
			oc.BatchTimeout = genesisDefaults.Orderer.BatchTimeout
		case oc.BatchSize.MaxMessageCount == 0:
			logger.Infof("Orderer.BatchSize.MaxMessageCount unset, setting to %s", genesisDefaults.Orderer.BatchSize.MaxMessageCount)
			oc.BatchSize.MaxMessageCount = genesisDefaults.Orderer.BatchSize.MaxMessageCount
		case oc.BatchSize.AbsoluteMaxBytes == 0:
			logger.Infof("Orderer.BatchSize.AbsoluteMaxBytes unset, setting to %s", genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes)
			oc.BatchSize.AbsoluteMaxBytes = genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes
		case oc.BatchSize.PreferredMaxBytes == 0:
			logger.Infof("Orderer.BatchSize.PreferredMaxBytes unset, setting to %s", genesisDefaults.Orderer.BatchSize.PreferredMaxBytes)
			oc.BatchSize.PreferredMaxBytes = genesisDefaults.Orderer.BatchSize.PreferredMaxBytes
		case oc.Kafka.Brokers == nil:
			logger.Infof("Orderer.Kafka.Brokers unset, setting to %v", genesisDefaults.Orderer.Kafka.Brokers)
			oc.Kafka.Brokers = genesisDefaults.Orderer.Kafka.Brokers
		default:
			return
		}
	}
}

func translatePaths(configDir string, org *Organization) {
	cf.TranslatePathInPlace(configDir, &org.MSPDir)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach Authors.
//
// 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. See the AUTHORS file
// for names of contributors.
//
// Author: Daniel Theophanes (kardianos@gmail.com)

package server

import (
	"net"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/storage/engine"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/log"
)

// Context defaults.
const (
	defaultAddr           = ":8080"
	defaultMaxOffset      = 250 * time.Millisecond
	defaultGossipInterval = 2 * time.Second
	defaultCacheSize      = 1 << 30 // GB
)

// Context holds parameters needed to setup a server.
// Calling "server/cli".InitFlags(ctx *Context) will initialize Context using
// command flags. Keep in sync with "server/cli/flags.go".
type Context struct {
	// Addr is the host:port to bind for HTTP/RPC traffic.
	Addr string

	// Certs specifies a directory containing RSA key and x509 certs.
	Certs string

	// Stores is specified to enable durable key-value storage.
	// Memory-backed key value stores may be optionally specified
	// via mem=<integer byte size>.
	//
	// Stores specify a comma-separated list of stores specified by a
	// colon-separated list of device attributes followed by '=' and
	// either a filepath for a persistent store or an integer size in bytes for an
	// in-memory store. Device attributes typically include whether the store is
	// flash (ssd), spinny disk (hdd), fusion-io (fio), in-memory (mem); device
	// attributes might also include speeds and other specs (7200rpm, 200kiops, etc.).
	// For example, -store=hdd:7200rpm=/mnt/hda1,ssd=/mnt/ssd01,ssd=/mnt/ssd02,mem=1073741824
	Stores string

	// Attrs specifies a colon-separated list of node topography or machine
	// capabilities, used to match capabilities or location preferences specified
	// in zone configs.
	Attrs string

	// Maximum clock offset for the cluster.
	MaxOffset time.Duration

	// InitAndStart starts the server after bootstrap & initialization.
	InitAndStart bool

	// GossipBootstrap is a comma-separated list of node addresses that
	// act as bootstrap hosts for connecting to the gossip network.
	GossipBootstrap string

	// GossipInterval is a time interval specifying how often gossip is
	// communicated between hosts on the gossip network.
	GossipInterval time.Duration

	// Enables linearizable behaviour of operations on this node by making sure
	// that no commit timestamp is reported back to the client until all other
	// node clocks have necessarily passed it.
	Linearizable bool

	// CacheSize is the amount of memory in bytes to use for caching data.
	// The value is split evenly between the stores if there are more than one.
	CacheSize int64

	// Parsed values.

	// Engines is the storage instances specified by Stores.
	Engines []engine.Engine

	// NodeAttributes is the parsed representation of Attrs.
	NodeAttributes proto.Attributes

	// GossipBootstrapAddrs is a list of node addresses that act
	// as bootstrap hosts for connecting to the gossip network.
	GossipBootstrapAddrs []net.Addr
}

// NewContext returns a Context with default values.
func NewContext() *Context {
	return &Context{
		Addr: defaultAddr,

		MaxOffset: defaultMaxOffset,

		GossipInterval: defaultGossipInterval,

		CacheSize: defaultCacheSize,
	}
}

// Init interprets the stores parameter to initialize a slice of
// engine.Engine objects and parses node attributes.
func (ctx *Context) Init() error {
	storesRE := regexp.MustCompile(`([^=]+)=([^,]+)(,|$)`)
	// Error if regexp doesn't match.
	storeSpecs := storesRE.FindAllStringSubmatch(ctx.Stores, -1)
	if storeSpecs == nil || len(storeSpecs) == 0 {
		return util.Errorf("invalid or empty engines specification %q", ctx.Stores)
	}

	ctx.Engines = nil
	for _, store := range storeSpecs {
		if len(store) != 4 {
			return util.Errorf("unable to parse attributes and path from store %q", store[0])
		}
		// There are two matches for each store specification: the colon-separated
		// list of attributes and the path.
		engine, err := ctx.initEngine(store[1], store[2])
		if err != nil {
			return util.Errorf("unable to init engine for store %q: %v", store[0], err)
		}
		ctx.Engines = append(ctx.Engines, engine)
	}
	log.Infof("initialized %d storage engine(s)", len(ctx.Engines))

	ctx.NodeAttributes = parseAttributes(ctx.Attrs)

	ctx.GossipBootstrapAddrs = parseGossipBootstrapAddrs(ctx.GossipBootstrap)

	return nil
}

// initEngine parses the store attributes as a colon-separated list
// and instantiates an engine based on the dir parameter. If dir parses
// to an integer, it's taken to mean an in-memory engine; otherwise,
// dir is treated as a path and a RocksDB engine is created.
func (ctx *Context) initEngine(attrsStr, path string) (engine.Engine, error) {
	attrs := parseAttributes(attrsStr)
	if size, err := strconv.ParseUint(path, 10, 64); err == nil {
		if size == 0 {
			return nil, util.Errorf("unable to initialize an in-memory store with capacity 0")
		}
		return engine.NewInMem(attrs, int64(size)), nil
		// TODO(spencer): should be using rocksdb for in-memory stores and
		// relegate the InMem engine to usage only from unittests.
	}
	return engine.NewRocksDB(attrs, path, ctx.CacheSize), nil
}

// parseAttributes parses a colon-separated list of strings,
// filtering empty strings (i.e. "::" will yield no attributes.
// Returns the list of strings as Attributes.
func parseAttributes(attrsStr string) proto.Attributes {
	var filtered []string
	for _, attr := range strings.Split(attrsStr, ":") {
		if len(attr) != 0 {
			filtered = append(filtered, attr)
		}
	}
	sort.Strings(filtered)
	return proto.Attributes{Attrs: filtered}
}

// parseGossipBootstrapAddrs parses a comma-separated list of
// gossip bootstrap addresses.
func parseGossipBootstrapAddrs(gossipBootstrap string) []net.Addr {
	var bootstrapAddrs []net.Addr
	addresses := strings.Split(gossipBootstrap, ",")
	for _, addr := range addresses {
		addr = strings.TrimSpace(addr)
		if len(addr) == 0 {
			continue
		}
		bootstrapAddrs = append(bootstrapAddrs, util.MakeRawAddr("tcp", addr))
	}
	return bootstrapAddrs
}
<commit_msg>do not sort attribute lists<commit_after>// Copyright 2015 The Cockroach Authors.
//
// 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. See the AUTHORS file
// for names of contributors.
//
// Author: Daniel Theophanes (kardianos@gmail.com)

package server

import (
	"net"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/cockroachdb/cockroach/proto"
	"github.com/cockroachdb/cockroach/storage/engine"
	"github.com/cockroachdb/cockroach/util"
	"github.com/cockroachdb/cockroach/util/log"
)

// Context defaults.
const (
	defaultAddr           = ":8080"
	defaultMaxOffset      = 250 * time.Millisecond
	defaultGossipInterval = 2 * time.Second
	defaultCacheSize      = 1 << 30 // GB
)

// Context holds parameters needed to setup a server.
// Calling "server/cli".InitFlags(ctx *Context) will initialize Context using
// command flags. Keep in sync with "server/cli/flags.go".
type Context struct {
	// Addr is the host:port to bind for HTTP/RPC traffic.
	Addr string

	// Certs specifies a directory containing RSA key and x509 certs.
	Certs string

	// Stores is specified to enable durable key-value storage.
	// Memory-backed key value stores may be optionally specified
	// via mem=<integer byte size>.
	//
	// Stores specify a comma-separated list of stores specified by a
	// colon-separated list of device attributes followed by '=' and
	// either a filepath for a persistent store or an integer size in bytes for an
	// in-memory store. Device attributes typically include whether the store is
	// flash (ssd), spinny disk (hdd), fusion-io (fio), in-memory (mem); device
	// attributes might also include speeds and other specs (7200rpm, 200kiops, etc.).
	// For example, -store=hdd:7200rpm=/mnt/hda1,ssd=/mnt/ssd01,ssd=/mnt/ssd02,mem=1073741824
	Stores string

	// Attrs specifies a colon-separated list of node topography or machine
	// capabilities, used to match capabilities or location preferences specified
	// in zone configs.
	Attrs string

	// Maximum clock offset for the cluster.
	MaxOffset time.Duration

	// InitAndStart starts the server after bootstrap & initialization.
	InitAndStart bool

	// GossipBootstrap is a comma-separated list of node addresses that
	// act as bootstrap hosts for connecting to the gossip network.
	GossipBootstrap string

	// GossipInterval is a time interval specifying how often gossip is
	// communicated between hosts on the gossip network.
	GossipInterval time.Duration

	// Enables linearizable behaviour of operations on this node by making sure
	// that no commit timestamp is reported back to the client until all other
	// node clocks have necessarily passed it.
	Linearizable bool

	// CacheSize is the amount of memory in bytes to use for caching data.
	// The value is split evenly between the stores if there are more than one.
	CacheSize int64

	// Parsed values.

	// Engines is the storage instances specified by Stores.
	Engines []engine.Engine

	// NodeAttributes is the parsed representation of Attrs.
	NodeAttributes proto.Attributes

	// GossipBootstrapAddrs is a list of node addresses that act
	// as bootstrap hosts for connecting to the gossip network.
	GossipBootstrapAddrs []net.Addr
}

// NewContext returns a Context with default values.
func NewContext() *Context {
	return &Context{
		Addr: defaultAddr,

		MaxOffset: defaultMaxOffset,

		GossipInterval: defaultGossipInterval,

		CacheSize: defaultCacheSize,
	}
}

// Init interprets the stores parameter to initialize a slice of
// engine.Engine objects and parses node attributes.
func (ctx *Context) Init() error {
	storesRE := regexp.MustCompile(`([^=]+)=([^,]+)(,|$)`)
	// Error if regexp doesn't match.
	storeSpecs := storesRE.FindAllStringSubmatch(ctx.Stores, -1)
	if storeSpecs == nil || len(storeSpecs) == 0 {
		return util.Errorf("invalid or empty engines specification %q", ctx.Stores)
	}

	ctx.Engines = nil
	for _, store := range storeSpecs {
		if len(store) != 4 {
			return util.Errorf("unable to parse attributes and path from store %q", store[0])
		}
		// There are two matches for each store specification: the colon-separated
		// list of attributes and the path.
		engine, err := ctx.initEngine(store[1], store[2])
		if err != nil {
			return util.Errorf("unable to init engine for store %q: %v", store[0], err)
		}
		ctx.Engines = append(ctx.Engines, engine)
	}
	log.Infof("initialized %d storage engine(s)", len(ctx.Engines))

	ctx.NodeAttributes = parseAttributes(ctx.Attrs)

	ctx.GossipBootstrapAddrs = parseGossipBootstrapAddrs(ctx.GossipBootstrap)

	return nil
}

// initEngine parses the store attributes as a colon-separated list
// and instantiates an engine based on the dir parameter. If dir parses
// to an integer, it's taken to mean an in-memory engine; otherwise,
// dir is treated as a path and a RocksDB engine is created.
func (ctx *Context) initEngine(attrsStr, path string) (engine.Engine, error) {
	attrs := parseAttributes(attrsStr)
	if size, err := strconv.ParseUint(path, 10, 64); err == nil {
		if size == 0 {
			return nil, util.Errorf("unable to initialize an in-memory store with capacity 0")
		}
		return engine.NewInMem(attrs, int64(size)), nil
		// TODO(spencer): should be using rocksdb for in-memory stores and
		// relegate the InMem engine to usage only from unittests.
	}
	return engine.NewRocksDB(attrs, path, ctx.CacheSize), nil
}

// parseAttributes parses a colon-separated list of strings,
// filtering empty strings (i.e. "::" will yield no attributes.
// Returns the list of strings as Attributes.
func parseAttributes(attrsStr string) proto.Attributes {
	var filtered []string
	for _, attr := range strings.Split(attrsStr, ":") {
		if len(attr) != 0 {
			filtered = append(filtered, attr)
		}
	}
	return proto.Attributes{Attrs: filtered}
}

// parseGossipBootstrapAddrs parses a comma-separated list of
// gossip bootstrap addresses.
func parseGossipBootstrapAddrs(gossipBootstrap string) []net.Addr {
	var bootstrapAddrs []net.Addr
	addresses := strings.Split(gossipBootstrap, ",")
	for _, addr := range addresses {
		addr = strings.TrimSpace(addr)
		if len(addr) == 0 {
			continue
		}
		bootstrapAddrs = append(bootstrapAddrs, util.MakeRawAddr("tcp", addr))
	}
	return bootstrapAddrs
}
<|endoftext|>


<commit_before>//go:generate esc -o static.go -prefix ../web/build -pkg server ../web/build
package server

import (
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/cardigann/cardigann/config"
	"github.com/cardigann/cardigann/indexer"
	"github.com/cardigann/cardigann/logger"
	"github.com/cardigann/cardigann/torznab"
	"github.com/gorilla/mux"
)

const (
	buildDir = "/web/build"
)

var (
	log              = logger.Logger
	apiRoutePrefixes = []string{
		"/torznab/",
		"/download/",
		"/xhr/",
	}
)

type Params struct {
	BaseURL    string
	APIKey     []byte
	Passphrase string
	Config     config.Config
	Version    string
}

type handler struct {
	http.Handler
	Params      Params
	FileHandler http.Handler
	indexers    map[string]torznab.Indexer
}

func NewHandler(p Params) (http.Handler, error) {
	h := &handler{
		Params:      p,
		FileHandler: http.FileServer(FS(false)),
		indexers:    map[string]torznab.Indexer{},
	}

	router := mux.NewRouter()

	// torznab routes
	router.HandleFunc("/torznab/{indexer}", h.torznabHandler).Methods("GET")
	router.HandleFunc("/torznab/{indexer}/api", h.torznabHandler).Methods("GET")
	router.HandleFunc("/download/{indexer}/{token}/{filename}", h.downloadHandler).Methods("HEAD")
	router.HandleFunc("/download/{indexer}/{token}/{filename}", h.downloadHandler).Methods("GET")

	// xhr routes for the webapp
	router.HandleFunc("/xhr/indexers/{indexer}/test", h.getIndexerTestHandler).Methods("GET")
	router.HandleFunc("/xhr/indexers/{indexer}/config", h.getIndexersConfigHandler).Methods("GET")
	router.HandleFunc("/xhr/indexers/{indexer}/config", h.patchIndexersConfigHandler).Methods("PATCH")
	router.HandleFunc("/xhr/indexers", h.getIndexersHandler).Methods("GET")
	router.HandleFunc("/xhr/indexers", h.patchIndexersHandler).Methods("PATCH")
	router.HandleFunc("/xhr/auth", h.getAuthHandler).Methods("GET")
	router.HandleFunc("/xhr/auth", h.postAuthHandler).Methods("POST")
	router.HandleFunc("/xhr/version", h.getVersionHandler).Methods("GET")

	h.Handler = router
	return h, h.initialize()
}

func (h *handler) initialize() error {
	if h.Params.Passphrase == "" {
		apiKey, hasApiKey, err := h.Params.Config.Get("global", "apikey")
		if err != nil {
			return err
		}
		if !hasApiKey {
			k, err := h.sharedKey()
			if err != nil {
				return err
			}
			h.Params.APIKey = k
			return h.Params.Config.Set("global", "apikey", fmt.Sprintf("%x", k))
		}
		k, err := hex.DecodeString(apiKey)
		if err != nil {
			return err
		}
		h.Params.APIKey = k
	}
	return nil
}

func (h *handler) baseURL(r *http.Request, path string) (*url.URL, error) {
	if h.Params.BaseURL != "" {
		return url.Parse(h.Params.BaseURL)
	}
	proto := "http"
	if r.TLS != nil {
		proto = "https"
	}
	return url.Parse(fmt.Sprintf("%s://%s%s", proto, r.Host, path))
}

func (h *handler) createIndexer(key string) (torznab.Indexer, error) {
	def, err := indexer.LoadDefinition(key)
	if err != nil {
		log.WithError(err).Warnf("Failed to load definition for %q", key)
		return nil, err
	}

	log.WithFields(logrus.Fields{"indexer": key}).Debugf("Loaded indexer")
	indexer, err := indexer.NewRunner(def, h.Params.Config), nil
	if err != nil {
		return nil, err
	}

	return indexer, nil
}

func (h *handler) lookupIndexer(key string) (torznab.Indexer, error) {
	if key == "aggregate" {
		return h.createAggregate()
	}
	if _, ok := h.indexers[key]; !ok {
		indexer, err := h.createIndexer(key)
		if err != nil {
			return nil, err
		}
		h.indexers[key] = indexer
	}

	return h.indexers[key], nil
}

func (h *handler) createAggregate() (torznab.Indexer, error) {
	keys, err := indexer.ListDefinitions()
	if err != nil {
		return nil, err
	}

	agg := indexer.Aggregate{}
	for _, key := range keys {
		indexer, err := h.lookupIndexer(key)
		if err != nil {
			return nil, err
		}
		agg = append(agg, indexer)
	}

	return agg, nil
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if origin := r.Header.Get("Origin"); origin != "" {
		w.Header().Set("Access-Control-Allow-Origin", origin)
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Allow-Headers",
			"Accept, Cache-Control, Content-Type, Content-Length, Accept-Encoding, Authorization, Last-Event-ID")
	}
	if r.Method == "OPTIONS" {
		return
	}

	log.WithFields(logrus.Fields{
		"method": r.Method,
		"path":   r.URL.RequestURI(),
		"remote": r.RemoteAddr,
	}).Debugf("%s %s", r.Method, r.URL.RequestURI())

	for _, prefix := range apiRoutePrefixes {
		if strings.HasPrefix(r.URL.Path, prefix) {
			h.Handler.ServeHTTP(w, r)
			return
		}
	}

	h.FileHandler.ServeHTTP(w, r)
}

func (h *handler) torznabHandler(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	indexerID := params["indexer"]

	apiKey := r.URL.Query().Get("apikey")
	if !h.checkAPIKey(apiKey) {
		torznab.Error(w, "Invalid apikey parameter", torznab.ErrInsufficientPrivs)
		return
	}

	indexer, err := h.lookupIndexer(indexerID)
	if err != nil {
		torznab.Error(w, err.Error(), torznab.ErrIncorrectParameter)
		return
	}

	t := r.URL.Query().Get("t")

	if t == "" {
		http.Redirect(w, r, r.URL.Path+"?t=caps", http.StatusTemporaryRedirect)
		return
	}

	switch t {
	case "caps":
		indexer.Capabilities().ServeHTTP(w, r)

	case "search", "tvsearch", "tv-search":
		feed, err := h.search(r, indexer, indexerID)
		if err != nil {
			torznab.Error(w, err.Error(), torznab.ErrUnknownError)
			return
		}
		x, err := xml.MarshalIndent(feed, "", "  ")
		if err != nil {
			torznab.Error(w, err.Error(), torznab.ErrUnknownError)
			return
		}
		w.Header().Set("Content-Type", "application/rss+xml")
		w.Write(x)

	default:
		torznab.Error(w, "Unknown type parameter", torznab.ErrIncorrectParameter)
	}
}

func (h *handler) downloadHandler(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	token := params["token"]
	filename := params["filename"]

	log.WithFields(logrus.Fields{"filename": filename}).Debugf("Processing download via handler")

	k, err := h.sharedKey()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	t, err := decodeToken(token, k)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}

	indexer, err := h.lookupIndexer(t.Site)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}

	rc, _, err := indexer.Download(t.Link)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}

	w.Header().Set("Content-Type", "application/x-bittorrent")
	w.Header().Set("Content-Disposition", "attachment; filename="+filename)
	w.Header().Set("Content-Transfer-Encoding", "binary")

	defer rc.Close()
	io.Copy(w, rc)
}

func (h *handler) search(r *http.Request, indexer torznab.Indexer, siteKey string) (*torznab.ResultFeed, error) {
	baseURL, err := h.baseURL(r, "/download")
	if err != nil {
		return nil, err
	}

	query, err := torznab.ParseQuery(r.URL.Query())
	if err != nil {
		return nil, err
	}

	items, err := indexer.Search(query)
	if err != nil {
		return nil, err
	}

	feed := &torznab.ResultFeed{
		Info:  indexer.Info(),
		Items: items,
	}

	k, err := h.sharedKey()
	if err != nil {
		return nil, err
	}

	// rewrite links to use the server
	for idx, item := range feed.Items {
		t := &token{
			Site: item.Site,
			Link: item.Link,
		}

		te, err := t.Encode(k)
		if err != nil {
			log.Debugf("Error encoding token: %v", err)
			return nil, err
		}

		log.Debugf("Generated signed token %q", te)
		feed.Items[idx].Link = fmt.Sprintf("%s/%s/%s.torrent", baseURL.String(), te, item.Title)
	}

	return feed, err
}
<commit_msg>Remove indexer from download url slug<commit_after>//go:generate esc -o static.go -prefix ../web/build -pkg server ../web/build
package server

import (
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/cardigann/cardigann/config"
	"github.com/cardigann/cardigann/indexer"
	"github.com/cardigann/cardigann/logger"
	"github.com/cardigann/cardigann/torznab"
	"github.com/gorilla/mux"
)

const (
	buildDir = "/web/build"
)

var (
	log              = logger.Logger
	apiRoutePrefixes = []string{
		"/torznab/",
		"/download/",
		"/xhr/",
	}
)

type Params struct {
	BaseURL    string
	APIKey     []byte
	Passphrase string
	Config     config.Config
	Version    string
}

type handler struct {
	http.Handler
	Params      Params
	FileHandler http.Handler
	indexers    map[string]torznab.Indexer
}

func NewHandler(p Params) (http.Handler, error) {
	h := &handler{
		Params:      p,
		FileHandler: http.FileServer(FS(false)),
		indexers:    map[string]torznab.Indexer{},
	}

	router := mux.NewRouter()

	// torznab routes
	router.HandleFunc("/torznab/{indexer}", h.torznabHandler).Methods("GET")
	router.HandleFunc("/torznab/{indexer}/api", h.torznabHandler).Methods("GET")
	router.HandleFunc("/download/{indexer}/{token}/{filename}", h.downloadHandler).Methods("HEAD")
	router.HandleFunc("/download/{indexer}/{token}/{filename}", h.downloadHandler).Methods("GET")
	router.HandleFunc("/download/{token}/{filename}", h.downloadHandler).Methods("HEAD")
	router.HandleFunc("/download/{token}/{filename}", h.downloadHandler).Methods("GET")

	// xhr routes for the webapp
	router.HandleFunc("/xhr/indexers/{indexer}/test", h.getIndexerTestHandler).Methods("GET")
	router.HandleFunc("/xhr/indexers/{indexer}/config", h.getIndexersConfigHandler).Methods("GET")
	router.HandleFunc("/xhr/indexers/{indexer}/config", h.patchIndexersConfigHandler).Methods("PATCH")
	router.HandleFunc("/xhr/indexers", h.getIndexersHandler).Methods("GET")
	router.HandleFunc("/xhr/indexers", h.patchIndexersHandler).Methods("PATCH")
	router.HandleFunc("/xhr/auth", h.getAuthHandler).Methods("GET")
	router.HandleFunc("/xhr/auth", h.postAuthHandler).Methods("POST")
	router.HandleFunc("/xhr/version", h.getVersionHandler).Methods("GET")

	h.Handler = router
	return h, h.initialize()
}

func (h *handler) initialize() error {
	if h.Params.Passphrase == "" {
		apiKey, hasApiKey, err := h.Params.Config.Get("global", "apikey")
		if err != nil {
			return err
		}
		if !hasApiKey {
			k, err := h.sharedKey()
			if err != nil {
				return err
			}
			h.Params.APIKey = k
			return h.Params.Config.Set("global", "apikey", fmt.Sprintf("%x", k))
		}
		k, err := hex.DecodeString(apiKey)
		if err != nil {
			return err
		}
		h.Params.APIKey = k
	}
	return nil
}

func (h *handler) baseURL(r *http.Request, path string) (*url.URL, error) {
	if h.Params.BaseURL != "" {
		return url.Parse(h.Params.BaseURL)
	}
	proto := "http"
	if r.TLS != nil {
		proto = "https"
	}
	return url.Parse(fmt.Sprintf("%s://%s%s", proto, r.Host, path))
}

func (h *handler) createIndexer(key string) (torznab.Indexer, error) {
	def, err := indexer.LoadDefinition(key)
	if err != nil {
		log.WithError(err).Warnf("Failed to load definition for %q", key)
		return nil, err
	}

	log.WithFields(logrus.Fields{"indexer": key}).Debugf("Loaded indexer")
	indexer, err := indexer.NewRunner(def, h.Params.Config), nil
	if err != nil {
		return nil, err
	}

	return indexer, nil
}

func (h *handler) lookupIndexer(key string) (torznab.Indexer, error) {
	if key == "aggregate" {
		return h.createAggregate()
	}
	if _, ok := h.indexers[key]; !ok {
		indexer, err := h.createIndexer(key)
		if err != nil {
			return nil, err
		}
		h.indexers[key] = indexer
	}

	return h.indexers[key], nil
}

func (h *handler) createAggregate() (torznab.Indexer, error) {
	keys, err := indexer.ListDefinitions()
	if err != nil {
		return nil, err
	}

	agg := indexer.Aggregate{}
	for _, key := range keys {
		indexer, err := h.lookupIndexer(key)
		if err != nil {
			return nil, err
		}
		agg = append(agg, indexer)
	}

	return agg, nil
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if origin := r.Header.Get("Origin"); origin != "" {
		w.Header().Set("Access-Control-Allow-Origin", origin)
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Allow-Headers",
			"Accept, Cache-Control, Content-Type, Content-Length, Accept-Encoding, Authorization, Last-Event-ID")
	}
	if r.Method == "OPTIONS" {
		return
	}

	log.WithFields(logrus.Fields{
		"method": r.Method,
		"path":   r.URL.RequestURI(),
		"remote": r.RemoteAddr,
	}).Debugf("%s %s", r.Method, r.URL.RequestURI())

	for _, prefix := range apiRoutePrefixes {
		if strings.HasPrefix(r.URL.Path, prefix) {
			h.Handler.ServeHTTP(w, r)
			return
		}
	}

	h.FileHandler.ServeHTTP(w, r)
}

func (h *handler) torznabHandler(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	indexerID := params["indexer"]

	apiKey := r.URL.Query().Get("apikey")
	if !h.checkAPIKey(apiKey) {
		torznab.Error(w, "Invalid apikey parameter", torznab.ErrInsufficientPrivs)
		return
	}

	indexer, err := h.lookupIndexer(indexerID)
	if err != nil {
		torznab.Error(w, err.Error(), torznab.ErrIncorrectParameter)
		return
	}

	t := r.URL.Query().Get("t")

	if t == "" {
		http.Redirect(w, r, r.URL.Path+"?t=caps", http.StatusTemporaryRedirect)
		return
	}

	switch t {
	case "caps":
		indexer.Capabilities().ServeHTTP(w, r)

	case "search", "tvsearch", "tv-search":
		feed, err := h.search(r, indexer, indexerID)
		if err != nil {
			torznab.Error(w, err.Error(), torznab.ErrUnknownError)
			return
		}
		x, err := xml.MarshalIndent(feed, "", "  ")
		if err != nil {
			torznab.Error(w, err.Error(), torznab.ErrUnknownError)
			return
		}
		w.Header().Set("Content-Type", "application/rss+xml")
		w.Write(x)

	default:
		torznab.Error(w, "Unknown type parameter", torznab.ErrIncorrectParameter)
	}
}

func (h *handler) downloadHandler(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	token := params["token"]
	filename := params["filename"]

	log.WithFields(logrus.Fields{"filename": filename}).Debugf("Processing download via handler")

	k, err := h.sharedKey()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	t, err := decodeToken(token, k)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}

	indexer, err := h.lookupIndexer(t.Site)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}

	rc, _, err := indexer.Download(t.Link)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}

	w.Header().Set("Content-Type", "application/x-bittorrent")
	w.Header().Set("Content-Disposition", "attachment; filename="+filename)
	w.Header().Set("Content-Transfer-Encoding", "binary")

	defer rc.Close()
	io.Copy(w, rc)
}

func (h *handler) search(r *http.Request, indexer torznab.Indexer, siteKey string) (*torznab.ResultFeed, error) {
	baseURL, err := h.baseURL(r, "/download")
	if err != nil {
		return nil, err
	}

	query, err := torznab.ParseQuery(r.URL.Query())
	if err != nil {
		return nil, err
	}

	items, err := indexer.Search(query)
	if err != nil {
		return nil, err
	}

	feed := &torznab.ResultFeed{
		Info:  indexer.Info(),
		Items: items,
	}

	k, err := h.sharedKey()
	if err != nil {
		return nil, err
	}

	// rewrite links to use the server
	for idx, item := range feed.Items {
		t := &token{
			Site: item.Site,
			Link: item.Link,
		}

		te, err := t.Encode(k)
		if err != nil {
			log.Debugf("Error encoding token: %v", err)
			return nil, err
		}

		log.Debugf("Generated signed token %q", te)
		feed.Items[idx].Link = fmt.Sprintf("%s/%s/%s.torrent", baseURL.String(), te, item.Title)
	}

	return feed, err
}
<|endoftext|>