<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
)

func TestAccDataSourceAWSELB_basic(t *testing.T) {
	// Must be less than 32 characters for ELB name
	rName := fmt.Sprintf("TestAccDataSourceAWSELB-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAWSELBConfigBasic(rName, t.Name()),
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "name", rName),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "cross_zone_load_balancing", "true"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "idle_timeout", "30"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "internal", "true"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "subnets.#", "2"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "security_groups.#", "1"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "tags.%", "1"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "tags.TestName", t.Name()),
					resource.TestCheckResourceAttrSet("data.aws_elb.elb_test", "dns_name"),
					resource.TestCheckResourceAttrSet("data.aws_elb.elb_test", "zone_id"),
				),
			},
		},
	})
}

func testAccDataSourceAWSELBConfigBasic(rName, testName string) string {
	return fmt.Sprintf(`
resource "aws_elb" "elb_test" {
  name            = "%[1]s"
  internal        = true
  security_groups = ["${aws_security_group.elb_test.id}"]
  subnets         = ["${aws_subnet.elb_test.*.id}"]

  idle_timeout = 30

  listener {
    instance_port     = 80
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }

  tags = {
    TestName = "%[2]s"
  }
}

variable "subnets" {
  default = ["10.0.1.0/24", "10.0.2.0/24"]
  type    = "list"
}

data "aws_availability_zones" "available" {}

resource "aws_vpc" "elb_test" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "terraform-testacc-elb-data-source"
  }
}

resource "aws_subnet" "elb_test" {
  count                   = 2
  vpc_id                  = "${aws_vpc.elb_test.id}"
  cidr_block              = "${element(var.subnets, count.index)}"
  map_public_ip_on_launch = true
  availability_zone       = "${element(data.aws_availability_zones.available.names, count.index)}"

  tags = {
    Name = "tf-acc-elb-data-source"
  }
}

resource "aws_security_group" "elb_test" {
  name        = "%[1]s"
  description = "%[2]s"
  vpc_id      = "${aws_vpc.elb_test.id}"

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    self        = true
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    TestName = "%[2]s"
  }
}

data "aws_elb" "elb_test" {
	name = "${aws_elb.elb_test.name}"
}`, rName, testName)
}
<commit_msg>Fixing TestAccDataSourceAWSELB for 0.12<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
)

func TestAccDataSourceAWSELB_basic(t *testing.T) {
	// Must be less than 32 characters for ELB name
	rName := fmt.Sprintf("TestAccDataSourceAWSELB-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			{
				Config: testAccDataSourceAWSELBConfigBasic(rName, t.Name()),
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "name", rName),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "cross_zone_load_balancing", "true"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "idle_timeout", "30"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "internal", "true"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "subnets.#", "2"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "security_groups.#", "1"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "tags.%", "1"),
					resource.TestCheckResourceAttr("data.aws_elb.elb_test", "tags.TestName", t.Name()),
					resource.TestCheckResourceAttrSet("data.aws_elb.elb_test", "dns_name"),
					resource.TestCheckResourceAttrSet("data.aws_elb.elb_test", "zone_id"),
				),
			},
		},
	})
}

func testAccDataSourceAWSELBConfigBasic(rName, testName string) string {
	return fmt.Sprintf(`
resource "aws_elb" "elb_test" {
  name            = "%[1]s"
  internal        = true
  security_groups = ["${aws_security_group.elb_test.id}"]
  subnets         = ["${aws_subnet.elb_test.0.id}", "${aws_subnet.elb_test.1.id}"]

  idle_timeout = 30

  listener {
    instance_port     = 80
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }

  tags = {
    TestName = "%[2]s"
  }
}

variable "subnets" {
  default = ["10.0.1.0/24", "10.0.2.0/24"]
  type    = "list"
}

data "aws_availability_zones" "available" {}

resource "aws_vpc" "elb_test" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "terraform-testacc-elb-data-source"
  }
}

resource "aws_subnet" "elb_test" {
  count                   = 2
  vpc_id                  = "${aws_vpc.elb_test.id}"
  cidr_block              = "${element(var.subnets, count.index)}"
  map_public_ip_on_launch = true
  availability_zone       = "${element(data.aws_availability_zones.available.names, count.index)}"

  tags = {
    Name = "tf-acc-elb-data-source"
  }
}

resource "aws_security_group" "elb_test" {
  name        = "%[1]s"
  description = "%[2]s"
  vpc_id      = "${aws_vpc.elb_test.id}"

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    self        = true
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    TestName = "%[2]s"
  }
}

data "aws_elb" "elb_test" {
	name = "${aws_elb.elb_test.name}"
}`, rName, testName)
}
<|endoftext|>


<commit_before>package aws

import (
	"errors"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/elbv2"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsLbListener() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsLbListenerCreate,
		Read:   resourceAwsLbListenerRead,
		Update: resourceAwsLbListenerUpdate,
		Delete: resourceAwsLbListenerDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"load_balancer_arn": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"port": {
				Type:         schema.TypeInt,
				Required:     true,
				ValidateFunc: validation.IntBetween(1, 65535),
			},

			"protocol": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "HTTP",
				StateFunc: func(v interface{}) string {
					return strings.ToUpper(v.(string))
				},
				ValidateFunc: validation.StringInSlice([]string{
					elbv2.ProtocolEnumHttp,
					elbv2.ProtocolEnumHttps,
					elbv2.ProtocolEnumTcp,
				}, true),
			},

			"ssl_policy": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"certificate_arn": {
				Type:     schema.TypeString,
				Optional: true,
			},

			"default_action": {
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"type": {
							Type:     schema.TypeString,
							Required: true,
							ValidateFunc: validation.StringInSlice([]string{
								elbv2.ActionTypeEnumFixedResponse,
								elbv2.ActionTypeEnumForward,
								elbv2.ActionTypeEnumRedirect,
							}, true),
						},
						"forward": {
							Type: schema.TypeList,
							Optional: true,
							MaxItems: 1,
							Elem: &schema.Resource{
								Schema: map[string]*schema.Schema{
									"target_group_arn": {
										Type:             schema.TypeString,
										Required:         true,
										DiffSuppressFunc: suppressIfDefaultActionTypeNot("forward"),
									},
								},
							},
						},
						"redirect": {
							Type: schema.TypeList,
							Optional: true,
							MaxItems: 1,
							Elem: &schema.Resource{
								Schema: map[string]*schema.Schema{
									"status_code": {
										Type:             schema.TypeString,
										Optional:         true,
										Computed:         true,
										DiffSuppressFunc: suppressIfDefaultActionTypeNot("redirect"),
									},
									"host": {
										Type:             schema.TypeString,
										Optional:         true,
										Computed:         true,
										DiffSuppressFunc: suppressIfDefaultActionTypeNot("redirect"),
									},
									"query": {
										Type:             schema.TypeString,
										Optional:         true,
										Computed:         true,
										DiffSuppressFunc: suppressIfDefaultActionTypeNot("redirect"),
									},
									"path": {
										Type:             schema.TypeString,
										Optional:         true,
										Computed:         true,
										DiffSuppressFunc: suppressIfDefaultActionTypeNot("redirect"),
									},
									"port": {
										Type:             schema.TypeInt,
										Optional:         true,
										Computed:         true,
										DiffSuppressFunc: suppressIfDefaultActionTypeNot("redirect"),
									},
								},
							},
						},
						"fixed_response": {
							Type: schema.TypeList,
							Optional: true,
							MaxItems: 1,
							Elem: &schema.Resource{
								Schema: map[string]*schema.Schema{
									"status_code": {
										Type:             schema.TypeInt,
										Optional:         true,
										Computed:         true,
										DiffSuppressFunc: suppressIfDefaultActionTypeNot("fixed-response"),
									},
									"content_type": {
										Type:             schema.TypeString,
										Optional:         true,
										Computed:         true,
										DiffSuppressFunc: suppressIfDefaultActionTypeNot("fixed-response"),
									},
									"message_body": {
										Type:             schema.TypeString,
										Optional:         true,
										DiffSuppressFunc: suppressIfDefaultActionTypeNot("fixed-response"),
									},
								},
							},
						},
					},
				},
			},
		},
	}
}

func suppressIfDefaultActionTypeNot(t string) schema.SchemaDiffSuppressFunc {
	return func(k, old, new string, d *schema.ResourceData) bool {
		return d.Get("default_action.type").(string) != t
	}
}

func resourceAwsLbListenerCreate(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	lbArn := d.Get("load_balancer_arn").(string)

	params := &elbv2.CreateListenerInput{
		LoadBalancerArn: aws.String(lbArn),
		Port:            aws.Int64(int64(d.Get("port").(int))),
		Protocol:        aws.String(d.Get("protocol").(string)),
	}

	if sslPolicy, ok := d.GetOk("ssl_policy"); ok {
		params.SslPolicy = aws.String(sslPolicy.(string))
	}

	if certificateArn, ok := d.GetOk("certificate_arn"); ok {
		params.Certificates = make([]*elbv2.Certificate, 1)
		params.Certificates[0] = &elbv2.Certificate{
			CertificateArn: aws.String(certificateArn.(string)),
		}
	}

	if defaultActions := d.Get("default_action").([]interface{}); len(defaultActions) == 1 {
		params.DefaultActions = make([]*elbv2.Action, len(defaultActions))

		for i, defaultAction := range defaultActions {
			defaultActionMap := defaultAction.(map[string]interface{})

			params.DefaultActions[i] = &elbv2.Action{
				TargetGroupArn: aws.String(defaultActionMap["target_group_arn"].(string)),
				Type:           aws.String(defaultActionMap["type"].(string)),
			}
		}
	}

	var resp *elbv2.CreateListenerOutput

	err := resource.Retry(5*time.Minute, func() *resource.RetryError {
		var err error
		log.Printf("[DEBUG] Creating LB listener for ARN: %s", d.Get("load_balancer_arn").(string))
		resp, err = elbconn.CreateListener(params)
		if err != nil {
			if isAWSErr(err, elbv2.ErrCodeCertificateNotFoundException, "") {
				return resource.RetryableError(err)
			}
			return resource.NonRetryableError(err)
		}
		return nil
	})

	if err != nil {
		return fmt.Errorf("Error creating LB Listener: %s", err)
	}

	if len(resp.Listeners) == 0 {
		return errors.New("Error creating LB Listener: no listeners returned in response")
	}

	d.SetId(*resp.Listeners[0].ListenerArn)

	return resourceAwsLbListenerRead(d, meta)
}

