<commit_before>package dockertest

import (
	"fmt"

	"github.com/docker/go-connections/nat"
	"github.com/pkg/errors"
)

// Protocol is a string representing a protocol such as TCP or UDP.
type Protocol string

const (
	// RandomPort may be passed to a function to indicate
	// that a random port should be chosen.
	RandomPort uint16 = 0

	// ProtocolTCP represents a tcp Protocol.
	ProtocolTCP Protocol = "tcp"

	// ProtocolUDP represents a udp Protocol.
	ProtocolUDP Protocol = "udp"
)

// Port represents a port spec that may be used by the *Ports struct
// to expose a port on a container.
type Port struct {
	// Private is the port exposed on the inside of the container
	// that you wish to map.
	Private uint16

	// Public is the publicly facing port that you wish to expose
	// the Private port on. Note, this may be `RandomPort` if you
	// wish to expose a random port instead of a specific port.
	Public uint16

	// Address is the IP address to expose the port mapping
	// on. By default, 0.0.0.0 will be used.
	Address string

	// Protocol is the network protocol to expose.
	Protocol Protocol
}

// Port converts the struct into a nat.Port
func (s *Port) Port() (nat.Port, error) {
	if s.Protocol == "" {
		return nat.Port(0), errors.New("Protocol not specified")
	}
	return nat.NewPort(
		string(s.Protocol), fmt.Sprintf("%d", s.Private))
}

// Binding converts the struct in a a nat.PortBinding. If no address
// has been given 0.0.0.0 will be used for the host ip.
func (s *Port) Binding() nat.PortBinding {
	address := s.Address
	if address == "" {
		address = "0.0.0.0"
	}
	return nat.PortBinding{
		HostIP:   address,
		HostPort: fmt.Sprintf("%d", s.Public),
	}
}

// Ports is when to convey port exposures to RunContainer()
type Ports struct {
	// Specs is a map of internal to external ports. The external
	// port may be the same as the internal port or it may be the
	// constant `RandomPort` if you wish for Docker to chose a port
	// for you.
	Specs []*Port
}

// Bindings will take the port specs and return port bindings that can
// be used in by the container.HostConfig.PortBindings field.
func (p *Ports) Bindings() (nat.PortMap, error) {
	ports := nat.PortMap{}

	for _, spec := range p.Specs {
		port, err := spec.Port()
		if err != nil {
			return nil, err
		}

		_, exists := ports[port]
		if !exists {
			ports[port] = []nat.PortBinding{}
		}

		ports[port] = append(ports[port], spec.Binding())
	}

	return ports, nil
}

// Add is a shortcut function to add a new port spec.
func (p *Ports) Add(port *Port) {
	p.Specs = append(p.Specs, port)
}

// NewPorts will produces a new *Ports struct that's ready to be
// modified.
func NewPorts() *Ports {
	return &Ports{Specs: []*Port{}}
}
<commit_msg>adding json fields to Port<commit_after>package dockertest

import (
	"fmt"

	"github.com/docker/go-connections/nat"
	"github.com/pkg/errors"
)

// Protocol is a string representing a protocol such as TCP or UDP.
type Protocol string

const (
	// RandomPort may be passed to a function to indicate
	// that a random port should be chosen.
	RandomPort uint16 = 0

	// ProtocolTCP represents a tcp Protocol.
	ProtocolTCP Protocol = "tcp"

	// ProtocolUDP represents a udp Protocol.
	ProtocolUDP Protocol = "udp"
)

// Port represents a port spec that may be used by the *Ports struct
// to expose a port on a container.
type Port struct {
	// Private is the port exposed on the inside of the container
	// that you wish to map.
	Private uint16

	// Public is the publicly facing port that you wish to expose
	// the Private port on. Note, this may be `RandomPort` if you
	// wish to expose a random port instead of a specific port.
	Public uint16 `json:"port"`

	// Address is the IP address to expose the port mapping
	// on. By default, 0.0.0.0 will be used.
	Address string `json:"address"`

	// Protocol is the network protocol to expose.
	Protocol Protocol `json:"protocol"`
}

// Port converts the struct into a nat.Port
func (s *Port) Port() (nat.Port, error) {
	if s.Protocol == "" {
		return nat.Port(0), errors.New("Protocol not specified")
	}
	return nat.NewPort(
		string(s.Protocol), fmt.Sprintf("%d", s.Private))
}

// Binding converts the struct in a a nat.PortBinding. If no address
// has been given 0.0.0.0 will be used for the host ip.
func (s *Port) Binding() nat.PortBinding {
	address := s.Address
	if address == "" {
		address = "0.0.0.0"
	}
	return nat.PortBinding{
		HostIP:   address,
		HostPort: fmt.Sprintf("%d", s.Public),
	}
}

// Ports is when to convey port exposures to RunContainer()
type Ports struct {
	// Specs is a map of internal to external ports. The external
	// port may be the same as the internal port or it may be the
	// constant `RandomPort` if you wish for Docker to chose a port
	// for you.
	Specs []*Port
}

// Bindings will take the port specs and return port bindings that can
// be used in by the container.HostConfig.PortBindings field.
func (p *Ports) Bindings() (nat.PortMap, error) {
	ports := nat.PortMap{}

	for _, spec := range p.Specs {
		port, err := spec.Port()
		if err != nil {
			return nil, err
		}

		_, exists := ports[port]
		if !exists {
			ports[port] = []nat.PortBinding{}
		}

		ports[port] = append(ports[port], spec.Binding())
	}

	return ports, nil
}

// Add is a shortcut function to add a new port spec.
func (p *Ports) Add(port *Port) {
	p.Specs = append(p.Specs, port)
}

// NewPorts will produces a new *Ports struct that's ready to be
// modified.
func NewPorts() *Ports {
	return &Ports{Specs: []*Port{}}
}
<|endoftext|>


<commit_before>package main

/**
Even Fibonacci numbers
Problem 2

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
**/

import (
	"fmt"
)
const (
	LIMIT = 4000000
)

func main() {
	sum := 0
	c := fib_generator()
	for {
		fib := <- c
		if 0 == fib % 2 {
			sum += fib
		} else if fib >= LIMIT {
			break
		}
	}

	fmt.Printf("the sum of the even-valued terms is %d\n", sum)
}

func fib_generator() chan int {
  c := make(chan int)

  go func() {
    for i, j := 0, 1; j <= LIMIT; i, j = i+j,i {
        c <- i
    }

    close(c)
  }()

  return c
}<commit_msg>deferring close<commit_after>package main

/**
Even Fibonacci numbers
Problem 2

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
**/

import (
	"fmt"
)
const (
	LIMIT = 4000000
)

func main() {
	sum := 0
	c := fib_generator()
	for {
		fib := <- c
		if 0 == fib % 2 {
			sum += fib
		} else if fib >= LIMIT {
			break
		}
	}

	fmt.Printf("the sum of the even-valued terms is %d\n", sum)
}

func fib_generator() chan int {
  c := make(chan int)

  go func() {
    for i, j := 0, 1; j <= LIMIT; i, j = i+j,i {
        c <- i
    }
    defer close(c)
  }()

  return c
}<|endoftext|>


<commit_before>//
// Copyright (c) 2017 Intel Corporation
//
// 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 main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/url"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	yaml "gopkg.in/yaml.v2"
)

// Constants for the Guest image used by ciao-down

const (
	guestDownloadURL       = "https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img"
	guestImageFriendlyName = "Ubuntu 16.04"
	defaultHostname        = "singlevm"
)

type portMapping struct {
	Host  int `yaml:"host"`
	Guest int `yaml:"guest"`
}

func (p portMapping) String() string {
	return fmt.Sprintf("%d-%d", p.Host, p.Guest)
}

type mount struct {
	Tag           string `yaml:"tag"`
	SecurityModel string `yaml:"security_model"`
	Path          string `yaml:"path"`
}

func (m mount) String() string {
	return fmt.Sprintf("%s,%s,%s", m.Tag, m.SecurityModel, m.Path)
}

type instance struct {
	MemGiB       int           `yaml:"mem_gib"`
	CPUs         int           `yaml:"cpus"`
	PortMappings []portMapping `yaml:"ports"`
	Mounts       []mount       `yaml:"mounts"`
}

type instanceSpec struct {
	BaseImageURL  string `yaml:"base_image_url"`
	BaseImageName string `yaml:"base_image_name"`
	Hostname      string `yaml:"hostname"`
}

func newDefaultInstance(ws *workspace, vmType string) *instance {
	var in instance

	in.Mounts = []mount{
		{
			Tag:           "hostgo",
			SecurityModel: "passthrough",
			Path:          ws.GoPath,
		},
	}

	in.PortMappings = []portMapping{
		{
			Host:  10022,
			Guest: 22,
		},
	}

	if vmType == CIAO {
		in.PortMappings = append(in.PortMappings, portMapping{
			Host:  3000,
			Guest: 3000,
		})
	}

	return &in
}

func newInstanceFromFile(ws *workspace) (*instance, error) {
	in := &instance{}

	err := in.load(ws)
	if err == nil {
		return in, nil
	}

	// Check for legacy state files.

	vmType := CIAO
	data, err := ioutil.ReadFile(path.Join(ws.instanceDir, "vmtype.txt"))
	if err == nil {
		vmType = string(data)
		if vmType != CIAO && vmType != CLEARCONTAINERS {
			err := fmt.Errorf("Unsupported vmType %s. Should be one of "+CIAO+"|"+CLEARCONTAINERS, vmType)
			return nil, err
		}
	}

	uiPath := ""
	data, err = ioutil.ReadFile(path.Join(ws.instanceDir, "ui_path.txt"))
	if err == nil {
		uiPath = string(data)
	}

	in = newDefaultInstance(ws, vmType)

	if uiPath != "" {
		in.Mounts = append(in.Mounts, mount{
			Tag:           "hostui",
			SecurityModel: "mapped",
			Path:          filepath.Clean(uiPath),
		})
	}

	return in, nil
}

func (in *instance) unmarshall(data []byte) error {
	err := yaml.Unmarshal(data, in)
	if err != nil {
		return fmt.Errorf("Unable to unmarshall instance state : %v", err)
	}

	for i := range in.Mounts {
		if err := checkDirectory(in.Mounts[i].Path); err != nil {
			return fmt.Errorf("Bad mount %s specified: %v",
				in.Mounts[i].Path, err)
		}
	}

	var memDef, cpuDef int
	if in.MemGiB == 0 || in.CPUs == 0 {
		memDef, cpuDef = getMemAndCpus()
		if in.MemGiB == 0 {
			in.MemGiB = memDef
		}
		if in.CPUs == 0 {
			in.CPUs = cpuDef
		}
	}

	var i int
	for i = 0; i < len(in.PortMappings); i++ {
		if in.PortMappings[i].Guest == 22 {
			break
		}
	}
	if i == len(in.PortMappings) {
		in.PortMappings = append(in.PortMappings,
			portMapping{
				Host:  10022,
				Guest: 22,
			})
	}

	return nil
}

func (in *instance) unmarshallWithTemplate(ws *workspace, data string) error {
	tmpl, err := template.New("instance-data").Parse(string(data))
	if err != nil {
		return fmt.Errorf("Unable to parse instance data template: %v", err)
	}
	var buf bytes.Buffer
	err = tmpl.Execute(&buf, ws)
	if err != nil {
		return fmt.Errorf("Unable to execute instance data template: %v", err)
	}
	return in.unmarshall(buf.Bytes())
}

func (in *instance) load(ws *workspace) error {
	data, err := ioutil.ReadFile(path.Join(ws.instanceDir, "state.yaml"))
	if err != nil {
		return fmt.Errorf("Unable to read instance state : %v", err)
	}

	return in.unmarshall(data)
}

func (in *instance) save(ws *workspace) error {
	data, err := yaml.Marshal(in)
	if err != nil {
		return fmt.Errorf("Unable to marshall instance state : %v", err)
	}
	err = ioutil.WriteFile(path.Join(ws.instanceDir, "state.yaml"),
		data, 0600)
	if err != nil {
		return fmt.Errorf("Unable to write instance state : %v", err)
	}
	return nil
}