func resourceAwsLbListenerRead(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	resp, err := elbconn.DescribeListeners(&elbv2.DescribeListenersInput{
		ListenerArns: []*string{aws.String(d.Id())},
	})
	if err != nil {
		if isAWSErr(err, elbv2.ErrCodeListenerNotFoundException, "") {
			log.Printf("[WARN] DescribeListeners - removing %s from state", d.Id())
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error retrieving Listener: %s", err)
	}

	if len(resp.Listeners) != 1 {
		return fmt.Errorf("Error retrieving Listener %q", d.Id())
	}

	listener := resp.Listeners[0]

	d.Set("arn", listener.ListenerArn)
	d.Set("load_balancer_arn", listener.LoadBalancerArn)
	d.Set("port", listener.Port)
	d.Set("protocol", listener.Protocol)
	d.Set("ssl_policy", listener.SslPolicy)

	if listener.Certificates != nil && len(listener.Certificates) == 1 && listener.Certificates[0] != nil {
		d.Set("certificate_arn", listener.Certificates[0].CertificateArn)
	}

	defaultActions := make([]map[string]interface{}, 0)
	if listener.DefaultActions != nil && len(listener.DefaultActions) > 0 {
		for _, defaultAction := range listener.DefaultActions {
			action := map[string]interface{}{
				"target_group_arn": aws.StringValue(defaultAction.TargetGroupArn),
				"type":             aws.StringValue(defaultAction.Type),
			}
			defaultActions = append(defaultActions, action)
		}
	}
	d.Set("default_action", defaultActions)

	return nil
}

func resourceAwsLbListenerUpdate(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	params := &elbv2.ModifyListenerInput{
		ListenerArn: aws.String(d.Id()),
		Port:        aws.Int64(int64(d.Get("port").(int))),
		Protocol:    aws.String(d.Get("protocol").(string)),
	}

	if sslPolicy, ok := d.GetOk("ssl_policy"); ok {
		params.SslPolicy = aws.String(sslPolicy.(string))
	}

	if certificateArn, ok := d.GetOk("certificate_arn"); ok {
		params.Certificates = make([]*elbv2.Certificate, 1)
		params.Certificates[0] = &elbv2.Certificate{
			CertificateArn: aws.String(certificateArn.(string)),
		}
	}

	if defaultActions := d.Get("default_action").([]interface{}); len(defaultActions) == 1 {
		params.DefaultActions = make([]*elbv2.Action, len(defaultActions))

		for i, defaultAction := range defaultActions {
			defaultActionMap := defaultAction.(map[string]interface{})

			params.DefaultActions[i] = &elbv2.Action{
				TargetGroupArn: aws.String(defaultActionMap["target_group_arn"].(string)),
				Type:           aws.String(defaultActionMap["type"].(string)),
			}
		}
	}

	err := resource.Retry(5*time.Minute, func() *resource.RetryError {
		_, err := elbconn.ModifyListener(params)
		if err != nil {
			if isAWSErr(err, elbv2.ErrCodeCertificateNotFoundException, "") {
				return resource.RetryableError(err)
			}
			return resource.NonRetryableError(err)
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("Error modifying LB Listener: %s", err)
	}

	return resourceAwsLbListenerRead(d, meta)
}

func resourceAwsLbListenerDelete(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	_, err := elbconn.DeleteListener(&elbv2.DeleteListenerInput{
		ListenerArn: aws.String(d.Id()),
	})
	if err != nil {
		return fmt.Errorf("Error deleting Listener: %s", err)
	}

	return nil
}
<commit_msg>Update aws_lb_listener resource<commit_after>package aws

import (
	"errors"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/elbv2"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsLbListener() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsLbListenerCreate,
		Read:   resourceAwsLbListenerRead,
		Update: resourceAwsLbListenerUpdate,
		Delete: resourceAwsLbListenerDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"load_balancer_arn": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"port": {
				Type:         schema.TypeInt,
				Required:     true,
				ValidateFunc: validation.IntBetween(1, 65535),
			},

			"protocol": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "HTTP",
				StateFunc: func(v interface{}) string {
					return strings.ToUpper(v.(string))
				},
				ValidateFunc: validation.StringInSlice([]string{
					elbv2.ProtocolEnumHttp,
					elbv2.ProtocolEnumHttps,
					elbv2.ProtocolEnumTcp,
				}, true),
			},

			"ssl_policy": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"certificate_arn": {
				Type:     schema.TypeString,
				Optional: true,
			},

			"default_action": {
				Type:     schema.TypeList,
				Required: true,
				MaxItems: 1,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"type": {
							Type:     schema.TypeString,
							Required: true,
							ValidateFunc: validation.StringInSlice([]string{
								elbv2.ActionTypeEnumFixedResponse,
								elbv2.ActionTypeEnumForward,
								elbv2.ActionTypeEnumRedirect,
							}, true),
						},

						"target_group_arn": {
							Type:             schema.TypeString,
							Optional:         true,
							DiffSuppressFunc: suppressIfDefaultActionTypeNot("forward"),
						},

						"redirect": {
							Type:             schema.TypeList,
							Optional:         true,
							DiffSuppressFunc: suppressIfDefaultActionTypeNot("redirect"),
							MaxItems:         1,
							Elem: &schema.Resource{
								Schema: map[string]*schema.Schema{
									"host": {
										Type:     schema.TypeString,
										Required: true,
									},
									"path": {
										Type:     schema.TypeString,
										Required: true,
									},
									"port": {
										Type:     schema.TypeString,
										Required: true,
									},
									"protocol": {
										Type:     schema.TypeString,
										Required: true,
									},
									"query": {
										Type:     schema.TypeString,
										Optional: true,
										Computed: true,
									},
									"status_code": {
										Type:     schema.TypeString,
										Optional: true,
										Computed: true,
									},
								},
							},
						},

						"fixed_response": {
							Type:             schema.TypeList,
							Optional:         true,
							DiffSuppressFunc: suppressIfDefaultActionTypeNot("fixed-response"),
							MaxItems:         1,
							Elem: &schema.Resource{
								Schema: map[string]*schema.Schema{
									"content_type": {
										Type:     schema.TypeString,
										Optional: true,
										Computed: true,
									},
									"message_body": {
										Type:     schema.TypeString,
										Optional: true,
									},
									"status_code": {
										Type:     schema.TypeString,
										Optional: true,
										Computed: true,
									},
								},
							},
						},
					},
				},
			},
		},
	}
}

func suppressIfDefaultActionTypeNot(t string) schema.SchemaDiffSuppressFunc {
	return func(k, old, new string, d *schema.ResourceData) bool {
		take := 2
		i := strings.IndexFunc(k, func(r rune) bool {
			if r == '.' {
				take -= 1
				return take == 0
			}
			return false
		})
		at := k[:i+1] + "type"
		log.Printf("[DEBUG] at %s %s", k, at)
		return d.Get(at).(string) != t
	}
}

func resourceAwsLbListenerCreate(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	lbArn := d.Get("load_balancer_arn").(string)

	params := &elbv2.CreateListenerInput{
		LoadBalancerArn: aws.String(lbArn),
		Port:            aws.Int64(int64(d.Get("port").(int))),
		Protocol:        aws.String(d.Get("protocol").(string)),
	}

	if sslPolicy, ok := d.GetOk("ssl_policy"); ok {
		params.SslPolicy = aws.String(sslPolicy.(string))
	}

	if certificateArn, ok := d.GetOk("certificate_arn"); ok {
		params.Certificates = make([]*elbv2.Certificate, 1)
		params.Certificates[0] = &elbv2.Certificate{
			CertificateArn: aws.String(certificateArn.(string)),
		}
	}

	if defaultActions := d.Get("default_action").([]interface{}); len(defaultActions) == 1 {
		params.DefaultActions = make([]*elbv2.Action, len(defaultActions))

		for i, defaultAction := range defaultActions {
			defaultActionMap := defaultAction.(map[string]interface{})

			action := &elbv2.Action{
				Type: aws.String(defaultActionMap["type"].(string)),
			}

			switch defaultActionMap["type"].(string) {
			case "forward":
				action.TargetGroupArn = aws.String(defaultActionMap["target_group_arn"].(string))

			case "redirect":
				redirectList := defaultActionMap["redirect"].([]interface{})

				if len(redirectList) == 1 {
					redirectMap := redirectList[0].(map[string]interface{})

					action.RedirectConfig = &elbv2.RedirectActionConfig{
						Host:       aws.String(redirectMap["host"].(string)),
						Path:       aws.String(redirectMap["path"].(string)),
						Port:       aws.String(redirectMap["port"].(string)),
						Protocol:   aws.String(redirectMap["protocol"].(string)),
						Query:      aws.String(redirectMap["query"].(string)),
						StatusCode: aws.String(redirectMap["status_code"].(string)),
					}
				}

			case "fixed-response":
				fixedResponseList := defaultActionMap["fixed_response"].([]interface{})

				if len(fixedResponseList) == 1 {
					fixedResponseMap := fixedResponseList[0].(map[string]interface{})

					action.FixedResponseConfig = &elbv2.FixedResponseActionConfig{
						ContentType: aws.String(fixedResponseMap["content_type"].(string)),
						MessageBody: aws.String(fixedResponseMap["message_body"].(string)),
						StatusCode:  aws.String(fixedResponseMap["status_code"].(string)),
					}
				}
			}

			params.DefaultActions[i] = action
		}
	}

	var resp *elbv2.CreateListenerOutput

	err := resource.Retry(5*time.Minute, func() *resource.RetryError {
		var err error
		log.Printf("[DEBUG] Creating LB listener for ARN: %s", d.Get("load_balancer_arn").(string))
		resp, err = elbconn.CreateListener(params)
		if err != nil {
			if isAWSErr(err, elbv2.ErrCodeCertificateNotFoundException, "") {
				return resource.RetryableError(err)
			}
			return resource.NonRetryableError(err)
		}
		return nil
	})

	if err != nil {
		return fmt.Errorf("Error creating LB Listener: %s", err)
	}

	if len(resp.Listeners) == 0 {
		return errors.New("Error creating LB Listener: no listeners returned in response")
	}

	d.SetId(*resp.Listeners[0].ListenerArn)

	return resourceAwsLbListenerRead(d, meta)
}