func (in *instance) mergeMounts(m mounts) {
	mountCount := len(in.Mounts)
	for _, mount := range m {
		var i int
		for i = 0; i < mountCount; i++ {
			if mount.Tag == in.Mounts[i].Tag {
				break
			}
		}

		if i == mountCount {
			in.Mounts = append(in.Mounts, mount)
		} else {
			in.Mounts[i] = mount
		}
	}
}

func (in *instance) mergePorts(p ports) {
	portCount := len(in.PortMappings)
	for _, port := range p {
		var i int
		for i = 0; i < portCount; i++ {
			if port.Guest == in.PortMappings[i].Guest {
				break
			}
		}

		if i == portCount {
			in.PortMappings = append(in.PortMappings, port)
		} else {
			in.PortMappings[i] = port
		}
	}
}

func (in *instance) sshPort() (int, error) {
	for _, p := range in.PortMappings {
		if p.Guest == 22 {
			return p.Host, nil
		}
	}
	return 0, fmt.Errorf("No SSH port configured")
}

func (ins *instanceSpec) unmarshall(data []byte) error {
	err := yaml.Unmarshal(data, ins)
	if err != nil {
		return fmt.Errorf("Unable to unmarshall instance specification : %v", err)
	}

	if ins.BaseImageURL == "" {
		ins.BaseImageURL = guestDownloadURL
		ins.BaseImageName = guestImageFriendlyName
	} else {
		url, err := url.Parse(ins.BaseImageURL)
		if err != nil {
			return fmt.Errorf("Unable to parse url %s : %v",
				ins.BaseImageURL, err)
		}
		if ins.BaseImageName == "" {
			lastSlash := strings.LastIndex(url.Path, "/")
			if lastSlash == -1 {
				ins.BaseImageName = url.Path
			} else {
				ins.BaseImageName = url.Path[lastSlash+1:]
			}
		}
	}

	if ins.Hostname == "" {
		ins.Hostname = defaultHostname
	}
	return nil
}

func (ins *instanceSpec) unmarshallWithTemplate(ws *workspace, data string) error {
	tmpl, err := template.New("instance-spec").Parse(string(data))
	if err != nil {
		return fmt.Errorf("Unable to parse instance data template: %v", err)
	}
	var buf bytes.Buffer
	err = tmpl.Execute(&buf, ws)
	if err != nil {
		return fmt.Errorf("Unable to execute instance data template: %v", err)
	}
	return ins.unmarshall(buf.Bytes())
}
<commit_msg>ciao-down: rename newDefaultInstance to newLegacyInstance<commit_after>//
// Copyright (c) 2017 Intel Corporation
//
// 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 main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/url"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	yaml "gopkg.in/yaml.v2"
)

// Constants for the Guest image used by ciao-down

const (
	guestDownloadURL       = "https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img"
	guestImageFriendlyName = "Ubuntu 16.04"
	defaultHostname        = "singlevm"
)

type portMapping struct {
	Host  int `yaml:"host"`
	Guest int `yaml:"guest"`
}

func (p portMapping) String() string {
	return fmt.Sprintf("%d-%d", p.Host, p.Guest)
}

type mount struct {
	Tag           string `yaml:"tag"`
	SecurityModel string `yaml:"security_model"`
	Path          string `yaml:"path"`
}

func (m mount) String() string {
	return fmt.Sprintf("%s,%s,%s", m.Tag, m.SecurityModel, m.Path)
}

type instance struct {
	MemGiB       int           `yaml:"mem_gib"`
	CPUs         int           `yaml:"cpus"`
	PortMappings []portMapping `yaml:"ports"`
	Mounts       []mount       `yaml:"mounts"`
}

type instanceSpec struct {
	BaseImageURL  string `yaml:"base_image_url"`
	BaseImageName string `yaml:"base_image_name"`
	Hostname      string `yaml:"hostname"`
}

// This function creates a default instanceData object for legacy ciao-down
// ciao VMs.  These old VMs did not store information about mounts and
// mapped ports as this information was hard-coded into ciao-down itself.
// Consequently, when migrating one of these old VMs we need to fill in
// the missing information.
func newLegacyInstance(ws *workspace, vmType string) *instance {
	var in instance

	in.Mounts = []mount{
		{
			Tag:           "hostgo",
			SecurityModel: "passthrough",
			Path:          ws.GoPath,
		},
	}

	in.PortMappings = []portMapping{
		{
			Host:  10022,
			Guest: 22,
		},
	}

	if vmType == CIAO {
		in.PortMappings = append(in.PortMappings, portMapping{
			Host:  3000,
			Guest: 3000,
		})
	}

	return &in
}

func newInstanceFromFile(ws *workspace) (*instance, error) {
	in := &instance{}

	err := in.load(ws)
	if err == nil {
		return in, nil
	}

	// Check for legacy state files.

	vmType := CIAO
	data, err := ioutil.ReadFile(path.Join(ws.instanceDir, "vmtype.txt"))
	if err == nil {
		vmType = string(data)
		if vmType != CIAO && vmType != CLEARCONTAINERS {
			err := fmt.Errorf("Unsupported vmType %s. Should be one of "+CIAO+"|"+CLEARCONTAINERS, vmType)
			return nil, err
		}
	}

	uiPath := ""
	data, err = ioutil.ReadFile(path.Join(ws.instanceDir, "ui_path.txt"))
	if err == nil {
		uiPath = string(data)
	}

	in = newLegacyInstance(ws, vmType)

	if uiPath != "" {
		in.Mounts = append(in.Mounts, mount{
			Tag:           "hostui",
			SecurityModel: "mapped",
			Path:          filepath.Clean(uiPath),
		})
	}

	return in, nil
}

func (in *instance) unmarshall(data []byte) error {
	err := yaml.Unmarshal(data, in)
	if err != nil {
		return fmt.Errorf("Unable to unmarshall instance state : %v", err)
	}

	for i := range in.Mounts {
		if err := checkDirectory(in.Mounts[i].Path); err != nil {
			return fmt.Errorf("Bad mount %s specified: %v",
				in.Mounts[i].Path, err)
		}
	}

	var memDef, cpuDef int
	if in.MemGiB == 0 || in.CPUs == 0 {
		memDef, cpuDef = getMemAndCpus()
		if in.MemGiB == 0 {
			in.MemGiB = memDef
		}
		if in.CPUs == 0 {
			in.CPUs = cpuDef
		}
	}

	var i int
	for i = 0; i < len(in.PortMappings); i++ {
		if in.PortMappings[i].Guest == 22 {
			break
		}
	}
	if i == len(in.PortMappings) {
		in.PortMappings = append(in.PortMappings,
			portMapping{
				Host:  10022,
				Guest: 22,
			})
	}

	return nil
}

func (in *instance) unmarshallWithTemplate(ws *workspace, data string) error {
	tmpl, err := template.New("instance-data").Parse(string(data))
	if err != nil {
		return fmt.Errorf("Unable to parse instance data template: %v", err)
	}
	var buf bytes.Buffer
	err = tmpl.Execute(&buf, ws)
	if err != nil {
		return fmt.Errorf("Unable to execute instance data template: %v", err)
	}
	return in.unmarshall(buf.Bytes())
}

func (in *instance) load(ws *workspace) error {
	data, err := ioutil.ReadFile(path.Join(ws.instanceDir, "state.yaml"))
	if err != nil {
		return fmt.Errorf("Unable to read instance state : %v", err)
	}

	return in.unmarshall(data)
}

func (in *instance) save(ws *workspace) error {
	data, err := yaml.Marshal(in)
	if err != nil {
		return fmt.Errorf("Unable to marshall instance state : %v", err)
	}
	err = ioutil.WriteFile(path.Join(ws.instanceDir, "state.yaml"),
		data, 0600)
	if err != nil {
		return fmt.Errorf("Unable to write instance state : %v", err)
	}
	return nil
}

func (in *instance) mergeMounts(m mounts) {
	mountCount := len(in.Mounts)
	for _, mount := range m {
		var i int
		for i = 0; i < mountCount; i++ {
			if mount.Tag == in.Mounts[i].Tag {
				break
			}
		}

		if i == mountCount {
			in.Mounts = append(in.Mounts, mount)
		} else {
			in.Mounts[i] = mount
		}
	}
}

func (in *instance) mergePorts(p ports) {
	portCount := len(in.PortMappings)
	for _, port := range p {
		var i int
		for i = 0; i < portCount; i++ {
			if port.Guest == in.PortMappings[i].Guest {
				break
			}
		}

		if i == portCount {
			in.PortMappings = append(in.PortMappings, port)
		} else {
			in.PortMappings[i] = port
		}
	}
}

func (in *instance) sshPort() (int, error) {
	for _, p := range in.PortMappings {
		if p.Guest == 22 {
			return p.Host, nil
		}
	}
	return 0, fmt.Errorf("No SSH port configured")
}

func (ins *instanceSpec) unmarshall(data []byte) error {
	err := yaml.Unmarshal(data, ins)
	if err != nil {
		return fmt.Errorf("Unable to unmarshall instance specification : %v", err)
	}

	if ins.BaseImageURL == "" {
		ins.BaseImageURL = guestDownloadURL
		ins.BaseImageName = guestImageFriendlyName
	} else {
		url, err := url.Parse(ins.BaseImageURL)
		if err != nil {
			return fmt.Errorf("Unable to parse url %s : %v",
				ins.BaseImageURL, err)
		}
		if ins.BaseImageName == "" {
			lastSlash := strings.LastIndex(url.Path, "/")
			if lastSlash == -1 {
				ins.BaseImageName = url.Path
			} else {
				ins.BaseImageName = url.Path[lastSlash+1:]
			}
		}
	}

	if ins.Hostname == "" {
		ins.Hostname = defaultHostname
	}
	return nil
}

func (ins *instanceSpec) unmarshallWithTemplate(ws *workspace, data string) error {
	tmpl, err := template.New("instance-spec").Parse(string(data))
	if err != nil {
		return fmt.Errorf("Unable to parse instance data template: %v", err)
	}
	var buf bytes.Buffer
	err = tmpl.Execute(&buf, ws)
	if err != nil {
		return fmt.Errorf("Unable to execute instance data template: %v", err)
	}
	return ins.unmarshall(buf.Bytes())
}
<|endoftext|>


<commit_before>package quictun

import (
	"io"
	"log"
	"net"
	"sync/atomic"
	"time"
)

type closeWriter interface {
	CloseWrite() error
}

var count int64

func Proxy(conn, targetConn net.Conn) {
	defer conn.Close()
	defer targetConn.Close()
	defer atomic.AddInt64(&count, -1)

	atomic.AddInt64(&count, 1)

	log.Printf("[%d] link start %v <-> %v",
		atomic.LoadInt64(&count), conn.RemoteAddr(), targetConn.RemoteAddr())

	copyAndWait := func(dst, src net.Conn, c chan int64) {
		buf := make([]byte, 1024)
		n, err := io.CopyBuffer(dst, src, buf)
		if err != nil {
			log.Printf("Copy: %s\n", err.Error())
		}
		if tcpConn, ok := dst.(closeWriter); ok {
			tcpConn.CloseWrite()
		} else {
			dst.SetReadDeadline(time.Now().Add(time.Second))
		}
		c <- n
	}

	start := time.Now()

	stod := make(chan int64)
	go copyAndWait(targetConn, conn, stod)

	dtos := make(chan int64)
	go copyAndWait(conn, targetConn, dtos)

	var nstod, ndtos int64
	for i := 0; i < 2; {
		select {
		case nstod = <-stod:
			i++
		case ndtos = <-dtos:
			i++
		}
	}
	d := BeautifyDuration(time.Since(start))
	log.Printf("CLOSE %s after %s ->%s <-%s\n",
		targetConn.RemoteAddr(), d, BeautifySize(nstod), BeautifySize(ndtos))
}
<commit_msg>set read deadline seems not work...<commit_after>package quictun

import (
	"io"
	"log"
	"net"
	"sync/atomic"
	"time"
)

type closeWriter interface {
	CloseWrite() error
}

var count int64

func Proxy(conn, targetConn net.Conn) {
	defer conn.Close()
	defer targetConn.Close()
	defer atomic.AddInt64(&count, -1)

	atomic.AddInt64(&count, 1)

	log.Printf("[%d] link start %v <-> %v",
		atomic.LoadInt64(&count), conn.RemoteAddr(), targetConn.RemoteAddr())

	copyAndWait := func(dst, src net.Conn, c chan int64) {
		buf := make([]byte, 1024)
		n, err := io.CopyBuffer(dst, src, buf)
		if err != nil {
			log.Printf("Copy: %s\n", err.Error())
		}
		if tcpConn, ok := dst.(closeWriter); ok {
			tcpConn.CloseWrite()
		} else {
			dst.Close()
		}
		c <- n
	}

	start := time.Now()

	stod := make(chan int64)
	go copyAndWait(targetConn, conn, stod)

	dtos := make(chan int64)
	go copyAndWait(conn, targetConn, dtos)

	var nstod, ndtos int64
	for i := 0; i < 2; {
		select {
		case nstod = <-stod:
			i++
		case ndtos = <-dtos:
			i++
		}
	}
	d := BeautifyDuration(time.Since(start))
	log.Printf("CLOSE %s after %s ->%s <-%s\n",
		targetConn.RemoteAddr(), d, BeautifySize(nstod), BeautifySize(ndtos))
}
<|endoftext|>


<commit_before>package http_api

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"

	"github.com/uniqush/uniqush-push/push"
	"github.com/uniqush/uniqush-push/srv/apns/common"
)

// HTTPPushRequestProcessor sends push notification requests to APNS using HTTP API
type HTTPPushRequestProcessor struct {
	client *http.Client
}

// NewRequestProcessor returns a new HTTPPushProcessor using net/http DefaultClient connection pool
func NewRequestProcessor() common.PushRequestProcessor {
	return &HTTPPushRequestProcessor{
		client: http.DefaultClient,
	}
}

func (processor *HTTPPushRequestProcessor) AddRequest(request *common.PushRequest) {
	go processor.sendRequests(request)
}

func (processor *HTTPPushRequestProcessor) GetMaxPayloadSize() int {
	return 4096
}

func (processor *HTTPPushRequestProcessor) Finalize() {
	switch transport := processor.client.Transport.(type) {
	case *http.Transport:
		transport.CloseIdleConnections()
	default:
	}
}

func (processor *HTTPPushRequestProcessor) SetErrorReportChan(errChan chan<- push.PushError) {}

func (processor *HTTPPushRequestProcessor) sendRequests(request *common.PushRequest) {
	defer close(request.ErrChan)

	jwtManager, err := common.NewJWTManager(request.PSP.FixedData["p8"], request.PSP.FixedData["keyid"], request.PSP.FixedData["teamid"])
	if err != nil {
		request.ErrChan <- push.NewError(err.Error())
		return
	}
	jwt, err := jwtManager.GenerateToken()
	if err != nil {
		request.ErrChan <- push.NewError(err.Error())
		return
	}

	wg := new(sync.WaitGroup)
	wg.Add(len(request.Devtokens))

	header := http.Header{
		"authorization":   []string{"bearer " + jwt},
		"apns-expiration": []string{"0"},  // Only attempt to send the notification once
		"apns-priority":   []string{"10"}, // Send notification immidiately
		"apns-topic":      []string{request.PSP.FixedData["bundleid"]},
	}

	for i, token := range request.Devtokens {
		msgID := request.GetId(i)

		url := fmt.Sprintf("%s/3/device/%s", request.PSP.VolatileData["addr"], hex.EncodeToString(token))
		httpRequest, err := http.NewRequest("POST", url, bytes.NewReader(request.Payload))
		if err != nil {
			request.ErrChan <- push.NewError(err.Error())
			continue
		}
		httpRequest.Header = header

		go processor.sendRequest(wg, httpRequest, msgID, request.ErrChan, request.ResChan)
	}

	wg.Wait()
}

func (processor *HTTPPushRequestProcessor) sendRequest(wg *sync.WaitGroup, request *http.Request, messageID uint32, errChan chan<- push.PushError, resChan chan<- *common.APNSResult) {
	defer wg.Done()

	response, err := processor.client.Do(request)
	if err != nil {
		errChan <- push.NewConnectionError(err)
		return
	}
	defer response.Body.Close()

	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		errChan <- push.NewError(err.Error())
		return
	}

	if len(responseBody) > 0 {
		// Successful request should return empty response body
		apnsError := new(APNSErrorResponse)
		err := json.Unmarshal(responseBody, apnsError)
		if err != nil {
			errChan <- push.NewError(err.Error())
		} else {
			errChan <- push.NewBadNotificationWithDetails(apnsError.Reason)
		}
	} else {
		resChan <- &common.APNSResult{
			MsgId: messageID,
		}
	}
}
<commit_msg>Determine message expiration from TTL<commit_after>package http_api

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"

	"github.com/uniqush/uniqush-push/push"
	"github.com/uniqush/uniqush-push/srv/apns/common"
)

// HTTPPushRequestProcessor sends push notification requests to APNS using HTTP API
type HTTPPushRequestProcessor struct {
	client *http.Client
}

// NewRequestProcessor returns a new HTTPPushProcessor using net/http DefaultClient connection pool
func NewRequestProcessor() common.PushRequestProcessor {
	return &HTTPPushRequestProcessor{
		client: http.DefaultClient,
	}
}

func (processor *HTTPPushRequestProcessor) AddRequest(request *common.PushRequest) {
	go processor.sendRequests(request)
}

func (processor *HTTPPushRequestProcessor) GetMaxPayloadSize() int {
	return 4096
}

func (processor *HTTPPushRequestProcessor) Finalize() {
	switch transport := processor.client.Transport.(type) {
	case *http.Transport:
		transport.CloseIdleConnections()
	default:
	}
}

func (processor *HTTPPushRequestProcessor) SetErrorReportChan(errChan chan<- push.PushError) {}

func (processor *HTTPPushRequestProcessor) sendRequests(request *common.PushRequest) {
	defer close(request.ErrChan)

	jwtManager, err := common.NewJWTManager(request.PSP.FixedData["p8"], request.PSP.FixedData["keyid"], request.PSP.FixedData["teamid"])
	if err != nil {
		request.ErrChan <- push.NewError(err.Error())
		return
	}
	jwt, err := jwtManager.GenerateToken()
	if err != nil {
		request.ErrChan <- push.NewError(err.Error())
		return
	}

	wg := new(sync.WaitGroup)
	wg.Add(len(request.Devtokens))

	header := http.Header{
		"authorization":   []string{"bearer " + jwt},
		"apns-expiration": []string{fmt.Sprint(request.Expiry)},
		"apns-priority":   []string{"10"}, // Send notification immidiately
		"apns-topic":      []string{request.PSP.FixedData["bundleid"]},
	}

	for i, token := range request.Devtokens {
		msgID := request.GetId(i)

		url := fmt.Sprintf("%s/3/device/%s", request.PSP.VolatileData["addr"], hex.EncodeToString(token))
		httpRequest, err := http.NewRequest("POST", url, bytes.NewReader(request.Payload))
		if err != nil {
			request.ErrChan <- push.NewError(err.Error())
			continue
		}
		httpRequest.Header = header

		go processor.sendRequest(wg, httpRequest, msgID, request.ErrChan, request.ResChan)
	}

	wg.Wait()
}

func (processor *HTTPPushRequestProcessor) sendRequest(wg *sync.WaitGroup, request *http.Request, messageID uint32, errChan chan<- push.PushError, resChan chan<- *common.APNSResult) {
	defer wg.Done()

	response, err := processor.client.Do(request)
	if err != nil {
		errChan <- push.NewConnectionError(err)
		return
	}
	defer response.Body.Close()

	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
		errChan <- push.NewError(err.Error())
		return
	}

	if len(responseBody) > 0 {
		// Successful request should return empty response body
		apnsError := new(APNSErrorResponse)
		err := json.Unmarshal(responseBody, apnsError)
		if err != nil {
			errChan <- push.NewError(err.Error())
		} else {
			errChan <- push.NewBadNotificationWithDetails(apnsError.Reason)
		}
	} else {
		resChan <- &common.APNSResult{
			MsgId: messageID,
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"strings"
	"sync"
	"time"
)

const (
	browserStarted int = iota
	browserFailed
	seleniumError
)

const (
	pingPath  string = "/ping"
	errPath   string = "/err"
	routePath string = "/wd/hub/session"
	proxyPath string = routePath + "/"
	head      int    = len(proxyPath)
	tail      int    = head + 32
	sessPart  int    = 4 // /wd/hub/session/{various length session}
)

var (
	port   = flag.Int("port", 8080, "port to bind to")
	conf   = flag.String("conf", "browsers.xml", "browsers configuration file path")
	listen string
	config Browsers
	routes map[string]*Host = make(map[string]*Host)
	num    uint64
	lock   sync.Mutex
)

type caps map[string]interface{}

func (c *caps) capability(k string) string {
	dc := (*c)["desiredCapabilities"]
	switch dc.(type) {
	case map[string]interface{}:
		v := dc.(map[string]interface{})
		switch v[k].(type) {
		case string:
			return v[k].(string)
		}
	}
	return ""
}

func (c *caps) browser() string {
	return c.capability("browserName")
}

func (c *caps) version() string {
	return c.capability("version")
}

func (h *Host) url() string {
	return fmt.Sprintf("http://%s%s", h.net(), routePath)
}

func (h *Host) session(c caps) (map[string]interface{}, int) {
	b, _ := json.Marshal(c)
	resp, err := http.Post(h.url(), "application/json", bytes.NewReader(b))
	if err != nil {
		return nil, seleniumError
	}
	if resp.StatusCode != http.StatusOK {
		return nil, browserFailed
	}
	var reply map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&reply)
	return reply, browserStarted
}

func reply(w http.ResponseWriter, msg map[string]interface{}) {
	reply, _ := json.Marshal(msg)
	w.Header().Set("Content-Type", "application/json")
	w.Write(reply)
}

func serial() uint64 {
	lock.Lock()
	defer lock.Unlock()
	id := num
	num++
	return id
}

func info(r *http.Request) (user, remote string) {
	user = "unknown"
	if u, _, ok := r.BasicAuth(); ok {
		user = u
	}
	remote = r.RemoteAddr
	return
}

func fmtBrowser(browser, version string) string {
	if version != "" {
		return fmt.Sprintf("%s-%s", browser, version)
	}
	return browser
}