func resourceAwsLbListenerRead(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	resp, err := elbconn.DescribeListeners(&elbv2.DescribeListenersInput{
		ListenerArns: []*string{aws.String(d.Id())},
	})
	if err != nil {
		if isAWSErr(err, elbv2.ErrCodeListenerNotFoundException, "") {
			log.Printf("[WARN] DescribeListeners - removing %s from state", d.Id())
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error retrieving Listener: %s", err)
	}

	if len(resp.Listeners) != 1 {
		return fmt.Errorf("Error retrieving Listener %q", d.Id())
	}

	listener := resp.Listeners[0]

	d.Set("arn", listener.ListenerArn)
	d.Set("load_balancer_arn", listener.LoadBalancerArn)
	d.Set("port", listener.Port)
	d.Set("protocol", listener.Protocol)
	d.Set("ssl_policy", listener.SslPolicy)

	if listener.Certificates != nil && len(listener.Certificates) == 1 && listener.Certificates[0] != nil {
		d.Set("certificate_arn", listener.Certificates[0].CertificateArn)
	}

	defaultActions := make([]map[string]interface{}, 0)
	if listener.DefaultActions != nil && len(listener.DefaultActions) > 0 {
		for _, defaultAction := range listener.DefaultActions {
			action := map[string]interface{}{
				"type": aws.StringValue(defaultAction.Type),
			}

			switch aws.StringValue(defaultAction.Type) {
			case "forward":
				action["target_group_arn"] = aws.StringValue(defaultAction.TargetGroupArn)

			case "redirect":
				action["redirect"] = []map[string]interface{}{
					{
						"host":        aws.StringValue(defaultAction.RedirectConfig.Host),
						"path":        aws.StringValue(defaultAction.RedirectConfig.Path),
						"port":        aws.StringValue(defaultAction.RedirectConfig.Port),
						"protocol":    aws.StringValue(defaultAction.RedirectConfig.Protocol),
						"query":       aws.StringValue(defaultAction.RedirectConfig.Query),
						"status_code": aws.StringValue(defaultAction.RedirectConfig.StatusCode),
					},
				}

			case "fixed-response":
				action["fixed_response"] = []map[string]interface{}{
					{
						"content_type": aws.StringValue(defaultAction.FixedResponseConfig.ContentType),
						"message_body": aws.StringValue(defaultAction.FixedResponseConfig.MessageBody),
						"status_code":  aws.StringValue(defaultAction.FixedResponseConfig.StatusCode),
					},
				}
			}

			defaultActions = append(defaultActions, action)
		}
	}
	d.Set("default_action", defaultActions)

	return nil
}

func resourceAwsLbListenerUpdate(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	params := &elbv2.ModifyListenerInput{
		ListenerArn: aws.String(d.Id()),
		Port:        aws.Int64(int64(d.Get("port").(int))),
		Protocol:    aws.String(d.Get("protocol").(string)),
	}

	if sslPolicy, ok := d.GetOk("ssl_policy"); ok {
		params.SslPolicy = aws.String(sslPolicy.(string))
	}

	if certificateArn, ok := d.GetOk("certificate_arn"); ok {
		params.Certificates = make([]*elbv2.Certificate, 1)
		params.Certificates[0] = &elbv2.Certificate{
			CertificateArn: aws.String(certificateArn.(string)),
		}
	}

	if defaultActions := d.Get("default_action").([]interface{}); len(defaultActions) == 1 {
		params.DefaultActions = make([]*elbv2.Action, len(defaultActions))

		for i, defaultAction := range defaultActions {
			defaultActionMap := defaultAction.(map[string]interface{})

			action := &elbv2.Action{
				Type: aws.String(defaultActionMap["type"].(string)),
			}

			switch defaultActionMap["type"].(string) {
			case "forward":
				action.TargetGroupArn = aws.String(defaultActionMap["target_group_arn"].(string))

			case "redirect":
				redirectList := defaultActionMap["redirect"].([]interface{})

				if len(redirectList) == 1 {
					redirectMap := redirectList[0].(map[string]interface{})

					action.RedirectConfig = &elbv2.RedirectActionConfig{
						Host:       aws.String(redirectMap["host"].(string)),
						Path:       aws.String(redirectMap["path"].(string)),
						Port:       aws.String(redirectMap["port"].(string)),
						Protocol:   aws.String(redirectMap["protocol"].(string)),
						Query:      aws.String(redirectMap["query"].(string)),
						StatusCode: aws.String(redirectMap["status_code"].(string)),
					}
				}

			case "fixed-response":
				fixedResponseList := defaultActionMap["fixed_response"].([]interface{})

				if len(fixedResponseList) == 1 {
					fixedResponseMap := fixedResponseList[0].(map[string]interface{})

					action.FixedResponseConfig = &elbv2.FixedResponseActionConfig{
						ContentType: aws.String(fixedResponseMap["content_type"].(string)),
						MessageBody: aws.String(fixedResponseMap["message_body"].(string)),
						StatusCode:  aws.String(fixedResponseMap["status_code"].(string)),
					}
				}
			}

			params.DefaultActions[i] = action
		}
	}

	err := resource.Retry(5*time.Minute, func() *resource.RetryError {
		_, err := elbconn.ModifyListener(params)
		if err != nil {
			if isAWSErr(err, elbv2.ErrCodeCertificateNotFoundException, "") {
				return resource.RetryableError(err)
			}
			return resource.NonRetryableError(err)
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("Error modifying LB Listener: %s", err)
	}

	return resourceAwsLbListenerRead(d, meta)
}

func resourceAwsLbListenerDelete(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	_, err := elbconn.DeleteListener(&elbv2.DeleteListenerInput{
		ListenerArn: aws.String(d.Id()),
	})
	if err != nil {
		return fmt.Errorf("Error deleting Listener: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	cfitsio "github.com/sbinet/go-cfitsio"
)

func main() {
	flag.Parse()
	if flag.NArg() != 2 {
		flag.Usage = func() {
			const msg = `Usage:  go-cfitsio-fitscopy inputfile outputfile

Copy an input file to an output file, optionally filtering
the file in the process.  This seemingly simple program can
apply powerful filters which transform the input file as
it is being copied.  Filters may be used to extract a
subimage from a larger image, select rows from a table,
filter a table with a GTI time extension or a SAO region file,
create or delete columns in a table, create an image by
binning (histogramming) 2 table columns, and convert IRAF
format *.imh or raw binary data files into FITS images.
See the CFITSIO User's Guide for a complete description of
the Extended File Name filtering syntax.

Examples:

go-cfitsio-fitscopy in.fit out.fit                   (simple file copy)
go-cfitsio-fitscopy - -                              (stdin to stdout)
go-cfitsio-fitscopy in.fit[11:50,21:60] out.fit      (copy a subimage)
go-cfitsio-fitscopy iniraf.imh out.fit               (IRAF image to FITS)
go-cfitsio-fitscopy in.dat[i512,512] out.fit         (raw array to FITS)
go-cfitsio-fitscopy in.fit[events][pi>35] out.fit    (copy rows with pi>35)
go-cfitsio-fitscopy in.fit[events][bin X,Y] out.fit  (bin an image)
go-cfitsio-fitscopy in.fit[events][col x=.9*y] out.fit        (new x column)
go-cfitsio-fitscopy in.fit[events][gtifilter()] out.fit       (time filter)
go-cfitsio-fitscopy in.fit[2][regfilter(\"pow.reg\")] out.fit (spatial filter)

Note that it may be necessary to enclose the input file name
in single quote characters on the Unix command line.
`
			fmt.Fprintf(os.Stderr, "%v\n", msg)
		}
		flag.Usage()
		os.Exit(1)
	}

	// open input file
	in, err := cfitsio.Open(flag.Arg(0), cfitsio.ReadOnly)
	if err != nil {
		panic(err)
	}
	defer in.Close()

	// create output file
	out, err := cfitsio.Create(flag.Arg(1))
	if err != nil {
		panic(err)
	}
	defer out.Close()

	// copy every HDU until we get an error
	for i := 1; err == nil; i++ {
		_, err = in.MovAbsHdu(i)
		if err != nil {
			break
		}
		err = in.CopyHdu(&out, 0)
	}
	if err != nil && err != cfitsio.END_OF_FILE {
		panic(err)
	}
}
<commit_msg>fitscopy: docstring<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	cfitsio "github.com/sbinet/go-cfitsio"
)

func main() {
	flag.Parse()
	if flag.NArg() != 2 {
		flag.Usage = func() {
			const msg = `Usage:  go-cfitsio-fitscopy inputfile outputfile

Copy an input file to an output file, optionally filtering
the file in the process.  This seemingly simple program can
apply powerful filters which transform the input file as
it is being copied.  Filters may be used to extract a
subimage from a larger image, select rows from a table,
filter a table with a GTI time extension or a SAO region file,
create or delete columns in a table, create an image by
binning (histogramming) 2 table columns, and convert IRAF
format *.imh or raw binary data files into FITS images.
See the CFITSIO User's Guide for a complete description of
the Extended File Name filtering syntax.

Examples:

go-cfitsio-fitscopy in.fit out.fit                   (simple file copy)
go-cfitsio-fitscopy - -                              (stdin to stdout)
go-cfitsio-fitscopy in.fit[11:50,21:60] out.fit      (copy a subimage)
go-cfitsio-fitscopy iniraf.imh out.fit               (IRAF image to FITS)
go-cfitsio-fitscopy in.dat[i512,512] out.fit         (raw array to FITS)
go-cfitsio-fitscopy in.fit[events][pi>35] out.fit    (copy rows with pi>35)
go-cfitsio-fitscopy in.fit[events][bin X,Y] out.fit  (bin an image)
go-cfitsio-fitscopy in.fit[events][col x=.9*y] out.fit        (new x column)
go-cfitsio-fitscopy in.fit[events][gtifilter()] out.fit       (time filter)
go-cfitsio-fitscopy in.fit[2][regfilter("pow.reg")] out.fit (spatial filter)

Note that it may be necessary to enclose the input file name
in single quote characters on the Unix command line.
`
			fmt.Fprintf(os.Stderr, "%v\n", msg)
		}
		flag.Usage()
		os.Exit(1)
	}

	// open input file
	in, err := cfitsio.Open(flag.Arg(0), cfitsio.ReadOnly)
	if err != nil {
		panic(err)
	}
	defer in.Close()

	// create output file
	out, err := cfitsio.Create(flag.Arg(1))
	if err != nil {
		panic(err)
	}
	defer out.Close()

	// copy every HDU until we get an error
	for i := 1; err == nil; i++ {
		_, err = in.MovAbsHdu(i)
		if err != nil {
			break
		}
		err = in.CopyHdu(&out, 0)
	}
	if err != nil && err != cfitsio.END_OF_FILE {
		panic(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The etcd 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 integration

import (
	"fmt"
	"testing"
	"time"

	"github.com/coreos/etcd/clientv3/concurrency"
	"golang.org/x/net/context"
)

// TestElectionWait tests if followers can correctly wait for elections.
func TestElectionWait(t *testing.T) {
	clus := NewClusterV3(t, &ClusterConfig{Size: 3})
	defer clus.Terminate(t)
	defer dropSessionLease(clus)

	leaders := 3
	followers := 3

	electedc := make(chan string)
	nextc := []chan struct{}{}

	// wait for all elections
	donec := make(chan struct{})
	for i := 0; i < followers; i++ {
		nextc = append(nextc, make(chan struct{}))
		go func(ch chan struct{}) {
			for j := 0; j < leaders; j++ {
				b := concurrency.NewElection(clus.RandClient(), "test-election")
				cctx, cancel := context.WithCancel(context.TODO())
				defer cancel()
				s, ok := <-b.Observe(cctx)
				if !ok {
					t.Fatalf("could not observe election; channel closed")
				}
				electedc <- string(s.Kvs[0].Value)
				// wait for next election round
				<-ch
			}
			donec <- struct{}{}
		}(nextc[i])
	}

	// elect some leaders
	for i := 0; i < leaders; i++ {
		go func() {
			e := concurrency.NewElection(clus.RandClient(), "test-election")
			ev := fmt.Sprintf("electval-%v", time.Now().UnixNano())
			if err := e.Campaign(context.TODO(), ev); err != nil {
				t.Fatalf("failed volunteer (%v)", err)
			}
			// wait for followers to accept leadership
			for j := 0; j < followers; j++ {
				s := <-electedc
				if s != ev {
					t.Errorf("wrong election value got %s, wanted %s", s, ev)
				}
			}
			// let next leader take over
			if err := e.Resign(context.TODO()); err != nil {
				t.Fatalf("failed resign (%v)", err)
			}
			// tell followers to start listening for next leader
			for j := 0; j < followers; j++ {
				nextc[j] <- struct{}{}
			}
		}()
	}

	// wait on followers
	for i := 0; i < followers; i++ {
		<-donec
	}
}

// TestElectionFailover tests that an election will
func TestElectionFailover(t *testing.T) {
	clus := NewClusterV3(t, &ClusterConfig{Size: 3})
	defer clus.Terminate(t)
	defer dropSessionLease(clus)

	cctx, cancel := context.WithCancel(context.TODO())
	defer cancel()

	// first leader (elected)
	e := concurrency.NewElection(clus.clients[0], "test-election")
	if err := e.Campaign(context.TODO(), "foo"); err != nil {
		t.Fatalf("failed volunteer (%v)", err)
	}

	// check first leader
	resp, ok := <-e.Observe(cctx)
	if !ok {
		t.Fatalf("could not wait for first election; channel closed")
	}
	s := string(resp.Kvs[0].Value)
	if s != "foo" {
		t.Fatalf("wrong election result. got %s, wanted foo", s)
	}

	// next leader
	electedc := make(chan struct{})
	go func() {
		ee := concurrency.NewElection(clus.clients[1], "test-election")
		if eer := ee.Campaign(context.TODO(), "bar"); eer != nil {
			t.Fatal(eer)
		}
		electedc <- struct{}{}
	}()

	// invoke leader failover
	session, serr := concurrency.NewSession(clus.clients[0])
	if serr != nil {
		t.Fatal(serr)
	}
	if err := session.Close(); err != nil {
		t.Fatal(err)
	}

	// check new leader
	e = concurrency.NewElection(clus.clients[2], "test-election")
	resp, ok = <-e.Observe(cctx)
	if !ok {
		t.Fatalf("could not wait for second election; channel closed")
	}
	s = string(resp.Kvs[0].Value)
	if s != "bar" {
		t.Fatalf("wrong election result. got %s, wanted bar", s)
	}

	// leader must ack election (otherwise, Campaign may see closed conn)
	<-electedc
}

// TestElectionSessionRelock ensures that campaigning twice on the same election
// with the same lock will Proclaim instead of deadlocking.
func TestElectionSessionRecampaign(t *testing.T) {
	clus := NewClusterV3(t, &ClusterConfig{Size: 1})
	defer clus.Terminate(t)
	cli := clus.RandClient()

	e := concurrency.NewElection(cli, "test-elect")
	if err := e.Campaign(context.TODO(), "abc"); err != nil {
		t.Fatal(err)
	}
	e2 := concurrency.NewElection(cli, "test-elect")
	if err := e2.Campaign(context.TODO(), "def"); err != nil {
		t.Fatal(err)
	}

	ctx, cancel := context.WithCancel(context.TODO())
	defer cancel()
	if resp := <-e.Observe(ctx); len(resp.Kvs) == 0 || string(resp.Kvs[0].Value) != "def" {
		t.Fatalf("expected value=%q, got response %v", "def", resp)
	}
}
<commit_msg>integration: fix TestElectionWait<commit_after>// Copyright 2016 The etcd 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 integration

import (
	"fmt"
	"testing"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/clientv3/concurrency"
	"golang.org/x/net/context"
)

// TestElectionWait tests if followers can correctly wait for elections.
func TestElectionWait(t *testing.T) {
	clus := NewClusterV3(t, &ClusterConfig{Size: 3})
	defer clus.Terminate(t)
	defer dropSessionLease(clus)

	leaders := 3
	followers := 3
	var clients []*clientv3.Client
	newClient := makeMultiNodeClients(t, clus.cluster, &clients)

	electedc := make(chan string)
	nextc := []chan struct{}{}

	// wait for all elections
	donec := make(chan struct{})
	for i := 0; i < followers; i++ {
		nextc = append(nextc, make(chan struct{}))
		go func(ch chan struct{}) {
			for j := 0; j < leaders; j++ {
				b := concurrency.NewElection(newClient(), "test-election")
				cctx, cancel := context.WithCancel(context.TODO())
				defer cancel()
				s, ok := <-b.Observe(cctx)
				if !ok {
					t.Fatalf("could not observe election; channel closed")
				}
				electedc <- string(s.Kvs[0].Value)
				// wait for next election round
				<-ch
			}
			donec <- struct{}{}
		}(nextc[i])
	}

	// elect some leaders
	for i := 0; i < leaders; i++ {
		go func() {
			e := concurrency.NewElection(newClient(), "test-election")
			ev := fmt.Sprintf("electval-%v", time.Now().UnixNano())
			if err := e.Campaign(context.TODO(), ev); err != nil {
				t.Fatalf("failed volunteer (%v)", err)
			}
			// wait for followers to accept leadership
			for j := 0; j < followers; j++ {
				s := <-electedc
				if s != ev {
					t.Errorf("wrong election value got %s, wanted %s", s, ev)
				}
			}
			// let next leader take over
			if err := e.Resign(context.TODO()); err != nil {
				t.Fatalf("failed resign (%v)", err)
			}
			// tell followers to start listening for next leader
			for j := 0; j < followers; j++ {
				nextc[j] <- struct{}{}
			}
		}()
	}

	// wait on followers
	for i := 0; i < followers; i++ {
		<-donec
	}

	closeClients(t, clients)
}

// TestElectionFailover tests that an election will
func TestElectionFailover(t *testing.T) {
	clus := NewClusterV3(t, &ClusterConfig{Size: 3})
	defer clus.Terminate(t)
	defer dropSessionLease(clus)

	cctx, cancel := context.WithCancel(context.TODO())
	defer cancel()

	// first leader (elected)
	e := concurrency.NewElection(clus.clients[0], "test-election")
	if err := e.Campaign(context.TODO(), "foo"); err != nil {
		t.Fatalf("failed volunteer (%v)", err)
	}

	// check first leader
	resp, ok := <-e.Observe(cctx)
	if !ok {
		t.Fatalf("could not wait for first election; channel closed")
	}
	s := string(resp.Kvs[0].Value)
	if s != "foo" {
		t.Fatalf("wrong election result. got %s, wanted foo", s)
	}

	// next leader
	electedc := make(chan struct{})
	go func() {
		ee := concurrency.NewElection(clus.clients[1], "test-election")
		if eer := ee.Campaign(context.TODO(), "bar"); eer != nil {
			t.Fatal(eer)
		}
		electedc <- struct{}{}
	}()

	// invoke leader failover
	session, serr := concurrency.NewSession(clus.clients[0])
	if serr != nil {
		t.Fatal(serr)
	}
	if err := session.Close(); err != nil {
		t.Fatal(err)
	}

	// check new leader
	e = concurrency.NewElection(clus.clients[2], "test-election")
	resp, ok = <-e.Observe(cctx)
	if !ok {
		t.Fatalf("could not wait for second election; channel closed")
	}
	s = string(resp.Kvs[0].Value)
	if s != "bar" {
		t.Fatalf("wrong election result. got %s, wanted bar", s)
	}

	// leader must ack election (otherwise, Campaign may see closed conn)
	<-electedc
}

// TestElectionSessionRelock ensures that campaigning twice on the same election
// with the same lock will Proclaim instead of deadlocking.
func TestElectionSessionRecampaign(t *testing.T) {
	clus := NewClusterV3(t, &ClusterConfig{Size: 1})
	defer clus.Terminate(t)
	cli := clus.RandClient()

	e := concurrency.NewElection(cli, "test-elect")
	if err := e.Campaign(context.TODO(), "abc"); err != nil {
		t.Fatal(err)
	}
	e2 := concurrency.NewElection(cli, "test-elect")
	if err := e2.Campaign(context.TODO(), "def"); err != nil {
		t.Fatal(err)
	}

	ctx, cancel := context.WithCancel(context.TODO())
	defer cancel()
	if resp := <-e.Observe(ctx); len(resp.Kvs) == 0 || string(resp.Kvs[0].Value) != "def" {
		t.Fatalf("expected value=%q, got response %v", "def", resp)
	}
}
<|endoftext|>


<commit_before>package pgproto3

import (
	"bytes"
	"encoding/binary"
	"encoding/json"

	"github.com/jackc/pgx/pgio"
)

type Parse struct {
	Name          string
	Query         string
	ParameterOIDs []uint32
}

func (*Parse) Frontend() {}

func (dst *Parse) Decode(src []byte) error {
	buf := bytes.NewBuffer(src)

	b, err := buf.ReadBytes(0)
	if err != nil {
		return err
	}
	dst.Name = string(b[:len(b)-1])

	b, err = buf.ReadBytes(0)
	if err != nil {
		return err
	}
	dst.Query = string(b[:len(b)-1])

	if buf.Len() < 2 {
		return &invalidMessageFormatErr{messageType: "Parse"}
	}
	parameterOIDCount := int(binary.BigEndian.Uint16(buf.Next(2)))

	for i := 0; i < parameterOIDCount; i++ {
		if buf.Len() < 4 {
			return &invalidMessageFormatErr{messageType: "Parse"}
		}
		dst.ParameterOIDs = append(dst.ParameterOIDs, binary.BigEndian.Uint32(buf.Next(4)))
	}

	return nil
}

func (src *Parse) Encode(dst []byte) []byte {
	dst = append(dst, 'P')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)

	dst = append(dst, src.Name...)
	dst = append(dst, 0)
	dst = append(dst, src.Query...)
	dst = append(dst, 0)

	dst = pgio.AppendUint16(dst, uint16(len(src.ParameterOIDs)))
	for _, oid := range src.ParameterOIDs {
		dst = pgio.AppendUint32(dst, oid)
	}

	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
}

func (src *Parse) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type          string
		Name          string
		Query         string
		ParameterOIDs []uint32
	}{
		Type:          "Parse",
		Name:          src.Name,
		Query:         src.Query,
		ParameterOIDs: src.ParameterOIDs,
	})
}
<commit_msg>Ensure pgproto3.Parse.Decode overwrites itself entirely<commit_after>package pgproto3

import (
	"bytes"
	"encoding/binary"
	"encoding/json"

	"github.com/jackc/pgx/pgio"
)

type Parse struct {
	Name          string
	Query         string
	ParameterOIDs []uint32
}

func (*Parse) Frontend() {}

func (dst *Parse) Decode(src []byte) error {
	*dst = Parse{}

	buf := bytes.NewBuffer(src)

	b, err := buf.ReadBytes(0)
	if err != nil {
		return err
	}
	dst.Name = string(b[:len(b)-1])

	b, err = buf.ReadBytes(0)
	if err != nil {
		return err
	}
	dst.Query = string(b[:len(b)-1])

	if buf.Len() < 2 {
		return &invalidMessageFormatErr{messageType: "Parse"}
	}
	parameterOIDCount := int(binary.BigEndian.Uint16(buf.Next(2)))

	for i := 0; i < parameterOIDCount; i++ {
		if buf.Len() < 4 {
			return &invalidMessageFormatErr{messageType: "Parse"}
		}
		dst.ParameterOIDs = append(dst.ParameterOIDs, binary.BigEndian.Uint32(buf.Next(4)))
	}

	return nil
}

func (src *Parse) Encode(dst []byte) []byte {
	dst = append(dst, 'P')
	sp := len(dst)
	dst = pgio.AppendInt32(dst, -1)

	dst = append(dst, src.Name...)
	dst = append(dst, 0)
	dst = append(dst, src.Query...)
	dst = append(dst, 0)

	dst = pgio.AppendUint16(dst, uint16(len(src.ParameterOIDs)))
	for _, oid := range src.ParameterOIDs {
		dst = pgio.AppendUint32(dst, oid)
	}

	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))

	return dst
}

func (src *Parse) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type          string
		Name          string
		Query         string
		ParameterOIDs []uint32
	}{
		Type:          "Parse",
		Name:          src.Name,
		Query:         src.Query,
		ParameterOIDs: src.ParameterOIDs,
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

type (
	Expr interface {
		Eval(env *Env) Object
		Pos() Position
	}
	Position struct {
		line   int
		column int
	}
	LiteralExpr struct {
		Position
		obj Object
	}
	VectorExpr struct {
		Position
		v []Expr
	}
	MapExpr struct {
		Position
		keys   []Expr
		values []Expr
	}
	SetExpr struct {
		Position
		elements []Expr
	}
	IfExpr struct {
		Position
		cond     Expr
		positive Expr
		negative Expr
	}
	DefExpr struct {
		Position
		name  Symbol
		value Expr
		meta  Expr
	}
	CallExpr struct {
		Position
		callable Expr
		args     []Expr
	}
	RefExpr struct {
		Position
		symbol Symbol
	}
	VarExpr struct {
		Position
		symbol Symbol
	}
	MetaExpr struct {
		Position
		meta *MapExpr
		expr Expr
	}
	ParseError struct {
		obj ReadObject
		msg string
	}
	Callable interface {
		Call(args []Object) Object
	}
	Namespace struct {
		name     Symbol
		mappings map[Symbol]*Var
	}
	Env struct {
		namespaces       map[Symbol]*Namespace
		currentNamespace *Namespace
	}
)

// sym must be not qualified
func (ns *Namespace) intern(sym Symbol) *Var {
	sym.meta = nil
	v, ok := ns.mappings[sym]
	if !ok {
		v = &Var{
			ns:   ns,
			name: sym,
		}
		ns.mappings[sym] = v
	}
	return v
}

func (pos Position) Pos() Position {
	return pos
}

func NewLiteralExpr(obj ReadObject) *LiteralExpr {
	res := LiteralExpr{obj: obj.obj}
	res.line = obj.line
	res.column = obj.column
	return &res
}

func (err ParseError) Error() string {
	return fmt.Sprintf("stdin:%d:%d: Parse error: %s", err.obj.line, err.obj.column, err.msg)
}

func ensureReadObject(obj Object) ReadObject {
	switch obj := obj.(type) {
	case ReadObject:
		return obj
	default:
		return ReadObject{obj: obj}
	}
}

func parseSeq(seq Seq) []Expr {
	res := make([]Expr, 0)
	for !seq.IsEmpty() {
		res = append(res, parse(ensureReadObject(seq.First())))
		seq = seq.Rest()
	}
	return res
}

func parseVector(v *Vector, pos Position) Expr {
	r := make([]Expr, v.count)
	for i := 0; i < v.count; i++ {
		r[i] = parse(ensureReadObject(v.at(i)))
	}
	return &VectorExpr{
		v:        r,
		Position: pos,
	}
}

func parseMap(m *ArrayMap, pos Position) *MapExpr {
	res := &MapExpr{
		keys:     make([]Expr, m.Count()),
		values:   make([]Expr, m.Count()),
		Position: pos,
	}
	for iter, i := m.iter(), 0; iter.HasNext(); i++ {
		p := iter.Next()
		res.keys[i] = parse(ensureReadObject(p.key))
		res.values[i] = parse(ensureReadObject(p.value))
	}
	return res
}

func parseSet(s *Set, pos Position) Expr {
	res := &SetExpr{
		elements: make([]Expr, s.m.Count()),
		Position: pos,
	}
	for iter, i := iter(s.Seq()), 0; iter.HasNext(); i++ {
		res.elements[i] = parse(ensureReadObject(iter.Next()))
	}
	return res
}

func checkForm(obj ReadObject, min int, max int) int {
	list := obj.obj.(*List)
	if list.count < min {
		panic(&ParseError{obj: obj, msg: "Too few arguments to " + list.first.ToString(false)})
	}
	if list.count > max {
		panic(&ParseError{obj: obj, msg: "Too many arguments to " + list.first.ToString(false)})
	}
	return list.count
}

func parseDef(obj ReadObject) *DefExpr {
	count := checkForm(obj, 2, 4)
	seq := obj.obj.(Seq)
	s := ensureReadObject(Second(seq))
	switch v := s.obj.(type) {
	case Symbol:
		res := &DefExpr{
			name:     v,
			value:    nil,
			Position: Position{line: obj.line, column: obj.column},
		}
		if count == 3 {
			res.value = parse(ensureReadObject(Third(seq)))
		} else if count == 4 {
			res.value = parse(ensureReadObject(Forth(seq)))
			docstring := ensureReadObject(Third(seq))
			switch docstring.obj.(type) {
			case String:
				meta := EmptyArrayMap()
				meta.Add(Keyword(":doc"), docstring)
				res.meta = parse(DeriveReadObject(docstring, meta))
			default:
				panic(&ParseError{obj: docstring, msg: "Docstring must be a string"})
			}
		}
		return res
	default:
		panic(&ParseError{obj: s, msg: "First argument to def must be a Symbol"})
	}
}

func parseList(obj ReadObject) Expr {
	seq := obj.obj.(Seq)
	if seq.IsEmpty() {
		return NewLiteralExpr(obj)
	}
	first := ensureReadObject(seq.First())
	switch v := first.obj.(type) {
	case Symbol:
		switch *v.name {
		case "quote":
			// TODO: this probably needs unwrapping from ReadObject to Object
			// for collections
			return NewLiteralExpr(ensureReadObject(Second(seq)))
		case "if":
			checkForm(obj, 3, 4)
			return &IfExpr{
				cond:     parse(ensureReadObject(Second(seq))),
				positive: parse(ensureReadObject(Third(seq))),
				negative: parse(ensureReadObject(Forth(seq))),
				Position: Position{line: obj.line, column: obj.column},
			}
		case "def":
			return parseDef(obj)
		case "var":
			checkForm(obj, 2, 2)
			switch s := (ensureReadObject(Second(seq)).obj).(type) {
			case Symbol:
				return &VarExpr{
					symbol:   s,
					Position: Position{line: obj.line, column: obj.column},
				}
			default:
				panic(&ParseError{obj: obj, msg: "var's argument must be a symbol"})
			}
		}
	}
	return &CallExpr{
		callable: parse(ensureReadObject(seq.First())),
		args:     parseSeq(seq.Rest()),
		Position: Position{line: obj.line, column: obj.column},
	}
}

func parse(obj ReadObject) Expr {
	pos := Position{line: obj.line, column: obj.column}
	var res Expr
	canHaveMeta := false
	switch v := obj.obj.(type) {
	case Int, String, Char, Double, *BigInt, *BigFloat, Bool, Nil, *Ratio, Keyword, Regex:
		res = NewLiteralExpr(obj)
	case *Vector:
		canHaveMeta = true
		res = parseVector(v, pos)
	case *ArrayMap:
		canHaveMeta = true
		res = parseMap(v, pos)
	case *Set:
		canHaveMeta = true
		res = parseSet(v, pos)
	case Seq:
		res = parseList(obj)
	case Symbol:
		res = &RefExpr{
			symbol:   v,
			Position: pos,
		}
	default:
		panic(&ParseError{obj: obj, msg: "Cannot parse form: " + obj.ToString(false)})
	}
	if canHaveMeta {
		meta := obj.obj.(Meta).GetMeta()
		if meta != nil {
			return &MetaExpr{
				meta:     parseMap(meta, pos),
				expr:     res,
				Position: pos,
			}
		}
	}
	return res
}

func TryParse(obj ReadObject) (expr Expr, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
	}()
	return parse(obj), nil
}
<commit_msg>Support metadata in defs<commit_after>package main