func route(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	id := serial()
	user, remote := info(r)
	var c caps
	err := json.NewDecoder(r.Body).Decode(&c)
	if err != nil {
		http.Error(w, fmt.Sprintf("bad json format: %s", err.Error()), http.StatusBadRequest)
		log.Printf("[%d] [BAD_JSON] [%s] [%s] [%v]\n", id, user, remote, err)
		return
	}
	browser, version := c.browser(), c.version()
	if browser == "" {
		http.Error(w, "browser not set", http.StatusBadRequest)
		log.Printf("[%d] [BROWSER_NOT_SET] [%s] [%s]\n", id, user, remote)
		return
	}
	count := 0
loop:
	for {
		hosts := config.find(browser, version)
		if len(hosts) == 0 {
			http.Error(w, fmt.Sprintf("unsupported browser: %s %s", browser, version), http.StatusNotFound)
			log.Printf("[%d] [UNSUPPORTED_BROWSER] [%s] [%s] [%s]\n", id, user, remote, fmtBrowser(browser, version))
			return
		}
		for h, i := hosts.choose(); ; h, i = hosts.choose() {
			count++
			if h == nil {
				break loop
			}
			log.Printf("[%d] [SESSION_ATTEMPTED] [%s] [%s] [%s] [%s] [%d]\n", id, user, remote, fmtBrowser(browser, version), h.net(), count)
			excludes := make([]string, 0)
			switch resp, status := h.session(c); status {
			case browserStarted:
				sess := resp["sessionId"].(string)
				resp["sessionId"] = h.sum() + sess
				reply(w, resp)
				log.Printf("[%d] [%v] [SESSION_CREATED] [%s] [%s] [%s] [%s] [%s] [%d]\n", id, time.Now().Sub(start), user, remote, fmtBrowser(browser, version), h.net(), sess, count)
				return
			case browserFailed:
				hosts = append(hosts[:i], hosts[i+1:]...)
			case seleniumError:
				excludes = append(excludes, h.region)
				hosts = config.find(browser, version, excludes...)
			}
			log.Printf("[%d] [SESSION_FAILED] [%s] [%s] [%s] [%s]\n", id, user, remote, fmtBrowser(browser, version), h.net())
			if len(hosts) == 0 {
				break loop
			}
		}
	}
	http.Error(w, fmt.Sprintf("cannot create session %s on any hosts after %d attempt(s)", fmtBrowser(browser, version), count), http.StatusInternalServerError)
	log.Printf("[%d] [SESSION_NOT_CREATED] [%s] [%s] [%s]\n", id, user, remote, fmtBrowser(browser, version))
}

func proxy(r *http.Request) {
	user, remote := info(r)
	r.URL.Scheme = "http"
	if len(r.URL.Path) > tail {
		sum := r.URL.Path[head:tail]
		path := r.URL.Path[:head] + r.URL.Path[tail:]
		if h, ok := routes[sum]; ok {
			if body, err := ioutil.ReadAll(r.Body); err == nil {
				r.Body.Close()
				var msg map[string]interface{}
				if err := json.Unmarshal(body, &msg); err == nil {
					delete(msg, "sessionId")
					body, _ = json.Marshal(msg)
					r.ContentLength = int64(len(body))
				}
				r.Body = ioutil.NopCloser(bytes.NewReader(body))
			}
			r.URL.Host = h.net()
			r.URL.Path = path
			if r.Method == "DELETE" {
				sess := strings.Split(path, "/")[sessPart]
				log.Printf("[SESSION_DELETED] [%s] [%s] [%s] [%s]\n", user, remote, h.net(), sess)
			}
			return
		}
	}
	r.URL.Host = listen
	r.URL.Path = errPath
}

func ping(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Ok\n"))
}

func err(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "route not found", http.StatusNotFound)
}

func postOnly(handler http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
			return
		}
		handler(w, r)
	}
}

func readConfig(fn string, browsers *Browsers) error {
	file, err := ioutil.ReadFile(fn)
	if err != nil {
		return errors.New(fmt.Sprintf("error reading configuration file %s: %v", fn, err))
	}
	if err := xml.Unmarshal(file, browsers); err != nil {
		return errors.New(fmt.Sprintf("error parsing configuration file %s: %v", fn, err))
	}
	return nil
}

func linkRoutes(config *Browsers) {
	for _, b := range config.Browsers {
		for _, v := range b.Versions {
			for _, r := range v.Regions {
				for i, h := range r.Hosts {
					r.Hosts[i].region = r.Name
					routes[h.sum()] = &r.Hosts[i]
				}
			}
		}
	}
}

func init() {
	flag.Parse()
	listen = fmt.Sprintf(":%d", *port)

	err := readConfig(*conf, &config)
	if err != nil {
		log.Fatal(err)
	}
	linkRoutes(&config)
}

func mux() http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc(pingPath, ping)
	mux.HandleFunc(errPath, err)
	mux.HandleFunc(routePath, postOnly(route))
	mux.Handle(proxyPath, &httputil.ReverseProxy{Director: proxy})
	return mux
}

func main() {
	log.Println("listening on", listen)
	log.Print(http.ListenAndServe(listen, mux()))
}
<commit_msg>Removed remote port from logging.<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"strings"
	"sync"
	"time"
)

const (
	browserStarted int = iota
	browserFailed
	seleniumError
)

const (
	pingPath  string = "/ping"
	errPath   string = "/err"
	routePath string = "/wd/hub/session"
	proxyPath string = routePath + "/"
	head      int    = len(proxyPath)
	tail      int    = head + 32
	sessPart  int    = 4 // /wd/hub/session/{various length session}
)

var (
	port   = flag.Int("port", 8080, "port to bind to")
	conf   = flag.String("conf", "browsers.xml", "browsers configuration file path")
	listen string
	config Browsers
	routes map[string]*Host = make(map[string]*Host)
	num    uint64
	lock   sync.Mutex
)

type caps map[string]interface{}

func (c *caps) capability(k string) string {
	dc := (*c)["desiredCapabilities"]
	switch dc.(type) {
	case map[string]interface{}:
		v := dc.(map[string]interface{})
		switch v[k].(type) {
		case string:
			return v[k].(string)
		}
	}
	return ""
}

func (c *caps) browser() string {
	return c.capability("browserName")
}

func (c *caps) version() string {
	return c.capability("version")
}

func (h *Host) url() string {
	return fmt.Sprintf("http://%s%s", h.net(), routePath)
}

func (h *Host) session(c caps) (map[string]interface{}, int) {
	b, _ := json.Marshal(c)
	resp, err := http.Post(h.url(), "application/json", bytes.NewReader(b))
	if err != nil {
		return nil, seleniumError
	}
	if resp.StatusCode != http.StatusOK {
		return nil, browserFailed
	}
	var reply map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&reply)
	return reply, browserStarted
}

func reply(w http.ResponseWriter, msg map[string]interface{}) {
	reply, _ := json.Marshal(msg)
	w.Header().Set("Content-Type", "application/json")
	w.Write(reply)
}

func serial() uint64 {
	lock.Lock()
	defer lock.Unlock()
	id := num
	num++
	return id
}

func info(r *http.Request) (user, remote string) {
	user = "unknown"
	if u, _, ok := r.BasicAuth(); ok {
		user = u
	}
	remote, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		remote = r.RemoteAddr
	}
	return
}

func fmtBrowser(browser, version string) string {
	if version != "" {
		return fmt.Sprintf("%s-%s", browser, version)
	}
	return browser
}

func route(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	id := serial()
	user, remote := info(r)
	var c caps
	err := json.NewDecoder(r.Body).Decode(&c)
	if err != nil {
		http.Error(w, fmt.Sprintf("bad json format: %s", err.Error()), http.StatusBadRequest)
		log.Printf("[%d] [BAD_JSON] [%s] [%s] [%v]\n", id, user, remote, err)
		return
	}
	browser, version := c.browser(), c.version()
	if browser == "" {
		http.Error(w, "browser not set", http.StatusBadRequest)
		log.Printf("[%d] [BROWSER_NOT_SET] [%s] [%s]\n", id, user, remote)
		return
	}
	count := 0
loop:
	for {
		hosts := config.find(browser, version)
		if len(hosts) == 0 {
			http.Error(w, fmt.Sprintf("unsupported browser: %s %s", browser, version), http.StatusNotFound)
			log.Printf("[%d] [UNSUPPORTED_BROWSER] [%s] [%s] [%s]\n", id, user, remote, fmtBrowser(browser, version))
			return
		}
		for h, i := hosts.choose(); ; h, i = hosts.choose() {
			count++
			if h == nil {
				break loop
			}
			log.Printf("[%d] [SESSION_ATTEMPTED] [%s] [%s] [%s] [%s] [%d]\n", id, user, remote, fmtBrowser(browser, version), h.net(), count)
			excludes := make([]string, 0)
			switch resp, status := h.session(c); status {
			case browserStarted:
				sess := resp["sessionId"].(string)
				resp["sessionId"] = h.sum() + sess
				reply(w, resp)
				log.Printf("[%d] [%v] [SESSION_CREATED] [%s] [%s] [%s] [%s] [%s] [%d]\n", id, time.Now().Sub(start), user, remote, fmtBrowser(browser, version), h.net(), sess, count)
				return
			case browserFailed:
				hosts = append(hosts[:i], hosts[i+1:]...)
			case seleniumError:
				excludes = append(excludes, h.region)
				hosts = config.find(browser, version, excludes...)
			}
			log.Printf("[%d] [SESSION_FAILED] [%s] [%s] [%s] [%s]\n", id, user, remote, fmtBrowser(browser, version), h.net())
			if len(hosts) == 0 {
				break loop
			}
		}
	}
	http.Error(w, fmt.Sprintf("cannot create session %s on any hosts after %d attempt(s)", fmtBrowser(browser, version), count), http.StatusInternalServerError)
	log.Printf("[%d] [SESSION_NOT_CREATED] [%s] [%s] [%s]\n", id, user, remote, fmtBrowser(browser, version))
}

func proxy(r *http.Request) {
	user, remote := info(r)
	r.URL.Scheme = "http"
	if len(r.URL.Path) > tail {
		sum := r.URL.Path[head:tail]
		path := r.URL.Path[:head] + r.URL.Path[tail:]
		if h, ok := routes[sum]; ok {
			if body, err := ioutil.ReadAll(r.Body); err == nil {
				r.Body.Close()
				var msg map[string]interface{}
				if err := json.Unmarshal(body, &msg); err == nil {
					delete(msg, "sessionId")
					body, _ = json.Marshal(msg)
					r.ContentLength = int64(len(body))
				}
				r.Body = ioutil.NopCloser(bytes.NewReader(body))
			}
			r.URL.Host = h.net()
			r.URL.Path = path
			if r.Method == "DELETE" {
				sess := strings.Split(path, "/")[sessPart]
				log.Printf("[SESSION_DELETED] [%s] [%s] [%s] [%s]\n", user, remote, h.net(), sess)
			}
			return
		}
	}
	r.URL.Host = listen
	r.URL.Path = errPath
}

func ping(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Ok\n"))
}

func err(w http.ResponseWriter, r *http.Request) {
	http.Error(w, "route not found", http.StatusNotFound)
}

func postOnly(handler http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
			return
		}
		handler(w, r)
	}
}

func readConfig(fn string, browsers *Browsers) error {
	file, err := ioutil.ReadFile(fn)
	if err != nil {
		return errors.New(fmt.Sprintf("error reading configuration file %s: %v", fn, err))
	}
	if err := xml.Unmarshal(file, browsers); err != nil {
		return errors.New(fmt.Sprintf("error parsing configuration file %s: %v", fn, err))
	}
	return nil
}

func linkRoutes(config *Browsers) {
	for _, b := range config.Browsers {
		for _, v := range b.Versions {
			for _, r := range v.Regions {
				for i, h := range r.Hosts {
					r.Hosts[i].region = r.Name
					routes[h.sum()] = &r.Hosts[i]
				}
			}
		}
	}
}

func init() {
	flag.Parse()
	listen = fmt.Sprintf(":%d", *port)

	err := readConfig(*conf, &config)
	if err != nil {
		log.Fatal(err)
	}
	linkRoutes(&config)
}

func mux() http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc(pingPath, ping)
	mux.HandleFunc(errPath, err)
	mux.HandleFunc(routePath, postOnly(route))
	mux.Handle(proxyPath, &httputil.ReverseProxy{Director: proxy})
	return mux
}

func main() {
	log.Println("listening on", listen)
	log.Print(http.ListenAndServe(listen, mux()))
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.net/html/charset"
	"compress/gzip"
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"time"
)

var disableGzip = flag.Bool("disable-gzip", false, "Don't compress HTTP responses with gzip.")

type proxyHandler struct {
	// TLS is whether this is an HTTPS connection.
	TLS bool

	// connectPort is the server port that was specified in a CONNECT request.
	connectPort string

	// user is a user that has already been authenticated.
	user string

	// rt is the RoundTripper that will be used to fulfill the requests.
	// If it is nil, a default Transport will be used.
	rt http.RoundTripper
}

// lanAddress returns whether addr is in one of the LAN address ranges.
func lanAddress(addr string) bool {
	ip := net.ParseIP(addr)
	if ip4 := ip.To4(); ip4 != nil {
		switch ip4[0] {
		case 10, 127:
			return true
		case 172:
			return ip4[1]&0xf0 == 16
		case 192:
			return ip4[1] == 168
		}
		return false
	}

	if ip[0]&0xfe == 0xfc {
		return true
	}
	if ip[0] == 0xfe && (ip[1]&0xfc) == 0x80 {
		return true
	}

	return false
}

func (h proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	activeConnections.Add(1)
	defer activeConnections.Done()

	if len(r.URL.String()) > 10000 {
		http.Error(w, "URL too long", http.StatusRequestURITooLong)
		return
	}

	client := r.RemoteAddr
	host, _, err := net.SplitHostPort(client)
	if err == nil {
		client = host
	}
	user := client

	if h.user != "" {
		user = h.user
	} else if !*authNever && !h.TLS && (*authAlways || !lanAddress(client)) {
		u := authenticate(w, r)
		if u == "" {
			return
		}
		user = u
	}

	if r.Host == localServer {
		http.DefaultServeMux.ServeHTTP(w, r)
		return
	}

	if r.Method == "CONNECT" {
		if !tlsReady {
			sc := scorecard{
				tally: URLRules.MatchingRules(r.URL),
			}
			sc.calculate(user)
			if sc.action == BLOCK {
				showBlockPage(w, r, &sc, user)
				logAccess(r, nil, sc, "", 0, false, user)
				return
			}
		}

		conn, err := newHijackedConn(w)
		if err != nil {
			fmt.Fprintln(conn, "HTTP/1.1 500 Internal Server Error")
			fmt.Fprintln(conn)
			fmt.Fprintln(conn, err)
			conn.Close()
			return
		}
		fmt.Fprint(conn, "HTTP/1.1 200 Connection Established\r\n\r\n")
		if tlsReady {
			SSLBump(conn, r.URL.Host, user)
		} else {
			connectDirect(conn, r.URL.Host, nil)
		}
		return
	}

	if r.Header.Get("Upgrade") == "websocket" {
		h.makeWebsocketConnection(w, r)
		return
	}

	r.Header.Add("Via", r.Proto+" Redwood")
	r.Header.Add("X-Forwarded-For", client)

	gzipOK := !*disableGzip && strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
	r.Header.Del("Accept-Encoding")

	// Reconstruct the URL if it is incomplete (i.e. on a transparent proxy).
	if r.URL.Host == "" {
		r.URL.Host = r.Host
	}
	if r.URL.Scheme == "" {
		if h.TLS {
			r.URL.Scheme = "https"
		} else {
			r.URL.Scheme = "http"
		}
	}

	sc := scorecard{
		tally: URLRules.MatchingRules(r.URL),
	}
	sc.calculate(user)
	if sc.action == BLOCK {
		showBlockPage(w, r, &sc, user)
		logAccess(r, nil, sc, "", 0, false, user)
		return
	}

	changeQuery(r.URL)

	var resp *http.Response
	if h.rt == nil {
		resp, err = transport.RoundTrip(r)
	} else {
		resp, err = h.rt.RoundTrip(r)
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
		log.Printf("error fetching %s: %s", r.URL, err)
		logAccess(r, nil, sc, "", 0, false, user)
		return
	}
	defer resp.Body.Close()

	contentType, action := checkContentType(resp)

	switch action {
	case BLOCK:
		sc.action = BLOCK
		sc.blocked = []string{"blocked-mime"}
		showBlockPage(w, r, &sc, user)
		logAccess(r, resp, sc, contentType, 0, false, user)
		return

	case ALLOW:
		sc.action = IGNORE
		copyResponseHeader(w, resp)
		io.Copy(w, resp.Body)
		logAccess(r, resp, sc, contentType, 0, false, user)
		return
	}

	lr := &io.LimitedReader{
		R: resp.Body,
		N: 1e7,
	}
	content, err := ioutil.ReadAll(lr)
	if err != nil {
		log.Printf("error while reading response body (URL: %s): %s", r.URL, err)
	}
	if lr.N == 0 {
		log.Println("response body too long to filter:", r.URL)
		copyResponseHeader(w, resp)
		w.Write(content)
		n, err := io.Copy(w, resp.Body)
		if err != nil {
			log.Printf("error while copying response (URL: %s): %s", r.URL, err)
		}
		sc.action = IGNORE
		logAccess(r, resp, sc, contentType, int(n)+len(content), false, user)
		return
	}

	modified := false
	_, cs, _ := charset.DetermineEncoding(content, resp.Header.Get("Content-Type"))
	if strings.Contains(contentType, "html") {
		modified = pruneContent(r.URL, &content, cs)
		if modified {
			resp.Header.Set("Content-Type", "text/html; charset=utf-8")
			cs = "utf-8"
		}
	}

	scanContent(content, contentType, cs, sc.tally)
	sc.calculate(user)

	if sc.action == BLOCK {
		showBlockPage(w, r, &sc, user)
		logAccess(r, resp, sc, contentType, len(content), modified, user)
		return
	}

	if gzipOK && len(content) > 1000 {
		resp.Header.Set("Content-Encoding", "gzip")
		copyResponseHeader(w, resp)
		gzw := gzip.NewWriter(w)
		gzw.Write(content)
		gzw.Close()
	} else {
		copyResponseHeader(w, resp)
		w.Write(content)
	}

	logAccess(r, resp, sc, contentType, len(content), modified, user)
}

// copyResponseHeader writes resp's header and status code to w.
func copyResponseHeader(w http.ResponseWriter, resp *http.Response) {
	newHeader := w.Header()
	for key, values := range resp.Header {
		for _, v := range values {
			newHeader.Add(key, v)
		}
	}

	w.WriteHeader(resp.StatusCode)
}

// A hijackedConn is a connection that has been hijacked (to fulfill a CONNECT
// request).
type hijackedConn struct {
	net.Conn
	io.Reader
}

func (hc *hijackedConn) Read(b []byte) (int, error) {
	return hc.Reader.Read(b)
}

func newHijackedConn(w http.ResponseWriter) (*hijackedConn, error) {
	hj, ok := w.(http.Hijacker)
	if !ok {
		return nil, errors.New("connection doesn't support hijacking")
	}
	conn, bufrw, err := hj.Hijack()
	if err != nil {
		return nil, err
	}
	err = bufrw.Flush()
	if err != nil {
		return nil, err
	}
	return &hijackedConn{
		Conn:   conn,
		Reader: bufrw.Reader,
	}, nil
}

var transport = http.Transport{
	TLSClientConfig: unverifiedClientConfig,
	Proxy:           http.ProxyFromEnvironment,
}

// This is to deal with the problem of stale keepalive connections, which cause
// transport.RoundTrip to return io.EOF.
func init() {
	go func() {
		for _ = range time.Tick(10 * time.Second) {
			transport.CloseIdleConnections()
		}
	}()

	transport.RegisterProtocol("ftp", FTPTransport{})
}

func (h proxyHandler) makeWebsocketConnection(w http.ResponseWriter, r *http.Request) {
	addr := r.Host
	if _, _, err := net.SplitHostPort(addr); err != nil {
		// There is no port specified; we need to add it.
		port := h.connectPort
		if port == "" {
			port = "80"
		}
		addr = net.JoinHostPort(addr, port)
	}
	var err error
	var serverConn net.Conn
	if h.TLS {
		serverConn, err = tls.Dial("tcp", addr, unverifiedClientConfig)
	} else {
		serverConn, err = net.Dial("tcp", addr)
	}
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = r.Write(serverConn)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	hj, ok := w.(http.Hijacker)
	if !ok {
		http.Error(w, "Couldn't create a websocket connection", http.StatusInternalServerError)
		return
	}
	conn, bufrw, err := hj.Hijack()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	go func() {
		io.Copy(conn, serverConn)
		conn.Close()
	}()
	io.Copy(serverConn, bufrw)
	serverConn.Close()
	return
}
<commit_msg>If the response from the server doesn't include a Content-Type, don't add one.<commit_after>package main

import (
	"code.google.com/p/go.net/html/charset"
	"compress/gzip"
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"time"
)

var disableGzip = flag.Bool("disable-gzip", false, "Don't compress HTTP responses with gzip.")

type proxyHandler struct {
	// TLS is whether this is an HTTPS connection.
	TLS bool

	// connectPort is the server port that was specified in a CONNECT request.
	connectPort string

	// user is a user that has already been authenticated.
	user string

	// rt is the RoundTripper that will be used to fulfill the requests.
	// If it is nil, a default Transport will be used.
	rt http.RoundTripper
}

// lanAddress returns whether addr is in one of the LAN address ranges.
func lanAddress(addr string) bool {
	ip := net.ParseIP(addr)
	if ip4 := ip.To4(); ip4 != nil {
		switch ip4[0] {
		case 10, 127:
			return true
		case 172:
			return ip4[1]&0xf0 == 16
		case 192:
			return ip4[1] == 168
		}
		return false
	}

	if ip[0]&0xfe == 0xfc {
		return true
	}
	if ip[0] == 0xfe && (ip[1]&0xfc) == 0x80 {
		return true
	}

	return false
}

func (h proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	activeConnections.Add(1)
	defer activeConnections.Done()

	if len(r.URL.String()) > 10000 {
		http.Error(w, "URL too long", http.StatusRequestURITooLong)
		return
	}

	client := r.RemoteAddr
	host, _, err := net.SplitHostPort(client)
	if err == nil {
		client = host
	}
	user := client

	if h.user != "" {
		user = h.user
	} else if !*authNever && !h.TLS && (*authAlways || !lanAddress(client)) {
		u := authenticate(w, r)
		if u == "" {
			return
		}
		user = u
	}

	if r.Host == localServer {
		http.DefaultServeMux.ServeHTTP(w, r)
		return
	}

	if r.Method == "CONNECT" {
		if !tlsReady {
			sc := scorecard{
				tally: URLRules.MatchingRules(r.URL),
			}
			sc.calculate(user)
			if sc.action == BLOCK {
				showBlockPage(w, r, &sc, user)
				logAccess(r, nil, sc, "", 0, false, user)
				return
			}
		}

		conn, err := newHijackedConn(w)
		if err != nil {
			fmt.Fprintln(conn, "HTTP/1.1 500 Internal Server Error")
			fmt.Fprintln(conn)
			fmt.Fprintln(conn, err)
			conn.Close()
			return
		}
		fmt.Fprint(conn, "HTTP/1.1 200 Connection Established\r\n\r\n")
		if tlsReady {
			SSLBump(conn, r.URL.Host, user)
		} else {
			connectDirect(conn, r.URL.Host, nil)
		}
		return
	}

	if r.Header.Get("Upgrade") == "websocket" {
		h.makeWebsocketConnection(w, r)
		return
	}

	r.Header.Add("Via", r.Proto+" Redwood")
	r.Header.Add("X-Forwarded-For", client)

	gzipOK := !*disableGzip && strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
	r.Header.Del("Accept-Encoding")

	// Reconstruct the URL if it is incomplete (i.e. on a transparent proxy).
	if r.URL.Host == "" {
		r.URL.Host = r.Host
	}
	if r.URL.Scheme == "" {
		if h.TLS {
			r.URL.Scheme = "https"
		} else {
			r.URL.Scheme = "http"
		}
	}

	sc := scorecard{
		tally: URLRules.MatchingRules(r.URL),
	}
	sc.calculate(user)
	if sc.action == BLOCK {
		showBlockPage(w, r, &sc, user)
		logAccess(r, nil, sc, "", 0, false, user)
		return
	}

	changeQuery(r.URL)

	var resp *http.Response
	if h.rt == nil {
		resp, err = transport.RoundTrip(r)
	} else {
		resp, err = h.rt.RoundTrip(r)
	}

	if err != nil {
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
		log.Printf("error fetching %s: %s", r.URL, err)
		logAccess(r, nil, sc, "", 0, false, user)
		return
	}
	defer resp.Body.Close()

	contentType, action := checkContentType(resp)

	switch action {
	case BLOCK:
		sc.action = BLOCK
		sc.blocked = []string{"blocked-mime"}
		showBlockPage(w, r, &sc, user)
		logAccess(r, resp, sc, contentType, 0, false, user)
		return

	case ALLOW:
		sc.action = IGNORE
		copyResponseHeader(w, resp)
		io.Copy(w, resp.Body)
		logAccess(r, resp, sc, contentType, 0, false, user)
		return
	}

	lr := &io.LimitedReader{
		R: resp.Body,
		N: 1e7,
	}
	content, err := ioutil.ReadAll(lr)
	if err != nil {
		log.Printf("error while reading response body (URL: %s): %s", r.URL, err)
	}
	if lr.N == 0 {
		log.Println("response body too long to filter:", r.URL)
		copyResponseHeader(w, resp)
		w.Write(content)
		n, err := io.Copy(w, resp.Body)
		if err != nil {
			log.Printf("error while copying response (URL: %s): %s", r.URL, err)
		}
		sc.action = IGNORE
		logAccess(r, resp, sc, contentType, int(n)+len(content), false, user)
		return
	}

	modified := false
	_, cs, _ := charset.DetermineEncoding(content, resp.Header.Get("Content-Type"))
	if strings.Contains(contentType, "html") {
		modified = pruneContent(r.URL, &content, cs)
		if modified {
			resp.Header.Set("Content-Type", "text/html; charset=utf-8")
			cs = "utf-8"
		}
	}

	scanContent(content, contentType, cs, sc.tally)
	sc.calculate(user)

	if sc.action == BLOCK {
		showBlockPage(w, r, &sc, user)
		logAccess(r, resp, sc, contentType, len(content), modified, user)
		return
	}

	if resp.Header.Get("Content-Type") == "" {
		// If the server didn't specify a content type, don't let the http
		// package add one by doing MIME sniffing.
		resp.Header.Set("Content-Type", "")
	}

	if gzipOK && len(content) > 1000 {
		resp.Header.Set("Content-Encoding", "gzip")
		resp.Header.Del("Content-Length")
		copyResponseHeader(w, resp)
		gzw := gzip.NewWriter(w)
		gzw.Write(content)
		gzw.Close()
	} else {
		copyResponseHeader(w, resp)
		w.Write(content)
	}

	logAccess(r, resp, sc, contentType, len(content), modified, user)
}