import (
	"fmt"
)

type (
	Expr interface {
		Eval(env *Env) Object
		Pos() Position
	}
	Position struct {
		line   int
		column int
	}
	LiteralExpr struct {
		Position
		obj Object
	}
	VectorExpr struct {
		Position
		v []Expr
	}
	MapExpr struct {
		Position
		keys   []Expr
		values []Expr
	}
	SetExpr struct {
		Position
		elements []Expr
	}
	IfExpr struct {
		Position
		cond     Expr
		positive Expr
		negative Expr
	}
	DefExpr struct {
		Position
		name  Symbol
		value Expr
		meta  Expr
	}
	CallExpr struct {
		Position
		callable Expr
		args     []Expr
	}
	RefExpr struct {
		Position
		symbol Symbol
	}
	VarExpr struct {
		Position
		symbol Symbol
	}
	MetaExpr struct {
		Position
		meta *MapExpr
		expr Expr
	}
	ParseError struct {
		obj ReadObject
		msg string
	}
	Callable interface {
		Call(args []Object) Object
	}
	Namespace struct {
		name     Symbol
		mappings map[Symbol]*Var
	}
	Env struct {
		namespaces       map[Symbol]*Namespace
		currentNamespace *Namespace
	}
)

// sym must be not qualified
func (ns *Namespace) intern(sym Symbol) *Var {
	sym.meta = nil
	v, ok := ns.mappings[sym]
	if !ok {
		v = &Var{
			ns:   ns,
			name: sym,
		}
		ns.mappings[sym] = v
	}
	return v
}