// copyResponseHeader writes resp's header and status code to w.
func copyResponseHeader(w http.ResponseWriter, resp *http.Response) {
	newHeader := w.Header()
	for key, values := range resp.Header {
		for _, v := range values {
			newHeader.Add(key, v)
		}
	}

	w.WriteHeader(resp.StatusCode)
}

// A hijackedConn is a connection that has been hijacked (to fulfill a CONNECT
// request).
type hijackedConn struct {
	net.Conn
	io.Reader
}

func (hc *hijackedConn) Read(b []byte) (int, error) {
	return hc.Reader.Read(b)
}

func newHijackedConn(w http.ResponseWriter) (*hijackedConn, error) {
	hj, ok := w.(http.Hijacker)
	if !ok {
		return nil, errors.New("connection doesn't support hijacking")
	}
	conn, bufrw, err := hj.Hijack()
	if err != nil {
		return nil, err
	}
	err = bufrw.Flush()
	if err != nil {
		return nil, err
	}
	return &hijackedConn{
		Conn:   conn,
		Reader: bufrw.Reader,
	}, nil
}

var transport = http.Transport{
	TLSClientConfig: unverifiedClientConfig,
	Proxy:           http.ProxyFromEnvironment,
}

// This is to deal with the problem of stale keepalive connections, which cause
// transport.RoundTrip to return io.EOF.
func init() {
	go func() {
		for _ = range time.Tick(10 * time.Second) {
			transport.CloseIdleConnections()
		}
	}()

	transport.RegisterProtocol("ftp", FTPTransport{})
}

func (h proxyHandler) makeWebsocketConnection(w http.ResponseWriter, r *http.Request) {
	addr := r.Host
	if _, _, err := net.SplitHostPort(addr); err != nil {
		// There is no port specified; we need to add it.
		port := h.connectPort
		if port == "" {
			port = "80"
		}
		addr = net.JoinHostPort(addr, port)
	}
	var err error
	var serverConn net.Conn
	if h.TLS {
		serverConn, err = tls.Dial("tcp", addr, unverifiedClientConfig)
	} else {
		serverConn, err = net.Dial("tcp", addr)
	}
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = r.Write(serverConn)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	hj, ok := w.(http.Hijacker)
	if !ok {
		http.Error(w, "Couldn't create a websocket connection", http.StatusInternalServerError)
		return
	}
	conn, bufrw, err := hj.Hijack()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	go func() {
		io.Copy(conn, serverConn)
		conn.Close()
	}()
	io.Copy(serverConn, bufrw)
	serverConn.Close()
	return
}
<|endoftext|>


<commit_before>package shell

import (
	"context"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/cluster"
	"github.com/chrislusf/seaweedfs/weed/pb"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/pb/master_pb"
	"github.com/chrislusf/seaweedfs/weed/util/grace"
	"io"
	"math/rand"
	"os"
	"path"
	"regexp"
	"sort"
	"strings"

	"github.com/peterh/liner"
)

var (
	line        *liner.State
	historyPath = path.Join(os.TempDir(), "weed-shell")
)

func RunShell(options ShellOptions) {

	sort.Slice(Commands, func(i, j int) bool {
		return strings.Compare(Commands[i].Name(), Commands[j].Name()) < 0
	})

	line = liner.NewLiner()
	defer line.Close()
	grace.OnInterrupt(func() {
		line.Close()
	})

	line.SetCtrlCAborts(true)
	line.SetTabCompletionStyle(liner.TabPrints)

	setCompletionHandler()
	loadHistory()

	defer saveHistory()

	reg, _ := regexp.Compile(`'.*?'|".*?"|\S+`)

	commandEnv := NewCommandEnv(options)

	go commandEnv.MasterClient.KeepConnectedToMaster()
	commandEnv.MasterClient.WaitUntilConnected()

	if commandEnv.option.FilerAddress == "" {
		var filers []pb.ServerAddress
		commandEnv.MasterClient.WithClient(false, func(client master_pb.SeaweedClient) error {
			resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
				ClientType: cluster.FilerType,
			})
			if err != nil {
				return err
			}

			for _, clusterNode := range resp.ClusterNodes {
				filers = append(filers, pb.ServerAddress(clusterNode.Address))
			}
			return nil
		})
		fmt.Printf("master: %s ", *options.Masters)
		if len(filers) > 0 {
			fmt.Printf("filers: %v", filers)
			commandEnv.option.FilerAddress = filers[rand.Intn(len(filers))]
		}
		fmt.Println()
	}

	if commandEnv.option.FilerAddress != "" {
		commandEnv.WithFilerClient(false, func(filerClient filer_pb.SeaweedFilerClient) error {
			resp, err := filerClient.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
			if err != nil {
				return err
			}
			if resp.ClusterId != "" {
				fmt.Printf(`
---
Free Monitoring Data URL:
https://cloud.seaweedfs.com/ui/%s
---
`, resp.ClusterId)
			}
			return nil
		})
	}

	for {
		cmd, err := line.Prompt("> ")
		if err != nil {
			if err != io.EOF {
				fmt.Printf("%v\n", err)
			}
			return
		}

		for _, c := range strings.Split(cmd, ";") {
			if processEachCmd(reg, c, commandEnv) {
				return
			}
		}
	}
}

func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool {
	cmds := reg.FindAllString(cmd, -1)

	line.AppendHistory(cmd)

	if len(cmds) == 0 {
		return false
	} else {

		args := make([]string, len(cmds[1:]))

		for i := range args {
			args[i] = strings.Trim(string(cmds[1+i]), "\"'")
		}

		cmd := cmds[0]
		if cmd == "help" || cmd == "?" {
			printHelp(cmds)
		} else if cmd == "exit" || cmd == "quit" {
			return true
		} else {
			foundCommand := false
			for _, c := range Commands {
				if c.Name() == cmd || c.Name() == "fs."+cmd {
					if err := c.Do(args, commandEnv, os.Stdout); err != nil {
						fmt.Fprintf(os.Stderr, "error: %v\n", err)
					}
					foundCommand = true
				}
			}
			if !foundCommand {
				fmt.Fprintf(os.Stderr, "unknown command: %v\n", cmd)
			}
		}

	}
	return false
}

func printGenericHelp() {
	msg :=
		`Type:	"help <command>" for help on <command>. Most commands support "<command> -h" also for options.
`
	fmt.Print(msg)

	for _, c := range Commands {
		helpTexts := strings.SplitN(c.Help(), "\n", 2)
		fmt.Printf("  %-30s\t# %s \n", c.Name(), helpTexts[0])
	}
}

func printHelp(cmds []string) {
	args := cmds[1:]
	if len(args) == 0 {
		printGenericHelp()
	} else if len(args) > 1 {
		fmt.Println()
	} else {
		cmd := strings.ToLower(args[0])

		for _, c := range Commands {
			if c.Name() == cmd {
				fmt.Printf("  %s\t# %s\n", c.Name(), c.Help())
			}
		}
	}
}

func setCompletionHandler() {
	line.SetCompleter(func(line string) (c []string) {
		for _, i := range Commands {
			if strings.HasPrefix(i.Name(), strings.ToLower(line)) {
				c = append(c, i.Name())
			}
		}
		return
	})
}

func loadHistory() {
	if f, err := os.Open(historyPath); err == nil {
		line.ReadHistory(f)
		f.Close()
	}
}

func saveHistory() {
	if f, err := os.Create(historyPath); err != nil {
		fmt.Printf("Error creating history file: %v\n", err)
	} else {
		if _, err = line.WriteHistory(f); err != nil {
			fmt.Printf("Error writing history file: %v\n", err)
		}
		f.Close()
	}
}
<commit_msg>fix compilation<commit_after>package shell

import (
	"context"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/cluster"
	"github.com/chrislusf/seaweedfs/weed/pb"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/pb/master_pb"
	"github.com/chrislusf/seaweedfs/weed/util/grace"
	"io"
	"math/rand"
	"os"
	"path"
	"regexp"
	"sort"
	"strings"

	"github.com/peterh/liner"
)

var (
	line        *liner.State
	historyPath = path.Join(os.TempDir(), "weed-shell")
)

func RunShell(options ShellOptions) {

	sort.Slice(Commands, func(i, j int) bool {
		return strings.Compare(Commands[i].Name(), Commands[j].Name()) < 0
	})

	line = liner.NewLiner()
	defer line.Close()
	grace.OnInterrupt(func() {
		line.Close()
	})

	line.SetCtrlCAborts(true)
	line.SetTabCompletionStyle(liner.TabPrints)

	setCompletionHandler()
	loadHistory()

	defer saveHistory()

	reg, _ := regexp.Compile(`'.*?'|".*?"|\S+`)

	commandEnv := NewCommandEnv(&options)

	go commandEnv.MasterClient.KeepConnectedToMaster()
	commandEnv.MasterClient.WaitUntilConnected()

	if commandEnv.option.FilerAddress == "" {
		var filers []pb.ServerAddress
		commandEnv.MasterClient.WithClient(false, func(client master_pb.SeaweedClient) error {
			resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
				ClientType: cluster.FilerType,
			})
			if err != nil {
				return err
			}

			for _, clusterNode := range resp.ClusterNodes {
				filers = append(filers, pb.ServerAddress(clusterNode.Address))
			}
			return nil
		})
		fmt.Printf("master: %s ", *options.Masters)
		if len(filers) > 0 {
			fmt.Printf("filers: %v", filers)
			commandEnv.option.FilerAddress = filers[rand.Intn(len(filers))]
		}
		fmt.Println()
	}

	if commandEnv.option.FilerAddress != "" {
		commandEnv.WithFilerClient(false, func(filerClient filer_pb.SeaweedFilerClient) error {
			resp, err := filerClient.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
			if err != nil {
				return err
			}
			if resp.ClusterId != "" {
				fmt.Printf(`
---
Free Monitoring Data URL:
https://cloud.seaweedfs.com/ui/%s
---
`, resp.ClusterId)
			}
			return nil
		})
	}

	for {
		cmd, err := line.Prompt("> ")
		if err != nil {
			if err != io.EOF {
				fmt.Printf("%v\n", err)
			}
			return
		}

		for _, c := range strings.Split(cmd, ";") {
			if processEachCmd(reg, c, commandEnv) {
				return
			}
		}
	}
}