func (pos Position) Pos() Position {
	return pos
}

func NewLiteralExpr(obj ReadObject) *LiteralExpr {
	res := LiteralExpr{obj: obj.obj}
	res.line = obj.line
	res.column = obj.column
	return &res
}

func (err ParseError) Error() string {
	return fmt.Sprintf("stdin:%d:%d: Parse error: %s", err.obj.line, err.obj.column, err.msg)
}

func ensureReadObject(obj Object) ReadObject {
	switch obj := obj.(type) {
	case ReadObject:
		return obj
	default:
		return ReadObject{obj: obj}
	}
}

func parseSeq(seq Seq) []Expr {
	res := make([]Expr, 0)
	for !seq.IsEmpty() {
		res = append(res, parse(ensureReadObject(seq.First())))
		seq = seq.Rest()
	}
	return res
}

func parseVector(v *Vector, pos Position) Expr {
	r := make([]Expr, v.count)
	for i := 0; i < v.count; i++ {
		r[i] = parse(ensureReadObject(v.at(i)))
	}
	return &VectorExpr{
		v:        r,
		Position: pos,
	}
}

func parseMap(m *ArrayMap, pos Position) *MapExpr {
	res := &MapExpr{
		keys:     make([]Expr, m.Count()),
		values:   make([]Expr, m.Count()),
		Position: pos,
	}
	for iter, i := m.iter(), 0; iter.HasNext(); i++ {
		p := iter.Next()
		res.keys[i] = parse(ensureReadObject(p.key))
		res.values[i] = parse(ensureReadObject(p.value))
	}
	return res
}

func parseSet(s *Set, pos Position) Expr {
	res := &SetExpr{
		elements: make([]Expr, s.m.Count()),
		Position: pos,
	}
	for iter, i := iter(s.Seq()), 0; iter.HasNext(); i++ {
		res.elements[i] = parse(ensureReadObject(iter.Next()))
	}
	return res
}

func checkForm(obj ReadObject, min int, max int) int {
	list := obj.obj.(*List)
	if list.count < min {
		panic(&ParseError{obj: obj, msg: "Too few arguments to " + list.first.ToString(false)})
	}
	if list.count > max {
		panic(&ParseError{obj: obj, msg: "Too many arguments to " + list.first.ToString(false)})
	}
	return list.count
}

func parseDef(obj ReadObject) *DefExpr {
	count := checkForm(obj, 2, 4)
	seq := obj.obj.(Seq)
	s := ensureReadObject(Second(seq))
	var meta *ArrayMap
	switch v := s.obj.(type) {
	case Symbol:
		res := &DefExpr{
			name:     v,
			value:    nil,
			Position: Position{line: obj.line, column: obj.column},
		}
		meta = v.GetMeta()
		if count == 3 {
			res.value = parse(ensureReadObject(Third(seq)))
		} else if count == 4 {
			res.value = parse(ensureReadObject(Forth(seq)))
			docstring := ensureReadObject(Third(seq))
			switch docstring.obj.(type) {
			case String:
				if meta != nil {
					meta = meta.Assoc(Keyword(":doc"), docstring)
				} else {
					meta = EmptyArrayMap()
					meta.Add(Keyword(":doc"), docstring)
				}
			default:
				panic(&ParseError{obj: docstring, msg: "Docstring must be a string"})
			}
		}
		if meta != nil {
			res.meta = parse(DeriveReadObject(obj, meta))
		}
		return res
	default:
		panic(&ParseError{obj: s, msg: "First argument to def must be a Symbol"})
	}
}

func parseList(obj ReadObject) Expr {
	seq := obj.obj.(Seq)
	if seq.IsEmpty() {
		return NewLiteralExpr(obj)
	}
	first := ensureReadObject(seq.First())
	switch v := first.obj.(type) {
	case Symbol:
		switch *v.name {
		case "quote":
			// TODO: this probably needs unwrapping from ReadObject to Object
			// for collections
			return NewLiteralExpr(ensureReadObject(Second(seq)))
		case "if":
			checkForm(obj, 3, 4)
			return &IfExpr{
				cond:     parse(ensureReadObject(Second(seq))),
				positive: parse(ensureReadObject(Third(seq))),
				negative: parse(ensureReadObject(Forth(seq))),
				Position: Position{line: obj.line, column: obj.column},
			}
		case "def":
			return parseDef(obj)
		case "var":
			checkForm(obj, 2, 2)
			switch s := (ensureReadObject(Second(seq)).obj).(type) {
			case Symbol:
				return &VarExpr{
					symbol:   s,
					Position: Position{line: obj.line, column: obj.column},
				}
			default:
				panic(&ParseError{obj: obj, msg: "var's argument must be a symbol"})
			}
		}
	}
	return &CallExpr{
		callable: parse(ensureReadObject(seq.First())),
		args:     parseSeq(seq.Rest()),
		Position: Position{line: obj.line, column: obj.column},
	}
}