func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool {
	cmds := reg.FindAllString(cmd, -1)

	line.AppendHistory(cmd)

	if len(cmds) == 0 {
		return false
	} else {

		args := make([]string, len(cmds[1:]))

		for i := range args {
			args[i] = strings.Trim(string(cmds[1+i]), "\"'")
		}

		cmd := cmds[0]
		if cmd == "help" || cmd == "?" {
			printHelp(cmds)
		} else if cmd == "exit" || cmd == "quit" {
			return true
		} else {
			foundCommand := false
			for _, c := range Commands {
				if c.Name() == cmd || c.Name() == "fs."+cmd {
					if err := c.Do(args, commandEnv, os.Stdout); err != nil {
						fmt.Fprintf(os.Stderr, "error: %v\n", err)
					}
					foundCommand = true
				}
			}
			if !foundCommand {
				fmt.Fprintf(os.Stderr, "unknown command: %v\n", cmd)
			}
		}

	}
	return false
}

func printGenericHelp() {
	msg :=
		`Type:	"help <command>" for help on <command>. Most commands support "<command> -h" also for options.
`
	fmt.Print(msg)

	for _, c := range Commands {
		helpTexts := strings.SplitN(c.Help(), "\n", 2)
		fmt.Printf("  %-30s\t# %s \n", c.Name(), helpTexts[0])
	}
}

func printHelp(cmds []string) {
	args := cmds[1:]
	if len(args) == 0 {
		printGenericHelp()
	} else if len(args) > 1 {
		fmt.Println()
	} else {
		cmd := strings.ToLower(args[0])

		for _, c := range Commands {
			if c.Name() == cmd {
				fmt.Printf("  %s\t# %s\n", c.Name(), c.Help())
			}
		}
	}
}

func setCompletionHandler() {
	line.SetCompleter(func(line string) (c []string) {
		for _, i := range Commands {
			if strings.HasPrefix(i.Name(), strings.ToLower(line)) {
				c = append(c, i.Name())
			}
		}
		return
	})
}

func loadHistory() {
	if f, err := os.Open(historyPath); err == nil {
		line.ReadHistory(f)
		f.Close()
	}
}

func saveHistory() {
	if f, err := os.Create(historyPath); err != nil {
		fmt.Printf("Error creating history file: %v\n", err)
	} else {
		if _, err = line.WriteHistory(f); err != nil {
			fmt.Printf("Error writing history file: %v\n", err)
		}
		f.Close()
	}
}
<|endoftext|>


<commit_before>package xsdgen_test

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"aqwari.net/xml/xsdgen"
)

func tmpfile() *os.File {
	f, err := ioutil.TempFile("", "xsdgen_test")
	if err != nil {
		panic(err)
	}
	return f
}

func xsdfile(s string) (filename string) {
	file := tmpfile()
	defer file.Close()
	fmt.Fprintf(file, `
		<schema xmlns="http://www.w3.org/2001/XMLSchema"
		        xmlns:tns="http://www.example.com/"
		        xmlns:xs="http://www.w3.org/2001/XMLSchema"
		        xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
		        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
		        targetNamespace="http://www.example.com/">
		  %s
		</schema>
	`, s)
	return file.Name()
}

func ExampleConfig_GenCLI() {
	var cfg xsdgen.Config
	cfg.Option(
		xsdgen.IgnoreAttributes("id", "href", "offset"),
		xsdgen.IgnoreElements("comment"),
		xsdgen.PackageName("webapi"),
		xsdgen.Replace("_", ""),
		xsdgen.HandleSOAPArrayType(),
		xsdgen.SOAPArrayAsSlice(),
	)
	if err := cfg.GenCLI("webapi.xsd", "deps/soap11.xsd"); err != nil {
		log.Fatal(err)
	}
}

func ExampleLogOutput() {
	var cfg xsdgen.Config
	cfg.Option(
		xsdgen.LogOutput(log.New(os.Stderr, "", 0)),
		xsdgen.LogLevel(2))
	if err := cfg.GenCLI("file.wsdl"); err != nil {
		log.Fatal(err)
	}
}

func ExampleIgnoreAttributes() {
	doc := xsdfile(`
	  <complexType name="ArrayOfString">
	    <any maxOccurs="unbounded" />
	    <attribute name="soapenc:arrayType" type="xs:string" />
	  </complexType>
	`)
	var cfg xsdgen.Config
	cfg.Option(xsdgen.IgnoreAttributes("arrayType"))

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// type ArrayOfString struct {
	// 	Items []string `xml:",any"`
	// }
}

func ExampleIgnoreElements() {
	doc := xsdfile(`
	  <complexType name="Person">
	    <sequence>
	      <element name="name" type="xs:string" />
	      <element name="deceased" type="soapenc:boolean" />
	      <element name="private" type="xs:int" />
	    </sequence>
	  </complexType>
	`)
	var cfg xsdgen.Config
	cfg.Option(
		xsdgen.IgnoreElements("private"),
		xsdgen.IgnoreAttributes("id", "href"))

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// type Person struct {
	// 	Name     string `xml:"http://www.example.com/ name"`
	// 	Deceased bool   `xml:"http://www.example.com/ deceased"`
	// }
}

func ExamplePackageName() {
	doc := xsdfile(`
	  <simpleType name="zipcode">
	    <restriction base="xs:string">
	      <length value="10" />
	    </restriction>
	  </simpleType>
	`)
	var cfg xsdgen.Config
	cfg.Option(xsdgen.PackageName("postal"))

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package postal
	//
	// // May be no more than 10 items long
	// type Zipcode string
}

func ExampleReplace() {
	doc := xsdfile(`
	  <complexType name="ArrayOfString">
	    <any maxOccurs="unbounded" />
	    <attribute name="soapenc:arrayType" type="xs:string" />
	  </complexType>
	`)
	var cfg xsdgen.Config
	cfg.Option(xsdgen.Replace("ArrayOf(.*)", "${1}Array"))

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// type StringArray struct {
	// 	ArrayType string   `xml:"arrayType,attr"`
	// 	Items     []string `xml:",any"`
	// }
}

func ExampleHandleSOAPArrayType() {
	doc := xsdfile(`
	  <complexType name="BoolArray">
	    <complexContent>
	      <restriction base="soapenc:Array">
	        <attribute ref="soapenc:arrayType" wsdl:arrayType="xs:boolean[]"/>
	      </restriction>
	    </complexContent>
	  </complexType>`)

	var cfg xsdgen.Config
	cfg.Option(xsdgen.HandleSOAPArrayType())

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// type BoolArray struct {
	// 	Offset ArrayCoordinate `xml:"offset,attr"`
	// 	Id     string          `xml:"id,attr"`
	// 	Href   string          `xml:"href,attr"`
	// 	Items  []bool          `xml:",any"`
	// }
}

func ExampleSOAPArrayAsSlice() {
	doc := xsdfile(`
	  <complexType name="BoolArray">
	    <complexContent>
	      <restriction base="soapenc:Array">
	        <attribute ref="soapenc:arrayType" wsdl:arrayType="xs:boolean[]"/>
	      </restriction>
	    </complexContent>
	  </complexType>`)

	var cfg xsdgen.Config
	cfg.Option(
		xsdgen.HandleSOAPArrayType(),
		xsdgen.SOAPArrayAsSlice(),
		xsdgen.LogOutput(log.New(os.Stderr, "", 0)),
		xsdgen.LogLevel(3),
		xsdgen.IgnoreAttributes("offset", "id", "href"))

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// import "encoding/xml"
	//
	// type BoolArray []bool
	//
	// func (a *BoolArray) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	// 	tag := xml.StartElement{Name: xml.Name{"", ",any"}}
	// 	for _, elt := range *a {
	// 		if err := e.EncodeElement(elt, tag); err != nil {
	// 			return err
	// 		}
	// 	}
	// 	return nil
	// }
	// func (a *BoolArray) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) {
	// 	var tok xml.Token
	// 	for tok, err = d.Token(); err == nil; tok, err = d.Token() {
	// 		if tok, ok := tok.(xml.StartElement); ok {
	// 			var item bool
	// 			if err = d.DecodeElement(&item, &tok); err == nil {
	// 				*a = append(*a, item)
	// 			}
	// 		}
	// 		if _, ok := tok.(xml.EndElement); ok {
	// 			break
	// 		}
	// 	}
	// 	return err
	// }
}

func ExampleUseFieldNames() {
	doc := xsdfile(`
	  <complexType name="library">
	    <sequence>
	      <element name="book" maxOccurs="unbounded">
	        <complexType>
	          <all>
	            <element name="title" type="xs:string" />
	            <element name="published" type="xs:date" />
	            <element name="author" type="xs:string" />
	          </all>
	        </complexType>
	      </element>
	    </sequence>
	  </complexType>`)

	var cfg xsdgen.Config
	cfg.Option(xsdgen.UseFieldNames())

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// import (
	// 	"bytes"
	// 	"time"
	// )
	//
	// type Book struct {
	// 	Title     string  `xml:"http://www.example.com/ title"`
	// 	Published xsdDate `xml:"http://www.example.com/ published"`
	// 	Author    string  `xml:"http://www.example.com/ author"`
	// }
	// type Library struct {
	// 	Book      []Book  `xml:"http://www.example.com/ book"`
	// 	Title     string  `xml:"http://www.example.com/ title"`
	// 	Published xsdDate `xml:"http://www.example.com/ published"`
	// 	Author    string  `xml:"http://www.example.com/ author"`
	// }
	// type xsdDate time.Time
	//
	// func (t *xsdDate) UnmarshalText(text []byte) error {
	// 	return _unmarshalTime(text, (*time.Time)(t), "2006-01-02")
	// }
	// func (t *xsdDate) MarshalText() ([]byte, error) {
	// 	return []byte((*time.Time)(t).Format("2006-01-02")), nil
	// }
	// func _unmarshalTime(text []byte, t *time.Time, format string) (err error) {
	// 	s := string(bytes.TrimSpace(text))
	// 	*t, err = time.Parse(format, s)
	// 	if _, ok := err.(*time.ParseError); ok {
	// 		*t, err = time.Parse(format+"Z07:00", s)
	// 	}
	// 	return err
	// }
}
<commit_msg>Add missing whitespace to make tests happy<commit_after>package xsdgen_test

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"aqwari.net/xml/xsdgen"
)

func tmpfile() *os.File {
	f, err := ioutil.TempFile("", "xsdgen_test")
	if err != nil {
		panic(err)
	}
	return f
}

func xsdfile(s string) (filename string) {
	file := tmpfile()
	defer file.Close()
	fmt.Fprintf(file, `
		<schema xmlns="http://www.w3.org/2001/XMLSchema"
		        xmlns:tns="http://www.example.com/"
		        xmlns:xs="http://www.w3.org/2001/XMLSchema"
		        xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
		        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
		        targetNamespace="http://www.example.com/">
		  %s
		</schema>
	`, s)
	return file.Name()
}

func ExampleConfig_GenCLI() {
	var cfg xsdgen.Config
	cfg.Option(
		xsdgen.IgnoreAttributes("id", "href", "offset"),
		xsdgen.IgnoreElements("comment"),
		xsdgen.PackageName("webapi"),
		xsdgen.Replace("_", ""),
		xsdgen.HandleSOAPArrayType(),
		xsdgen.SOAPArrayAsSlice(),
	)
	if err := cfg.GenCLI("webapi.xsd", "deps/soap11.xsd"); err != nil {
		log.Fatal(err)
	}
}

func ExampleLogOutput() {
	var cfg xsdgen.Config
	cfg.Option(
		xsdgen.LogOutput(log.New(os.Stderr, "", 0)),
		xsdgen.LogLevel(2))
	if err := cfg.GenCLI("file.wsdl"); err != nil {
		log.Fatal(err)
	}
}

func ExampleIgnoreAttributes() {
	doc := xsdfile(`
	  <complexType name="ArrayOfString">
	    <any maxOccurs="unbounded" />
	    <attribute name="soapenc:arrayType" type="xs:string" />
	  </complexType>
	`)
	var cfg xsdgen.Config
	cfg.Option(xsdgen.IgnoreAttributes("arrayType"))

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// type ArrayOfString struct {
	// 	Items []string `xml:",any"`
	// }
}

func ExampleIgnoreElements() {
	doc := xsdfile(`
	  <complexType name="Person">
	    <sequence>
	      <element name="name" type="xs:string" />
	      <element name="deceased" type="soapenc:boolean" />
	      <element name="private" type="xs:int" />
	    </sequence>
	  </complexType>
	`)
	var cfg xsdgen.Config
	cfg.Option(
		xsdgen.IgnoreElements("private"),
		xsdgen.IgnoreAttributes("id", "href"))

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// type Person struct {
	// 	Name     string `xml:"http://www.example.com/ name"`
	// 	Deceased bool   `xml:"http://www.example.com/ deceased"`
	// }
}

func ExamplePackageName() {
	doc := xsdfile(`
	  <simpleType name="zipcode">
	    <restriction base="xs:string">
	      <length value="10" />
	    </restriction>
	  </simpleType>
	`)
	var cfg xsdgen.Config
	cfg.Option(xsdgen.PackageName("postal"))

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package postal
	//
	// // May be no more than 10 items long
	// type Zipcode string
}

func ExampleReplace() {
	doc := xsdfile(`
	  <complexType name="ArrayOfString">
	    <any maxOccurs="unbounded" />
	    <attribute name="soapenc:arrayType" type="xs:string" />
	  </complexType>
	`)
	var cfg xsdgen.Config
	cfg.Option(xsdgen.Replace("ArrayOf(.*)", "${1}Array"))

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// type StringArray struct {
	// 	ArrayType string   `xml:"arrayType,attr"`
	// 	Items     []string `xml:",any"`
	// }
}

func ExampleHandleSOAPArrayType() {
	doc := xsdfile(`
	  <complexType name="BoolArray">
	    <complexContent>
	      <restriction base="soapenc:Array">
	        <attribute ref="soapenc:arrayType" wsdl:arrayType="xs:boolean[]"/>
	      </restriction>
	    </complexContent>
	  </complexType>`)

	var cfg xsdgen.Config
	cfg.Option(xsdgen.HandleSOAPArrayType())

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// type BoolArray struct {
	// 	Offset ArrayCoordinate `xml:"offset,attr"`
	// 	Id     string          `xml:"id,attr"`
	// 	Href   string          `xml:"href,attr"`
	// 	Items  []bool          `xml:",any"`
	// }
}

func ExampleSOAPArrayAsSlice() {
	doc := xsdfile(`
	  <complexType name="BoolArray">
	    <complexContent>
	      <restriction base="soapenc:Array">
	        <attribute ref="soapenc:arrayType" wsdl:arrayType="xs:boolean[]"/>
	      </restriction>
	    </complexContent>
	  </complexType>`)

	var cfg xsdgen.Config
	cfg.Option(
		xsdgen.HandleSOAPArrayType(),
		xsdgen.SOAPArrayAsSlice(),
		xsdgen.LogOutput(log.New(os.Stderr, "", 0)),
		xsdgen.LogLevel(3),
		xsdgen.IgnoreAttributes("offset", "id", "href"))

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// import "encoding/xml"
	//
	// type BoolArray []bool
	//
	// func (a *BoolArray) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	// 	tag := xml.StartElement{Name: xml.Name{"", ",any"}}
	// 	for _, elt := range *a {
	// 		if err := e.EncodeElement(elt, tag); err != nil {
	// 			return err
	// 		}
	// 	}
	// 	return nil
	// }
	// func (a *BoolArray) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) {
	// 	var tok xml.Token
	// 	for tok, err = d.Token(); err == nil; tok, err = d.Token() {
	// 		if tok, ok := tok.(xml.StartElement); ok {
	// 			var item bool
	// 			if err = d.DecodeElement(&item, &tok); err == nil {
	// 				*a = append(*a, item)
	// 			}
	// 		}
	// 		if _, ok := tok.(xml.EndElement); ok {
	// 			break
	// 		}
	// 	}
	// 	return err
	// }
}

func ExampleUseFieldNames() {
	doc := xsdfile(`
	  <complexType name="library">
	    <sequence>
	      <element name="book" maxOccurs="unbounded">
	        <complexType>
	          <all>
	            <element name="title" type="xs:string" />
	            <element name="published" type="xs:date" />
	            <element name="author" type="xs:string" />
	          </all>
	        </complexType>
	      </element>
	    </sequence>
	  </complexType>`)

	var cfg xsdgen.Config
	cfg.Option(xsdgen.UseFieldNames())

	out, err := cfg.GenSource(doc)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", out)

	// Output: package ws
	//
	// import (
	// 	"bytes"
	// 	"time"
	// )
	//
	// type Book struct {
	// 	Title     string  `xml:"http://www.example.com/ title"`
	// 	Published xsdDate `xml:"http://www.example.com/ published"`
	// 	Author    string  `xml:"http://www.example.com/ author"`
	// }
	//
	// type Library struct {
	// 	Book      []Book  `xml:"http://www.example.com/ book"`
	// 	Title     string  `xml:"http://www.example.com/ title"`
	// 	Published xsdDate `xml:"http://www.example.com/ published"`
	// 	Author    string  `xml:"http://www.example.com/ author"`
	// }
	//
	// type xsdDate time.Time
	//
	// func (t *xsdDate) UnmarshalText(text []byte) error {
	// 	return _unmarshalTime(text, (*time.Time)(t), "2006-01-02")
	// }
	// func (t *xsdDate) MarshalText() ([]byte, error) {
	// 	return []byte((*time.Time)(t).Format("2006-01-02")), nil
	// }
	// func _unmarshalTime(text []byte, t *time.Time, format string) (err error) {
	// 	s := string(bytes.TrimSpace(text))
	// 	*t, err = time.Parse(format, s)
	// 	if _, ok := err.(*time.ParseError); ok {
	// 		*t, err = time.Parse(format+"Z07:00", s)
	// 	}
	// 	return err
	// }
}
<|endoftext|>


<commit_before>package renderweb

import (
	"net/http"
	"time"

	"github.com/rs/xhandler"
	"golang.org/x/net/context"

	"tower.pro/renderer/components"
	"tower.pro/renderer/middlewares"
	"tower.pro/renderer/template"
)

type webOptions struct {
	alwaysHTML bool
	reqTimeout time.Duration
	defaultCtx template.Context

	middlewares       []middlewares.Handler
	componentSetter   middlewares.Handler
	templateCtxSetter middlewares.Handler
}

// Option - Sets web server handler options.
type Option func(*webOptions)

// WithComponentSetter - Sets component reader HTTP request middleware.
func WithComponentSetter(componentSetter middlewares.Handler) Option {
	return func(o *webOptions) {
		o.componentSetter = componentSetter
	}
}

// WithTemplateContextSetter - Sets component template context setter HTTP request middleware.
func WithTemplateContextSetter(templateCtxSetter middlewares.Handler) Option {
	return func(o *webOptions) {
		o.templateCtxSetter = templateCtxSetter
	}
}

// WithTimeout - Sets API server request timeout.
func WithTimeout(t time.Duration) Option {
	return func(o *webOptions) {
		o.reqTimeout = t
	}
}

// WithAlwaysHTML - Responds with html only when enabled.
func WithAlwaysHTML(enable ...bool) Option {
	return func(o *webOptions) {
		if len(enable) == 0 {
			o.alwaysHTML = true
		} else {
			o.alwaysHTML = enable[0]
		}
	}
}

// WithDefaultTemplateContext - Sets default template context.
func WithDefaultTemplateContext(ctx template.Context) Option {
	return func(o *webOptions) {
		o.defaultCtx = ctx
	}
}

// WithMiddleware - Adds a middleware.
func WithMiddleware(m middlewares.Handler) Option {
	return func(o *webOptions) {
		o.middlewares = append(o.middlewares, m)
	}
}

func defaultCtxSetter(o *webOptions) middlewares.Handler {
	return ToMiddleware(func(ctx context.Context, w http.ResponseWriter, r *http.Request, next xhandler.HandlerC) {
		if _, ok := components.TemplateContext(ctx); !ok && o.defaultCtx != nil {
			ctx = components.NewTemplateContext(ctx, o.defaultCtx.Clone())
		}
		next.ServeHTTPC(ctx, w, r)
	})
}
<commit_msg>renderweb: tracing options<commit_after>package renderweb

import (
	"net/http"
	"time"

	"github.com/rs/xhandler"
	"golang.org/x/net/context"

	"tower.pro/renderer/components"
	"tower.pro/renderer/middlewares"
	"tower.pro/renderer/template"
)

type webOptions struct {
	tracing    bool
	alwaysHTML bool
	reqTimeout time.Duration
	defaultCtx template.Context

	middlewares       []middlewares.Handler
	componentSetter   middlewares.Handler
	templateCtxSetter middlewares.Handler
}

// Option - Sets web server handler options.
type Option func(*webOptions)

func constructOpts(opts ...Option) *webOptions {
	o := &webOptions{
		reqTimeout:      time.Second * 15,
		componentSetter: UnmarshalFromRequest,
	}
	o.templateCtxSetter = defaultCtxSetter(o)
	for _, opt := range opts {
		opt(o)
	}
	return o
}

// WithTracing - Enables tracing. Uses first parameter if any.
func WithTracing(enable ...bool) Option {
	return func(o *webOptions) {
		if len(enable) == 0 {
			o.tracing = true
		} else {
			o.tracing = enable[0]
		}
	}
}

// WithComponentSetter - Sets component reader HTTP request middleware.
func WithComponentSetter(componentSetter middlewares.Handler) Option {
	return func(o *webOptions) {
		o.componentSetter = componentSetter
	}
}

// WithTemplateContextSetter - Sets component template context setter HTTP request middleware.
func WithTemplateContextSetter(templateCtxSetter middlewares.Handler) Option {
	return func(o *webOptions) {
		o.templateCtxSetter = templateCtxSetter
	}
}

// WithTimeout - Sets API server request timeout.
func WithTimeout(t time.Duration) Option {
	return func(o *webOptions) {
		o.reqTimeout = t
	}
}

// WithAlwaysHTML - Responds with html only when enabled. Uses first parameter if any.
func WithAlwaysHTML(enable ...bool) Option {
	return func(o *webOptions) {
		if len(enable) == 0 {
			o.alwaysHTML = true
		} else {
			o.alwaysHTML = enable[0]
		}
	}
}

// WithDefaultTemplateContext - Sets default template context.
func WithDefaultTemplateContext(ctx template.Context) Option {
	return func(o *webOptions) {
		o.defaultCtx = ctx
	}
}

// WithMiddleware - Adds a middleware.
func WithMiddleware(m middlewares.Handler) Option {
	return func(o *webOptions) {
		o.middlewares = append(o.middlewares, m)
	}
}

func defaultCtxSetter(o *webOptions) middlewares.Handler {
	return ToMiddleware(func(ctx context.Context, w http.ResponseWriter, r *http.Request, next xhandler.HandlerC) {
		if _, ok := components.TemplateContext(ctx); !ok && o.defaultCtx != nil {
			ctx = components.NewTemplateContext(ctx, o.defaultCtx.Clone())
		}
		next.ServeHTTPC(ctx, w, r)
	})
}
<|endoftext|>