func parse(obj ReadObject) Expr {
	pos := Position{line: obj.line, column: obj.column}
	var res Expr
	canHaveMeta := false
	switch v := obj.obj.(type) {
	case Int, String, Char, Double, *BigInt, *BigFloat, Bool, Nil, *Ratio, Keyword, Regex:
		res = NewLiteralExpr(obj)
	case *Vector:
		canHaveMeta = true
		res = parseVector(v, pos)
	case *ArrayMap:
		canHaveMeta = true
		res = parseMap(v, pos)
	case *Set:
		canHaveMeta = true
		res = parseSet(v, pos)
	case Seq:
		res = parseList(obj)
	case Symbol:
		res = &RefExpr{
			symbol:   v,
			Position: pos,
		}
	default:
		panic(&ParseError{obj: obj, msg: "Cannot parse form: " + obj.ToString(false)})
	}
	if canHaveMeta {
		meta := obj.obj.(Meta).GetMeta()
		if meta != nil {
			return &MetaExpr{
				meta:     parseMap(meta, pos),
				expr:     res,
				Position: pos,
			}
		}
	}
	return res
}

func TryParse(obj ReadObject) (expr Expr, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
	}()
	return parse(obj), nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 Mathieu Turcotte
// Licensed under the MIT license.

package browserchannel

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"sync"
	"time"
)

var (
	ErrClosed = errors.New("channel closed")
)

const (
	maxOutgoingArrays          = 100
	channelReopenTimeoutDelay  = 20 * time.Second
	backChannelExpirationDelay = 1 * time.Minute
	backChannelHeartbeatDelay  = 30 * time.Second
	ackTimeoutDelay            = 1 * time.Minute
)

type channelState int

const (
	channelInit channelState = iota
	channelReady
	channelWriteClosed
	channelClosed
)

// Type of the data transmitted from the server to the client. The values
// stored in the array will be serialized to JSON.
type Array []interface{}

type outgoingArray struct {
	index    int
	elements Array
}

var (
	noopArray = Array{"noop"}
	stopArray = Array{"stop"}
)

func marshalOutgoingArrays(arrays []*outgoingArray) (data []byte, err error) {
	carrays := []interface{}{}
	for _, a := range arrays {
		carrays = append(carrays, []interface{}{a.index, a.elements})
	}
	data, err = json.Marshal(carrays)
	return
}

type Channel struct {
	// The client specific version string.
	Version string
	// The channel session id.
	Sid   SessionId
	state channelState

	backChannel backChannel
	corsInfo    *crossDomainInfo

	maps           *mapQueue
	outgoingArrays []*outgoingArray

	lastArrayId     int
	lastSentArrayId int

	ackTimeout            *time.Timer
	channelTimeout        *time.Timer
	backChannelExpiration *time.Timer
	backChannelHeartbeat  *time.Ticker

	heartbeatStop chan bool
	gcChan        chan<- SessionId
	mapChan       chan *Map

	lock sync.Mutex
}

func newChannel(clientVersion string, sid SessionId, gcChan chan<- SessionId,
	corsInfo *crossDomainInfo) (c *Channel) {
	return &Channel{
		Version:              clientVersion,
		Sid:                  sid,
		state:                channelInit,
		corsInfo:             corsInfo,
		maps:                 newMapQueue(100 /* capacity */),
		outgoingArrays:       []*outgoingArray{},
		backChannelHeartbeat: time.NewTicker(backChannelHeartbeatDelay),
		heartbeatStop:        make(chan bool, 1),
		mapChan:              make(chan *Map, 100),
		gcChan:               gcChan,
	}
}

func (c *Channel) log(format string, v ...interface{}) {
	log.Printf("%s: %s", c.Sid, fmt.Sprintf(format, v...))
}

// Sends an array on the channel. Will return an error if the channel isn't
// ready, i.e. initializing or closed.
func (c *Channel) SendArray(array Array) (err error) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.state != channelReady {
		err = ErrClosed
		return
	}

	c.queueArray(array)
	c.flush()
	return
}

func (c *Channel) queueArray(a Array) {
	c.lastArrayId++
	outgoingArray := &outgoingArray{c.lastArrayId, a}
	c.outgoingArrays = append(c.outgoingArrays, outgoingArray)
}

func (c *Channel) flush() {
	numUnsentArrays := c.lastArrayId - c.lastSentArrayId

	if c.backChannel == nil || numUnsentArrays == 0 {
		return
	}

	next := len(c.outgoingArrays) - numUnsentArrays
	data, _ := marshalOutgoingArrays(c.outgoingArrays[next:])
	c.backChannel.send(data)
	c.lastSentArrayId = c.lastArrayId
	c.resetAckTimeout()

	// If the channel is in the write closed state, i.e. was closed from the
	// server side, then permanently shutdown the channel. The client won't
	// send acknowledgments once the stop signal has been sent.
	if c.state == channelWriteClosed {
		c.terminateInternal()
		return
	}

	// If the number of buffered outgoing arrays is greater than a given
	// threshold, force a back channel change to get acknowledgments so
	// we can free some of them later.
	if !c.backChannel.isReusable() || len(c.outgoingArrays) > 100 {
		c.log("discarding back channel")
		c.clearBackChannel(false /* permanent */)
	}
}

// Reads a map from the client. The call will block until there is a map
// ready or the channel is closed in which case the ok will be false.
func (c *Channel) ReadMap() (m *Map, ok bool) {
	m, ok = <-c.mapChan
	return
}

// Close the channel from the server side. Outgoing arrays will be delivered to
// the client before shutting down the channel permanently. SendArray calls will
// return an error after the channel has been closed.
func (c *Channel) Close() {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.state = channelWriteClosed
	c.queueArray(stopArray)
	c.flush()
	close(c.mapChan)
}

// Close the channel after a query terminate was received from the client.
// Intended to be called from the channel handler.
func (c *Channel) terminate() {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.terminateInternal()
}

func (c *Channel) terminateInternal() {
	c.clearBackChannel(true /* permanent */)
	c.state = channelClosed
	c.gcChan <- c.Sid

	if c.state == channelInit || c.state == channelReady {
		close(c.mapChan)
	}
}

func (c *Channel) getState() []int {
	c.lock.Lock()
	defer c.lock.Unlock()

	outstanding := 0
	if len(c.outgoingArrays) > 0 {
		outstanding = 15
	}

	backChannel := 0
	if c.backChannel != nil {
		backChannel = 1
	}

	return []int{backChannel, c.lastSentArrayId, outstanding}
}

func (c *Channel) receiveMaps(offset int, maps []Map) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if len(maps) == 0 {
		return
	}

	if c.state == channelReady {
		c.log("receive %v", maps)
		c.maps.enqueue(offset, maps)
		c.dequeueMaps()
	} else {
		c.log("drop %v; channel isn't ready", maps)
	}
}

func (c *Channel) dequeueMaps() {
	for {
		if m, ok := c.maps.dequeue(); ok {
			c.mapChan <- &m
		} else {
			break
		}
	}
}

func (c *Channel) acknowledgeArrays(aid int) {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.log("acknowledge %d", aid)

	for len(c.outgoingArrays) > 0 && c.outgoingArrays[0].index <= aid {
		c.outgoingArrays = c.outgoingArrays[1:]
	}

	// Make sure to release the reference to the underlying array by copying
	// the unacknowledged arrays into a new slice.
	out := make([]*outgoingArray, len(c.outgoingArrays))
	copy(out, c.outgoingArrays)
	c.outgoingArrays = out

	c.clearAckTimeout()
}

// Sets or replaces the back channel.
func (c *Channel) setBackChannel(bc backChannel) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.state == channelClosed {
		bc.discard()
		return
	}

	c.log("set back channel [rid:%s,chunked:%t]", bc.getRequestId(), bc.isChunked())

	if c.state == channelInit {
		hostPrefix := getHostPrefix(c.corsInfo)
		c.queueArray(Array{"c", c.Sid.String(), hostPrefix, 8})
		c.state = channelReady
	}

	if c.backChannel != nil {
		c.log("dropping back channel to set new one")
		c.clearBackChannel(false /* permanent */)
	}

	c.backChannel = bc
	c.clearChannelTimeout()
	c.armBackChannelTimeouts()

	// Special care is needed to account for the fact that the old back
	// channel may have died uncleanly. To make sure that all arrays are
	// effectively received by the client, the last sent array id is reset
	// to the last unacknowledged array id.
	if len(c.outgoingArrays) > 0 {
		c.lastSentArrayId = c.outgoingArrays[0].index - 1
	}

	c.flush()
}

// Clear back channel and starts the channel session timeout if the permanent
// argument is false.
func (c *Channel) clearBackChannel(permanent bool) {
	if c.backChannel == nil {
		return
	}

	c.log("clear back channel %s", c.backChannel.getRequestId())

	c.clearAckTimeout()
	c.clearBackChannelTimeouts()

	c.backChannel.discard()
	c.backChannel = nil

	// When the back channel is cleared permanently, this means that the channel
	// is being shut down. The channel timeout shouldn't be arm in this case.
	if !permanent {
		c.armChannelTimeout()
	}
}

func (c *Channel) clearAckTimeout() {
	if c.ackTimeout != nil {
		c.ackTimeout.Stop()
		c.ackTimeout = nil
	}
}

func (c *Channel) resetAckTimeout() {
	c.clearAckTimeout()
	c.ackTimeout = time.AfterFunc(ackTimeoutDelay, func() {
		c.lock.Lock()
		defer c.lock.Unlock()

		c.log("ack timeout")
		c.clearBackChannel(false /* permanent */)
	})
}

func (c *Channel) armBackChannelTimeouts() {
	c.backChannelExpiration = time.AfterFunc(backChannelExpirationDelay, func() {
		c.lock.Lock()
		defer c.lock.Unlock()

		c.log("back channel expired")
		c.clearBackChannel(false /* permanent */)
	})

	go heartbeat(c, c.backChannelHeartbeat.C, c.heartbeatStop)
}

func heartbeat(c *Channel, ticks <-chan time.Time, stops <-chan bool) {
	c.log("start heartbeats")

	for {
		select {
		case <-ticks:
			c.log("heartbeat")
			c.SendArray(noopArray)
		case <-stops:
			c.log("stop heartbeats")
			return
		}
	}
}

func (c *Channel) clearBackChannelTimeouts() {
	c.backChannelExpiration.Stop()
	c.backChannelExpiration = nil
	c.heartbeatStop <- true
}

func (c *Channel) armChannelTimeout() {
	c.channelTimeout = time.AfterFunc(channelReopenTimeoutDelay, func() {
		c.log("channel timeout")
		c.terminate()
	})
}

func (c *Channel) clearChannelTimeout() {
	c.channelTimeout.Stop()
	c.channelTimeout = nil
}
<commit_msg>Remove ack timeout.<commit_after>// Copyright (c) 2013 Mathieu Turcotte
// Licensed under the MIT license.

package browserchannel

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"sync"
	"time"
)

var (
	ErrClosed = errors.New("channel closed")
)

const (
	maxOutgoingArrays          = 100
	channelReopenTimeoutDelay  = 20 * time.Second
	backChannelExpirationDelay = 3 * time.Minute
	backChannelHeartbeatDelay  = 30 * time.Second
)

type channelState int

const (
	channelInit channelState = iota
	channelReady
	channelWriteClosed
	channelClosed
)

// Type of the data transmitted from the server to the client. The values
// stored in the array will be serialized to JSON.
type Array []interface{}

type outgoingArray struct {
	index    int
	elements Array
}

var (
	noopArray = Array{"noop"}
	stopArray = Array{"stop"}
)

func marshalOutgoingArrays(arrays []*outgoingArray) (data []byte, err error) {
	carrays := []interface{}{}
	for _, a := range arrays {
		carrays = append(carrays, []interface{}{a.index, a.elements})
	}
	data, err = json.Marshal(carrays)
	return
}

type Channel struct {
	// The client specific version string.
	Version string
	// The channel session id.
	Sid   SessionId
	state channelState

	backChannel backChannel
	corsInfo    *crossDomainInfo

	maps           *mapQueue
	outgoingArrays []*outgoingArray

	lastArrayId     int
	lastSentArrayId int

	channelTimeout        *time.Timer
	backChannelExpiration *time.Timer
	backChannelHeartbeat  *time.Ticker

	heartbeatStop chan bool
	gcChan        chan<- SessionId
	mapChan       chan *Map

	lock sync.Mutex
}

func newChannel(clientVersion string, sid SessionId, gcChan chan<- SessionId,
	corsInfo *crossDomainInfo) (c *Channel) {
	return &Channel{
		Version:              clientVersion,
		Sid:                  sid,
		state:                channelInit,
		corsInfo:             corsInfo,
		maps:                 newMapQueue(100 /* capacity */),
		outgoingArrays:       []*outgoingArray{},
		backChannelHeartbeat: time.NewTicker(backChannelHeartbeatDelay),
		heartbeatStop:        make(chan bool, 1),
		mapChan:              make(chan *Map, 100),
		gcChan:               gcChan,
	}
}

func (c *Channel) log(format string, v ...interface{}) {
	log.Printf("%s: %s", c.Sid, fmt.Sprintf(format, v...))
}

// Sends an array on the channel. Will return an error if the channel isn't
// ready, i.e. initializing or closed.
func (c *Channel) SendArray(array Array) (err error) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.state != channelReady {
		err = ErrClosed
		return
	}

	c.queueArray(array)
	c.flush()
	return
}

func (c *Channel) queueArray(a Array) {
	c.lastArrayId++
	outgoingArray := &outgoingArray{c.lastArrayId, a}
	c.outgoingArrays = append(c.outgoingArrays, outgoingArray)
}

func (c *Channel) flush() {
	numUnsentArrays := c.lastArrayId - c.lastSentArrayId

	if c.backChannel == nil || numUnsentArrays == 0 {
		return
	}

	next := len(c.outgoingArrays) - numUnsentArrays
	data, _ := marshalOutgoingArrays(c.outgoingArrays[next:])
	c.backChannel.send(data)
	c.lastSentArrayId = c.lastArrayId

	// If the channel is in the write closed state, i.e. was closed from the
	// server side, then permanently shutdown the channel. The client won't
	// send acknowledgments once the stop signal has been sent.
	if c.state == channelWriteClosed {
		c.terminateInternal()
		return
	}

	// If the number of buffered outgoing arrays is greater than a given
	// threshold, force a back channel change to get acknowledgments so
	// we can free some of them later.
	if !c.backChannel.isReusable() || len(c.outgoingArrays) > 100 {
		c.log("discarding back channel")
		c.clearBackChannel(false /* permanent */)
	}
}

// Reads a map from the client. The call will block until there is a map
// ready or the channel is closed in which case the ok will be false.
func (c *Channel) ReadMap() (m *Map, ok bool) {
	m, ok = <-c.mapChan
	return
}

// Close the channel from the server side. Outgoing arrays will be delivered to
// the client before shutting down the channel permanently. SendArray calls will
// return an error after the channel has been closed.
func (c *Channel) Close() {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.state = channelWriteClosed
	c.queueArray(stopArray)
	c.flush()
	close(c.mapChan)
}

// Close the channel after a query terminate was received from the client.
// Intended to be called from the channel handler.
func (c *Channel) terminate() {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.terminateInternal()
}

func (c *Channel) terminateInternal() {
	c.clearBackChannel(true /* permanent */)
	c.state = channelClosed
	c.gcChan <- c.Sid

	if c.state == channelInit || c.state == channelReady {
		close(c.mapChan)
	}
}

func (c *Channel) getState() []int {
	c.lock.Lock()
	defer c.lock.Unlock()

	outstanding := 0
	if len(c.outgoingArrays) > 0 {
		outstanding = 15
	}

	backChannel := 0
	if c.backChannel != nil {
		backChannel = 1
	}

	return []int{backChannel, c.lastSentArrayId, outstanding}
}

func (c *Channel) receiveMaps(offset int, maps []Map) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if len(maps) == 0 {
		return
	}

	if c.state == channelReady {
		c.log("receive %v", maps)
		c.maps.enqueue(offset, maps)
		c.dequeueMaps()
	} else {
		c.log("drop %v; channel isn't ready", maps)
	}
}

func (c *Channel) dequeueMaps() {
	for {
		if m, ok := c.maps.dequeue(); ok {
			c.mapChan <- &m
		} else {
			break
		}
	}
}

func (c *Channel) acknowledgeArrays(aid int) {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.log("acknowledge %d", aid)

	for len(c.outgoingArrays) > 0 && c.outgoingArrays[0].index <= aid {
		c.outgoingArrays = c.outgoingArrays[1:]
	}

	// Make sure to release the reference to the underlying array by copying
	// the unacknowledged arrays into a new slice.
	out := make([]*outgoingArray, len(c.outgoingArrays))
	copy(out, c.outgoingArrays)
	c.outgoingArrays = out
}

// Sets or replaces the back channel.
func (c *Channel) setBackChannel(bc backChannel) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.state == channelClosed {
		bc.discard()
		return
	}

	c.log("set back channel [rid:%s,chunked:%t]", bc.getRequestId(), bc.isChunked())

	if c.state == channelInit {
		hostPrefix := getHostPrefix(c.corsInfo)
		c.queueArray(Array{"c", c.Sid.String(), hostPrefix, 8})
		c.state = channelReady
	}

	if c.backChannel != nil {
		c.log("dropping back channel to set new one")
		c.clearBackChannel(false /* permanent */)
	}

	c.backChannel = bc
	c.clearChannelTimeout()
	c.armBackChannelTimeouts()

	// Special care is needed to account for the fact that the old back
	// channel may have died uncleanly. To make sure that all arrays are
	// effectively received by the client, the last sent array id is reset
	// to the last unacknowledged array id.
	if len(c.outgoingArrays) > 0 {
		c.lastSentArrayId = c.outgoingArrays[0].index - 1
	}

	c.flush()
}

// Clear back channel and starts the channel session timeout if the permanent
// argument is false.
func (c *Channel) clearBackChannel(permanent bool) {
	if c.backChannel == nil {
		return
	}

	c.log("clear back channel %s", c.backChannel.getRequestId())

	c.clearBackChannelTimeouts()

	c.backChannel.discard()
	c.backChannel = nil

	// When the back channel is cleared permanently, this means that the channel
	// is being shut down. The channel timeout shouldn't be arm in this case.
	if !permanent {
		c.armChannelTimeout()
	}
}

func (c *Channel) armBackChannelTimeouts() {
	c.backChannelExpiration = time.AfterFunc(backChannelExpirationDelay, func() {
		c.lock.Lock()
		defer c.lock.Unlock()

		c.log("back channel expired")
		c.clearBackChannel(false /* permanent */)
	})

	go heartbeat(c, c.backChannelHeartbeat.C, c.heartbeatStop)
}

func heartbeat(c *Channel, ticks <-chan time.Time, stops <-chan bool) {
	c.log("start heartbeats")

	for {
		select {
		case <-ticks:
			c.log("heartbeat")
			c.SendArray(noopArray)
		case <-stops:
			c.log("stop heartbeats")
			return
		}
	}
}

func (c *Channel) clearBackChannelTimeouts() {
	c.backChannelExpiration.Stop()
	c.backChannelExpiration = nil
	c.heartbeatStop <- true
}

func (c *Channel) armChannelTimeout() {
	c.channelTimeout = time.AfterFunc(channelReopenTimeoutDelay, func() {
		c.log("channel timeout")
		c.terminate()
	})
}

func (c *Channel) clearChannelTimeout() {
	c.channelTimeout.Stop()
	c.channelTimeout = nil
}
<|endoftext|>