<commit_before>// Copyright 2015 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 etcdmain

import (
	"strconv"

	"github.com/coreos/etcd/embed"
)

var (
	usageline = `usage: etcd [flags]
       start an etcd server

       etcd --version
       show the version of etcd

       etcd -h | --help
       show the help information about etcd

       etcd --config-file
       path to the server configuration file

       etcd gateway
       run the stateless pass-through etcd TCP connection forwarding proxy

       etcd grpc-proxy
       run the stateless etcd v3 gRPC L7 reverse proxy
	`
	flagsline = `
member flags:
	--name 'default'
		human-readable name for this member.
	--data-dir '${name}.etcd'
		path to the data directory.
	--wal-dir ''
		path to the dedicated wal directory.
	--snapshot-count '100000'
		number of committed transactions to trigger a snapshot to disk.
	--heartbeat-interval '100'
		time (in milliseconds) of a heartbeat interval.
	--election-timeout '1000'
		time (in milliseconds) for an election to timeout. See tuning documentation for details.
	--listen-peer-urls 'http://localhost:2380'
		list of URLs to listen on for peer traffic.
	--listen-client-urls 'http://localhost:2379'
		list of URLs to listen on for client traffic.
	--max-snapshots '` + strconv.Itoa(embed.DefaultMaxSnapshots) + `'
		maximum number of snapshot files to retain (0 is unlimited).
	--max-wals '` + strconv.Itoa(embed.DefaultMaxWALs) + `'
		maximum number of wal files to retain (0 is unlimited).
	--quota-backend-bytes '0'
		raise alarms when backend size exceeds the given quota (0 defaults to low space quota).
	--max-txn-ops '128'
		maximum number of operations permitted in a transaction.
	--max-request-bytes '1572864'
		maximum client request size in bytes the server will accept.
	--grpc-keepalive-min-time '5s'
		minimum duration interval that a client should wait before pinging server.
	--grpc-keepalive-interval '2h'
		frequency duration of server-to-client ping to check if a connection is alive (0 to disable).
	--grpc-keepalive-timeout '20s'
		additional duration of wait before closing a non-responsive connection (0 to disable).

clustering flags:
	--initial-advertise-peer-urls 'http://localhost:2380'
		list of this member's peer URLs to advertise to the rest of the cluster.
	--initial-cluster 'default=http://localhost:2380'
		initial cluster configuration for bootstrapping.
	--initial-cluster-state 'new'
		initial cluster state ('new' or 'existing').
	--initial-cluster-token 'etcd-cluster'
		initial cluster token for the etcd cluster during bootstrap.
		Specifying this can protect you from unintended cross-cluster interaction when running multiple clusters.
	--advertise-client-urls 'http://localhost:2379'
		list of this member's client URLs to advertise to the public.
		The client URLs advertised should be accessible to machines that talk to etcd cluster. etcd client libraries parse these URLs to connect to the cluster.
	--discovery ''
		discovery URL used to bootstrap the cluster.
	--discovery-fallback 'proxy'
		expected behavior ('exit' or 'proxy') when discovery services fails.
		"proxy" supports v2 API only.
	--discovery-proxy ''
		HTTP proxy to use for traffic to discovery service.
	--discovery-srv ''
		dns srv domain used to bootstrap the cluster.
	--discovery-srv-name ''
		suffix to the dns srv name queried when bootstrapping.
	--strict-reconfig-check '` + strconv.FormatBool(embed.DefaultStrictReconfigCheck) + `'
		reject reconfiguration requests that would cause quorum loss.
	--pre-vote 'false'
		enable to run an additional Raft election phase.
	--auto-compaction-retention '0'
		auto compaction retention length. 0 means disable auto compaction.
	--auto-compaction-mode 'periodic'
		interpret 'auto-compaction-retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention.
	--enable-v2 '` + strconv.FormatBool(embed.DefaultEnableV2) + `'
		Accept etcd V2 client requests.

proxy flags (v2 API only):
	--proxy 'off'
		proxy mode setting ('off', 'readonly' or 'on').
	--proxy-failure-wait 5000
		time (in milliseconds) an endpoint will be held in a failed state.
	--proxy-refresh-interval 30000
		time (in milliseconds) of the endpoints refresh interval.
	--proxy-dial-timeout 1000
		time (in milliseconds) for a dial to timeout.
	--proxy-write-timeout 5000
		time (in milliseconds) for a write to timeout.
	--proxy-read-timeout 0
		time (in milliseconds) for a read to timeout.

security flags:
	--cert-file ''
		path to the client server TLS cert file.
	--key-file ''
		path to the client server TLS key file.
	--client-cert-auth 'false'
		enable client cert authentication.
	--client-crl-file ''
		path to the client certificate revocation list file.
	--trusted-ca-file ''
		path to the client server TLS trusted CA cert file.
	--auto-tls 'false'
		client TLS using generated certificates.
	--peer-cert-file ''
		path to the peer server TLS cert file.
	--peer-key-file ''
		path to the peer server TLS key file.
	--peer-client-cert-auth 'false'
		enable peer client cert authentication.
	--peer-trusted-ca-file ''
		path to the peer server TLS trusted CA file.
	--peer-auto-tls 'false'
		peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided.
	--peer-crl-file ''
		path to the peer certificate revocation list file.
	--cors '*'
		comma-separated whitelist of origins for CORS, or cross-origin resource sharing, (empty or * means allow all).
	--host-whitelist ''
		acceptable hostnames from HTTP client requests, if server is not secure (empty or * means allow all).

logging flags
	--debug 'false'
		enable debug-level logging for etcd.
	--log-package-levels ''
		specify a particular log level for each etcd package (eg: 'etcdmain=CRITICAL,etcdserver=DEBUG').
	--log-output 'default'
		specify 'stdout' or 'stderr' to skip journald logging even when running under systemd.

profiling flags:
	--enable-pprof 'false'
		Enable runtime profiling data via HTTP server. Address is at client URL + "/debug/pprof/"
	--metrics 'basic'
		Set level of detail for exported metrics, specify 'extensive' to include histogram metrics.
	--listen-metrics-urls ''
		List of URLs to listen on for metrics.

auth flags:
	--auth-token 'simple'
		Specify a v3 authentication token type and its options ('simple' or 'jwt').

experimental flags:
	--experimental-initial-corrupt-check 'false'
		enable to check data corruption before serving any client/peer traffic.
	--experimental-corrupt-check-time '0s'
		duration of time between cluster corruption check passes.
	--experimental-enable-v2v3 ''
		serve v2 requests through the v3 backend under a given prefix.

Please be CAUTIOUS when using unsafe flags because it will break the guarantees
given by the consensus protocol.

unsafe flags:
	--force-new-cluster 'false'
		force to create a new one-member cluster.
`
)
<commit_msg>etcdmain: default "--host-whitelist '*'"<commit_after>// Copyright 2015 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 etcdmain

import (
	"strconv"

	"github.com/coreos/etcd/embed"
)

var (
	usageline = `usage: etcd [flags]
       start an etcd server

       etcd --version
       show the version of etcd

       etcd -h | --help
       show the help information about etcd

       etcd --config-file
       path to the server configuration file

       etcd gateway
       run the stateless pass-through etcd TCP connection forwarding proxy

       etcd grpc-proxy
       run the stateless etcd v3 gRPC L7 reverse proxy
	`
	flagsline = `
member flags:
	--name 'default'
		human-readable name for this member.
	--data-dir '${name}.etcd'
		path to the data directory.
	--wal-dir ''
		path to the dedicated wal directory.
	--snapshot-count '100000'
		number of committed transactions to trigger a snapshot to disk.
	--heartbeat-interval '100'
		time (in milliseconds) of a heartbeat interval.
	--election-timeout '1000'
		time (in milliseconds) for an election to timeout. See tuning documentation for details.
	--listen-peer-urls 'http://localhost:2380'
		list of URLs to listen on for peer traffic.
	--listen-client-urls 'http://localhost:2379'
		list of URLs to listen on for client traffic.
	--max-snapshots '` + strconv.Itoa(embed.DefaultMaxSnapshots) + `'
		maximum number of snapshot files to retain (0 is unlimited).
	--max-wals '` + strconv.Itoa(embed.DefaultMaxWALs) + `'
		maximum number of wal files to retain (0 is unlimited).
	--quota-backend-bytes '0'
		raise alarms when backend size exceeds the given quota (0 defaults to low space quota).
	--max-txn-ops '128'
		maximum number of operations permitted in a transaction.
	--max-request-bytes '1572864'
		maximum client request size in bytes the server will accept.
	--grpc-keepalive-min-time '5s'
		minimum duration interval that a client should wait before pinging server.
	--grpc-keepalive-interval '2h'
		frequency duration of server-to-client ping to check if a connection is alive (0 to disable).
	--grpc-keepalive-timeout '20s'
		additional duration of wait before closing a non-responsive connection (0 to disable).

clustering flags:
	--initial-advertise-peer-urls 'http://localhost:2380'
		list of this member's peer URLs to advertise to the rest of the cluster.
	--initial-cluster 'default=http://localhost:2380'
		initial cluster configuration for bootstrapping.
	--initial-cluster-state 'new'
		initial cluster state ('new' or 'existing').
	--initial-cluster-token 'etcd-cluster'
		initial cluster token for the etcd cluster during bootstrap.
		Specifying this can protect you from unintended cross-cluster interaction when running multiple clusters.
	--advertise-client-urls 'http://localhost:2379'
		list of this member's client URLs to advertise to the public.
		The client URLs advertised should be accessible to machines that talk to etcd cluster. etcd client libraries parse these URLs to connect to the cluster.
	--discovery ''
		discovery URL used to bootstrap the cluster.
	--discovery-fallback 'proxy'
		expected behavior ('exit' or 'proxy') when discovery services fails.
		"proxy" supports v2 API only.
	--discovery-proxy ''
		HTTP proxy to use for traffic to discovery service.
	--discovery-srv ''
		dns srv domain used to bootstrap the cluster.
	--discovery-srv-name ''
		suffix to the dns srv name queried when bootstrapping.
	--strict-reconfig-check '` + strconv.FormatBool(embed.DefaultStrictReconfigCheck) + `'
		reject reconfiguration requests that would cause quorum loss.
	--pre-vote 'false'
		enable to run an additional Raft election phase.
	--auto-compaction-retention '0'
		auto compaction retention length. 0 means disable auto compaction.
	--auto-compaction-mode 'periodic'
		interpret 'auto-compaction-retention' one of: periodic|revision. 'periodic' for duration based retention, defaulting to hours if no time unit is provided (e.g. '5m'). 'revision' for revision number based retention.
	--enable-v2 '` + strconv.FormatBool(embed.DefaultEnableV2) + `'
		Accept etcd V2 client requests.

proxy flags (v2 API only):
	--proxy 'off'
		proxy mode setting ('off', 'readonly' or 'on').
	--proxy-failure-wait 5000
		time (in milliseconds) an endpoint will be held in a failed state.
	--proxy-refresh-interval 30000
		time (in milliseconds) of the endpoints refresh interval.
	--proxy-dial-timeout 1000
		time (in milliseconds) for a dial to timeout.
	--proxy-write-timeout 5000
		time (in milliseconds) for a write to timeout.
	--proxy-read-timeout 0
		time (in milliseconds) for a read to timeout.

security flags:
	--cert-file ''
		path to the client server TLS cert file.
	--key-file ''
		path to the client server TLS key file.
	--client-cert-auth 'false'
		enable client cert authentication.
	--client-crl-file ''
		path to the client certificate revocation list file.
	--trusted-ca-file ''
		path to the client server TLS trusted CA cert file.
	--auto-tls 'false'
		client TLS using generated certificates.
	--peer-cert-file ''
		path to the peer server TLS cert file.
	--peer-key-file ''
		path to the peer server TLS key file.
	--peer-client-cert-auth 'false'
		enable peer client cert authentication.
	--peer-trusted-ca-file ''
		path to the peer server TLS trusted CA file.
	--peer-auto-tls 'false'
		peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided.
	--peer-crl-file ''
		path to the peer certificate revocation list file.
	--cors '*'
		comma-separated whitelist of origins for CORS, or cross-origin resource sharing, (empty or * means allow all).
	--host-whitelist '*'
		acceptable hostnames from HTTP client requests, if server is not secure (empty or * means allow all).

logging flags
	--debug 'false'
		enable debug-level logging for etcd.
	--log-package-levels ''
		specify a particular log level for each etcd package (eg: 'etcdmain=CRITICAL,etcdserver=DEBUG').
	--log-output 'default'
		specify 'stdout' or 'stderr' to skip journald logging even when running under systemd.

profiling flags:
	--enable-pprof 'false'
		Enable runtime profiling data via HTTP server. Address is at client URL + "/debug/pprof/"
	--metrics 'basic'
		Set level of detail for exported metrics, specify 'extensive' to include histogram metrics.
	--listen-metrics-urls ''
		List of URLs to listen on for metrics.

auth flags:
	--auth-token 'simple'
		Specify a v3 authentication token type and its options ('simple' or 'jwt').

experimental flags:
	--experimental-initial-corrupt-check 'false'
		enable to check data corruption before serving any client/peer traffic.
	--experimental-corrupt-check-time '0s'
		duration of time between cluster corruption check passes.
	--experimental-enable-v2v3 ''
		serve v2 requests through the v3 backend under a given prefix.

Please be CAUTIOUS when using unsafe flags because it will break the guarantees
given by the consensus protocol.

unsafe flags:
	--force-new-cluster 'false'
		force to create a new one-member cluster.
`
)
<|endoftext|>


<commit_before>package transforms

import (
	"github.com/azylman/getl"
)

// TableTransform returns a Table that has applies the given transform function to the output channel.
func TableTransform(transform func(getl.Row, chan<- getl.Row) error) getl.TransformFunc {
	return func(in <-chan getl.Row, out chan<- getl.Row) error {
		for row := range in {
			if err := transform(row, out); err != nil {
				return err
			}
		}
		return nil
	}
}

// Select returns a Table that only has Rows that pass the filter.
func Select(filter func(getl.Row) (bool, error)) getl.TransformFunc {
	return TableTransform(func(row getl.Row, out chan<- getl.Row) error {
		pass, err := filter(row)
		if err != nil || !pass {
			return err
		}
		out <- row
		return nil
	})
}

// Map returns a Table that applies a transform function to every row in the input table.
func Map(transform func(getl.Row) (getl.Row, error)) getl.TransformFunc {
	return TableTransform(func(in getl.Row, out chan<- getl.Row) error {
		row, err := transform(in)
		if err != nil {
			return err
		}
		out <- row
		return nil
	})
}

// Fieldmap returns a Table that has all the Rows of the input Table with the field mapping applied.
func Fieldmap(mappings map[string][]string) getl.TransformFunc {
	return Map(func(row getl.Row) (getl.Row, error) {
		newRow := getl.Row{}
		for key, vals := range mappings {
			for _, val := range vals {
				newRow[val] = row[key]
			}
		}
		return newRow, nil
	})
}

// Valuemap returns a Table that has all the Rows of the input Table with a value mapping applied.
func Valuemap(mappings map[string]map[interface{}]interface{}) getl.TransformFunc {
	return Map(func(row getl.Row) (getl.Row, error) {
		newRow := getl.Row{}
		for key, val := range row {
			if mappings[key] == nil || mappings[key][val] == nil {
				newRow[key] = val
				continue
			}
			newRow[key] = mappings[key][val]
		}
		return newRow, nil
	})
}
<commit_msg>add Each<commit_after>package transforms

import (
	"github.com/azylman/getl"
)

// TableTransform returns a Table that has applies the given transform function to the output channel.
func TableTransform(transform func(getl.Row, chan<- getl.Row) error) getl.TransformFunc {
	return func(in <-chan getl.Row, out chan<- getl.Row) error {
		for row := range in {
			if err := transform(row, out); err != nil {
				return err
			}
		}
		return nil
	}
}

// Select returns a Table that only has Rows that pass the filter.
func Select(filter func(getl.Row) (bool, error)) getl.TransformFunc {
	return TableTransform(func(row getl.Row, out chan<- getl.Row) error {
		pass, err := filter(row)
		if err != nil || !pass {
			return err
		}
		out <- row
		return nil
	})
}

// Map returns a Table that applies a transform function to every row in the input table.
func Map(transform func(getl.Row) (getl.Row, error)) getl.TransformFunc {
	return TableTransform(func(in getl.Row, out chan<- getl.Row) error {
		row, err := transform(in)
		if err != nil {
			return err
		}
		out <- row
		return nil
	})
}

// Each returns a Table that passes through all the Rows from the source table, invoking a function
// for each.
func Each(fn func(getl.Row) error) {
	return Map(func(row getl.Row) (getl.Row, error) {
		if err := fn(row); err != nil {
			return err
		}
		return row
	})
}

// Fieldmap returns a Table that has all the Rows of the input Table with the field mapping applied.
func Fieldmap(mappings map[string][]string) getl.TransformFunc {
	return Map(func(row getl.Row) (getl.Row, error) {
		newRow := getl.Row{}
		for key, vals := range mappings {
			for _, val := range vals {
				newRow[val] = row[key]
			}
		}
		return newRow, nil
	})
}

// Valuemap returns a Table that has all the Rows of the input Table with a value mapping applied.
func Valuemap(mappings map[string]map[interface{}]interface{}) getl.TransformFunc {
	return Map(func(row getl.Row) (getl.Row, error) {
		newRow := getl.Row{}
		for key, val := range row {
			if mappings[key] == nil || mappings[key][val] == nil {
				newRow[key] = val
				continue
			}
			newRow[key] = mappings[key][val]
		}
		return newRow, nil
	})
}
<|endoftext|>


<commit_before>package http

import (
	"bytes"
	"context"
	"fmt"
	"github.com/cloudevents/sdk-go/pkg/cloudevents/observability"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/cloudevents/sdk-go/pkg/cloudevents"
	cecontext "github.com/cloudevents/sdk-go/pkg/cloudevents/context"
	"github.com/cloudevents/sdk-go/pkg/cloudevents/transport"
)

type EncodingSelector func(e cloudevents.Event) Encoding

// Transport adheres to transport.Transport.
var _ transport.Transport = (*Transport)(nil)

const (
	// DefaultShutdownTimeout defines the default timeout given to the http.Server when calling Shutdown.
	DefaultShutdownTimeout = time.Minute * 1
)

// Transport acts as both a http client and a http handler.
type Transport struct {
	// The encoding used to select the codec for outbound events.
	Encoding Encoding
	// DefaultEncodingSelectionFn allows for other encoding selection strategies to be injected.
	DefaultEncodingSelectionFn EncodingSelector

	// ShutdownTimeout defines the timeout given to the http.Server when calling Shutdown.
	// If nil, DefaultShutdownTimeout is used.
	ShutdownTimeout *time.Duration

	// Sending

	// Client is the http client that will be used to send requests.
	// If nil, the Transport will create a one.
	Client *http.Client
	// Req is the base http request that is used for http.Do.
	// Only .Method, .URL, and .Header is considered.
	// If not set, Req.Method defaults to POST.
	// Req.URL or context.WithTarget(url) are required for sending.
	Req *http.Request

	// Receiving

	// Receiver is invoked target for incoming events.
	Receiver transport.Receiver
	// Port is the port to bind the receiver to. Defaults to 8080.
	Port *int
	// Path is the path to bind the receiver to. Defaults to "/".
	Path string
	// Handler is the handler the http Server will use. Use this to reuse the
	// http server. If nil, the Transport will create a one.
	Handler *http.ServeMux

	realPort          int
	server            *http.Server
	handlerRegistered bool
	codec             transport.Codec
	// Create Mutex
	crMu sync.Mutex
	// Receive Mutex
	reMu sync.Mutex
}

func New(opts ...Option) (*Transport, error) {
	t := &Transport{
		Req: &http.Request{
			Method: http.MethodPost,
		},
	}
	if err := t.applyOptions(opts...); err != nil {
		return nil, err
	}
	return t, nil
}

func (t *Transport) applyOptions(opts ...Option) error {
	for _, fn := range opts {
		if err := fn(t); err != nil {
			return err
		}
	}
	return nil
}

func (t *Transport) loadCodec() bool {
	if t.codec == nil {
		t.crMu.Lock()
		if t.DefaultEncodingSelectionFn != nil && t.Encoding != Default {
			log.Printf("[warn] Transport has a DefaultEncodingSelectionFn set but Encoding is not Default. DefaultEncodingSelectionFn will be ignored.")
		}
		t.codec = &Codec{
			Encoding:                   t.Encoding,
			DefaultEncodingSelectionFn: t.DefaultEncodingSelectionFn,
		}
		t.crMu.Unlock()
	}
	return true
}

func copyHeaders(from, to http.Header) {
	if from == nil || to == nil {
		return
	}
	for header, values := range from {
		for _, value := range values {
			to.Add(header, value)
		}
	}
}

// Send implements Transport.Send
func (t *Transport) Send(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, error) {
	ctx, r := observability.NewReporter(ctx, reportSend)
	resp, err := t.obsSend(ctx, event)
	if err != nil {
		r.Error()
	} else {
		r.OK()
	}
	return resp, err
}

func (t *Transport) obsSend(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, error) {
	if t.Client == nil {
		t.crMu.Lock()
		t.Client = &http.Client{}
		t.crMu.Unlock()
	}

	req := http.Request{
		Header: HeaderFrom(ctx),
	}
	if t.Req != nil {
		req.Method = t.Req.Method
		req.URL = t.Req.URL
		copyHeaders(t.Req.Header, req.Header)
	}

	// Override the default request with target from context.
	if target := cecontext.TargetFrom(ctx); target != nil {
		req.URL = target
	}

	if ok := t.loadCodec(); !ok {
		return nil, fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
	}

	msg, err := t.codec.Encode(event)
	if err != nil {
		return nil, err
	}

	if m, ok := msg.(*Message); ok {
		copyHeaders(m.Header, req.Header)
		req.Body = ioutil.NopCloser(bytes.NewBuffer(m.Body))
		req.ContentLength = int64(len(m.Body))
		req.Close = true
		return httpDo(ctx, t.Client, &req, func(resp *http.Response, err error) (*cloudevents.Event, error) {
			if err != nil {
				return nil, err
			}
			defer resp.Body.Close()

			body, _ := ioutil.ReadAll(resp.Body)
			msg := &Message{
				Header: resp.Header,
				Body:   body,
			}

			var respEvent *cloudevents.Event
			if msg.CloudEventsVersion() != "" {
				if ok := t.loadCodec(); !ok {
					err := fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
					log.Printf("failed to load codec: %s", err)
				}
				if respEvent, err = t.codec.Decode(msg); err != nil {
					log.Printf("failed to decode message: %s %v", err, resp)
				}
			}

			if accepted(resp) {
				return respEvent, nil
			}
			return respEvent, fmt.Errorf("error sending cloudevent: %s", status(resp))
		})
	}
	return nil, fmt.Errorf("failed to encode Event into a Message")
}

// SetReceiver implements Transport.SetReceiver
func (t *Transport) SetReceiver(r transport.Receiver) {
	t.Receiver = r
}

// StartReceiver implements Transport.StartReceiver
// NOTE: This is a blocking call.
func (t *Transport) StartReceiver(ctx context.Context) error {
	t.reMu.Lock()
	defer t.reMu.Unlock()

	if t.Handler == nil {
		t.Handler = http.NewServeMux()
	}
	if !t.handlerRegistered {
		// handler.Handle might panic if the user tries to use the same path as the sdk.
		t.Handler.Handle(t.GetPath(), t)
		t.handlerRegistered = true
	}

	addr := fmt.Sprintf(":%d", t.GetPort())
	t.server = &http.Server{
		Addr:    addr,
		Handler: t.Handler,
	}

	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	t.realPort = listener.Addr().(*net.TCPAddr).Port

	// Shutdown
	defer func() {
		t.realPort = 0
		t.server.Close()
		t.server = nil
	}()

	errChan := make(chan error, 1)
	go func() {
		errChan <- t.server.Serve(listener)
	}()

	// wait for the server to return or ctx.Done().
	select {
	case <-ctx.Done():
		// Try a gracefully shutdown.
		timeout := DefaultShutdownTimeout
		if t.ShutdownTimeout != nil {
			timeout = *t.ShutdownTimeout
		}
		ctx, cancel := context.WithTimeout(context.Background(), timeout)
		defer cancel()
		return t.server.Shutdown(ctx)
	case err := <-errChan:
		return err
	}
}

type eventError struct {
	event *cloudevents.Event
	err   error
}

func httpDo(ctx context.Context, client *http.Client, req *http.Request, fn func(*http.Response, error) (*cloudevents.Event, error)) (*cloudevents.Event, error) {
	// Run the HTTP request in a goroutine and pass the response to fn.
	c := make(chan eventError, 1)
	req = req.WithContext(ctx)
	go func() {
		event, err := fn(client.Do(req))
		c <- eventError{event: event, err: err}
	}()
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case ee := <-c:
		return ee.event, ee.err
	}
}

// accepted is a helper method to understand if the response from the target
// accepted the CloudEvent.
func accepted(resp *http.Response) bool {
	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return true
	}
	return false
}

// status is a helper method to read the response of the target.
func status(resp *http.Response) string {
	status := resp.Status
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Sprintf("Status[%s] error reading response body: %v", status, err)
	}
	return fmt.Sprintf("Status[%s] %s", status, body)
}

func (t *Transport) invokeReceiver(ctx context.Context, event cloudevents.Event) (*Response, error) {
	ctx, r := observability.NewReporter(ctx, reportReceive)
	resp, err := t.obsInvokeReceiver(ctx, event)
	if err != nil {
		r.Error()
	} else {
		r.OK()
	}
	return resp, err
}

func (t *Transport) obsInvokeReceiver(ctx context.Context, event cloudevents.Event) (*Response, error) {
	if t.Receiver != nil {
		// Note: http does not use eventResp.Reason
		eventResp := cloudevents.EventResponse{}
		resp := Response{}

		err := t.Receiver.Receive(ctx, event, &eventResp)
		if err != nil {
			log.Printf("got an error from receiver fn: %s", err.Error())
			resp.StatusCode = http.StatusInternalServerError
			return &resp, err
		}

		if eventResp.Event != nil {
			if t.loadCodec() {
				if m, err := t.codec.Encode(*eventResp.Event); err != nil {
					log.Printf("failed to encode response from receiver fn: %s", err.Error())
				} else if msg, ok := m.(*Message); ok {
					resp.Header = msg.Header
					resp.Body = msg.Body
				}
			} else {
				log.Printf("failed to load codec")
				resp.StatusCode = http.StatusInternalServerError
				return &resp, err
			}
			// Look for a transport response context
			var trx *TransportResponseContext
			if ptrTrx, ok := eventResp.Context.(*TransportResponseContext); ok {
				// found a *TransportResponseContext, use it.
				trx = ptrTrx
			} else if realTrx, ok := eventResp.Context.(TransportResponseContext); ok {
				// found a TransportResponseContext, make it a pointer.
				trx = &realTrx
			}
			// If we found a TransportResponseContext, use it.
			if trx != nil && trx.Header != nil && len(trx.Header) > 0 {
				copyHeaders(trx.Header, resp.Header)
			}
		}

		if eventResp.Status != 0 {
			resp.StatusCode = eventResp.Status
		} else {
			resp.StatusCode = http.StatusAccepted // default is 202 - Accepted
		}
		return &resp, err
	}
	return nil, nil
}

// ServeHTTP implements http.Handler
func (t *Transport) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx, r := observability.NewReporter(req.Context(), reportServeHTTP)

	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Printf("failed to handle request: %s %v", err, req)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"error":"Invalid request"}`))
		r.Error()
		return
	}
	msg := &Message{
		Header: req.Header,
		Body:   body,
	}

	if ok := t.loadCodec(); !ok {
		err := fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
		log.Printf("failed to load codec: %s", err)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		r.Error()
		return
	}
	event, err := t.codec.Decode(msg)
	if err != nil {
		log.Printf("failed to decode message: %s %v", err, req)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		r.Error()
		return
	}

	if req != nil {
		ctx = WithTransportContext(ctx, NewTransportContext(req))
	}

	resp, err := t.invokeReceiver(ctx, *event)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		r.Error()
		return
	}
	if resp != nil {
		if t.Req != nil {
			copyHeaders(t.Req.Header, w.Header())
		}
		if len(resp.Header) > 0 {
			copyHeaders(resp.Header, w.Header())
		}
		status := http.StatusAccepted
		if resp.StatusCode >= 200 && resp.StatusCode < 600 {
			status = resp.StatusCode
		}
		w.WriteHeader(status)
		if len(resp.Body) > 0 {
			if _, err := w.Write(resp.Body); err != nil {
				r.Error()
				return
			}
		}

		r.OK()
		return
	}

	w.WriteHeader(http.StatusNoContent)
	r.OK()
}

// GetPort returns the port the transport is active on.
// .Port can be set to 0, which means the transport selects a port, GetPort
// allows the transport to report back the selected port.
func (t *Transport) GetPort() int {
	if t.Port != nil && *t.Port == 0 && t.realPort != 0 {
		return t.realPort
	}

	if t.Port != nil && *t.Port >= 0 { // 0 means next open port
		return *t.Port
	}
	return 8080 // default
}

// GetPath returns the path the transport is hosted on. If the path is '/',
// the transport will handle requests on any URI. To discover the true path
// a request was received on, inspect the context from Receive(cxt, ...) with
// TransportContextFrom(ctx).
func (t *Transport) GetPath() string {
	path := strings.TrimSpace(t.Path)
	if len(path) > 0 {
		return path
	}
	return "/" // default
}
<commit_msg>Ensure formatting<commit_after>package http

import (
	"bytes"
	"context"
	"fmt"
	"github.com/cloudevents/sdk-go/pkg/cloudevents/observability"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/cloudevents/sdk-go/pkg/cloudevents"
	cecontext "github.com/cloudevents/sdk-go/pkg/cloudevents/context"
	"github.com/cloudevents/sdk-go/pkg/cloudevents/transport"
)

type EncodingSelector func(e cloudevents.Event) Encoding

// Transport adheres to transport.Transport.
var _ transport.Transport = (*Transport)(nil)

const (
	// DefaultShutdownTimeout defines the default timeout given to the http.Server when calling Shutdown.
	DefaultShutdownTimeout = time.Minute * 1
)

// Transport acts as both a http client and a http handler.
type Transport struct {
	// The encoding used to select the codec for outbound events.
	Encoding Encoding
	// DefaultEncodingSelectionFn allows for other encoding selection strategies to be injected.
	DefaultEncodingSelectionFn EncodingSelector

	// ShutdownTimeout defines the timeout given to the http.Server when calling Shutdown.
	// If nil, DefaultShutdownTimeout is used.
	ShutdownTimeout *time.Duration

	// Sending

	// Client is the http client that will be used to send requests.
	// If nil, the Transport will create a one.
	Client *http.Client
	// Req is the base http request that is used for http.Do.
	// Only .Method, .URL, and .Header is considered.
	// If not set, Req.Method defaults to POST.
	// Req.URL or context.WithTarget(url) are required for sending.
	Req *http.Request

	// Receiving

	// Receiver is invoked target for incoming events.
	Receiver transport.Receiver
	// Port is the port to bind the receiver to. Defaults to 8080.
	Port *int
	// Path is the path to bind the receiver to. Defaults to "/".
	Path string
	// Handler is the handler the http Server will use. Use this to reuse the
	// http server. If nil, the Transport will create a one.
	Handler *http.ServeMux

	realPort          int
	server            *http.Server
	handlerRegistered bool
	codec             transport.Codec
	// Create Mutex
	crMu sync.Mutex
	// Receive Mutex
	reMu sync.Mutex
}

func New(opts ...Option) (*Transport, error) {
	t := &Transport{
		Req: &http.Request{
			Method: http.MethodPost,
		},
	}
	if err := t.applyOptions(opts...); err != nil {
		return nil, err
	}
	return t, nil
}

func (t *Transport) applyOptions(opts ...Option) error {
	for _, fn := range opts {
		if err := fn(t); err != nil {
			return err
		}
	}
	return nil
}

func (t *Transport) loadCodec() bool {
	if t.codec == nil {
		t.crMu.Lock()
		if t.DefaultEncodingSelectionFn != nil && t.Encoding != Default {
			log.Printf("[warn] Transport has a DefaultEncodingSelectionFn set but Encoding is not Default. DefaultEncodingSelectionFn will be ignored.")
		}
		t.codec = &Codec{
			Encoding:                   t.Encoding,
			DefaultEncodingSelectionFn: t.DefaultEncodingSelectionFn,
		}
		t.crMu.Unlock()
	}
	return true
}

func copyHeaders(from, to http.Header) {
	if from == nil || to == nil {
		return
	}
	for header, values := range from {
		for _, value := range values {
			to.Add(header, value)
		}
	}
}

// Send implements Transport.Send
func (t *Transport) Send(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, error) {
	ctx, r := observability.NewReporter(ctx, reportSend)
	resp, err := t.obsSend(ctx, event)
	if err != nil {
		r.Error()
	} else {
		r.OK()
	}
	return resp, err
}

func (t *Transport) obsSend(ctx context.Context, event cloudevents.Event) (*cloudevents.Event, error) {
	if t.Client == nil {
		t.crMu.Lock()
		t.Client = &http.Client{}
		t.crMu.Unlock()
	}

	req := http.Request{
		Header: HeaderFrom(ctx),
	}
	if t.Req != nil {
		req.Method = t.Req.Method
		req.URL = t.Req.URL
		copyHeaders(t.Req.Header, req.Header)
	}

	// Override the default request with target from context.
	if target := cecontext.TargetFrom(ctx); target != nil {
		req.URL = target
	}

	if ok := t.loadCodec(); !ok {
		return nil, fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
	}

	msg, err := t.codec.Encode(event)
	if err != nil {
		return nil, err
	}

	if m, ok := msg.(*Message); ok {
		copyHeaders(m.Header, req.Header)
		req.Body = ioutil.NopCloser(bytes.NewBuffer(m.Body))
		req.ContentLength = int64(len(m.Body))
		req.Close = true
		return httpDo(ctx, t.Client, &req, func(resp *http.Response, err error) (*cloudevents.Event, error) {
			if err != nil {
				return nil, err
			}
			defer resp.Body.Close()

			body, _ := ioutil.ReadAll(resp.Body)
			msg := &Message{
				Header: resp.Header,
				Body:   body,
			}

			var respEvent *cloudevents.Event
			if msg.CloudEventsVersion() != "" {
				if ok := t.loadCodec(); !ok {
					err := fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
					log.Printf("failed to load codec: %s", err)
				}
				if respEvent, err = t.codec.Decode(msg); err != nil {
					log.Printf("failed to decode message: %s %v", err, resp)
				}
			}

			if accepted(resp) {
				return respEvent, nil
			}
			return respEvent, fmt.Errorf("error sending cloudevent: %s", status(resp))
		})
	}
	return nil, fmt.Errorf("failed to encode Event into a Message")
}

// SetReceiver implements Transport.SetReceiver
func (t *Transport) SetReceiver(r transport.Receiver) {
	t.Receiver = r
}

// StartReceiver implements Transport.StartReceiver
// NOTE: This is a blocking call.
func (t *Transport) StartReceiver(ctx context.Context) error {
	t.reMu.Lock()
	defer t.reMu.Unlock()

	if t.Handler == nil {
		t.Handler = http.NewServeMux()
	}
	if !t.handlerRegistered {
		// handler.Handle might panic if the user tries to use the same path as the sdk.
		t.Handler.Handle(t.GetPath(), t)
		t.handlerRegistered = true
	}

	addr := fmt.Sprintf(":%d", t.GetPort())
	t.server = &http.Server{
		Addr:    addr,
		Handler: t.Handler,
	}

	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	t.realPort = listener.Addr().(*net.TCPAddr).Port

	// Shutdown
	defer func() {
		t.realPort = 0
		t.server.Close()
		t.server = nil
	}()

	errChan := make(chan error, 1)
	go func() {
		errChan <- t.server.Serve(listener)
	}()

	// wait for the server to return or ctx.Done().
	select {
	case <-ctx.Done():
		// Try a gracefully shutdown.
		timeout := DefaultShutdownTimeout
		if t.ShutdownTimeout != nil {
			timeout = *t.ShutdownTimeout
		}
		ctx, cancel := context.WithTimeout(context.Background(), timeout)
		defer cancel()
		return t.server.Shutdown(ctx)
	case err := <-errChan:
		return err
	}
}

type eventError struct {
	event *cloudevents.Event
	err   error
}

func httpDo(ctx context.Context, client *http.Client, req *http.Request, fn func(*http.Response, error) (*cloudevents.Event, error)) (*cloudevents.Event, error) {
	// Run the HTTP request in a goroutine and pass the response to fn.
	c := make(chan eventError, 1)
	req = req.WithContext(ctx)
	go func() {
		event, err := fn(client.Do(req))
		c <- eventError{event: event, err: err}
	}()
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case ee := <-c:
		return ee.event, ee.err
	}
}

// accepted is a helper method to understand if the response from the target
// accepted the CloudEvent.
func accepted(resp *http.Response) bool {
	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return true
	}
	return false
}

// status is a helper method to read the response of the target.
func status(resp *http.Response) string {
	status := resp.Status
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Sprintf("Status[%s] error reading response body: %v", status, err)
	}
	return fmt.Sprintf("Status[%s] %s", status, body)
}

func (t *Transport) invokeReceiver(ctx context.Context, event cloudevents.Event) (*Response, error) {
	ctx, r := observability.NewReporter(ctx, reportReceive)
	resp, err := t.obsInvokeReceiver(ctx, event)
	if err != nil {
		r.Error()
	} else {
		r.OK()
	}
	return resp, err
}

func (t *Transport) obsInvokeReceiver(ctx context.Context, event cloudevents.Event) (*Response, error) {
	if t.Receiver != nil {
		// Note: http does not use eventResp.Reason
		eventResp := cloudevents.EventResponse{}
		resp := Response{}

		err := t.Receiver.Receive(ctx, event, &eventResp)
		if err != nil {
			log.Printf("got an error from receiver fn: %s", err.Error())
			resp.StatusCode = http.StatusInternalServerError
			return &resp, err
		}

		if eventResp.Event != nil {
			if t.loadCodec() {
				if m, err := t.codec.Encode(*eventResp.Event); err != nil {
					log.Printf("failed to encode response from receiver fn: %s", err.Error())
				} else if msg, ok := m.(*Message); ok {
					resp.Header = msg.Header
					resp.Body = msg.Body
				}
			} else {
				log.Printf("failed to load codec")
				resp.StatusCode = http.StatusInternalServerError
				return &resp, err
			}
			// Look for a transport response context
			var trx *TransportResponseContext
			if ptrTrx, ok := eventResp.Context.(*TransportResponseContext); ok {
				// found a *TransportResponseContext, use it.
				trx = ptrTrx
			} else if realTrx, ok := eventResp.Context.(TransportResponseContext); ok {
				// found a TransportResponseContext, make it a pointer.
				trx = &realTrx
			}
			// If we found a TransportResponseContext, use it.
			if trx != nil && trx.Header != nil && len(trx.Header) > 0 {
				copyHeaders(trx.Header, resp.Header)
			}
		}

		if eventResp.Status != 0 {
			resp.StatusCode = eventResp.Status
		} else {
			resp.StatusCode = http.StatusAccepted // default is 202 - Accepted
		}
		return &resp, err
	}
	return nil, nil
}

// ServeHTTP implements http.Handler
func (t *Transport) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	ctx, r := observability.NewReporter(req.Context(), reportServeHTTP)

	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Printf("failed to handle request: %s %v", err, req)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"error":"Invalid request"}`))
		r.Error()
		return
	}
	msg := &Message{
		Header: req.Header,
		Body:   body,
	}

	if ok := t.loadCodec(); !ok {
		err := fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
		log.Printf("failed to load codec: %s", err)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		r.Error()
		return
	}
	event, err := t.codec.Decode(msg)
	if err != nil {
		log.Printf("failed to decode message: %s %v", err, req)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		r.Error()
		return
	}

	if req != nil {
		ctx = WithTransportContext(ctx, NewTransportContext(req))
	}

	resp, err := t.invokeReceiver(ctx, *event)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
		r.Error()
		return
	}
	if resp != nil {
		if t.Req != nil {
			copyHeaders(t.Req.Header, w.Header())
		}
		if len(resp.Header) > 0 {
			copyHeaders(resp.Header, w.Header())
		}
		status := http.StatusAccepted
		if resp.StatusCode >= 200 && resp.StatusCode < 600 {
			status = resp.StatusCode
		}
		w.WriteHeader(status)
		if len(resp.Body) > 0 {
			if _, err := w.Write(resp.Body); err != nil {
				r.Error()
				return
			}
		}

		r.OK()
		return
	}

	w.WriteHeader(http.StatusNoContent)
	r.OK()
}

// GetPort returns the port the transport is active on.
// .Port can be set to 0, which means the transport selects a port, GetPort
// allows the transport to report back the selected port.
func (t *Transport) GetPort() int {
	if t.Port != nil && *t.Port == 0 && t.realPort != 0 {
		return t.realPort
	}

	if t.Port != nil && *t.Port >= 0 { // 0 means next open port
		return *t.Port
	}
	return 8080 // default
}

// GetPath returns the path the transport is hosted on. If the path is '/',
// the transport will handle requests on any URI. To discover the true path
// a request was received on, inspect the context from Receive(cxt, ...) with
// TransportContextFrom(ctx).
func (t *Transport) GetPath() string {
	path := strings.TrimSpace(t.Path)
	if len(path) > 0 {
		return path
	}
	return "/" // default
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package options

import (
	"github.com/spf13/pflag"

	utilflag "k8s.io/apiserver/pkg/util/flag"
)

// APIEnablementOptions contains the options for which resources to turn on and off.
// Given small aggregated API servers, this option isn't required for "normal" API servers
type APIEnablementOptions struct {
	RuntimeConfig utilflag.ConfigurationMap
}

func NewAPIEnablementOptions() *APIEnablementOptions {
	return &APIEnablementOptions{
		RuntimeConfig: make(utilflag.ConfigurationMap),
	}
}

// AddFlags adds flags for a specific APIServer to the specified FlagSet
func (s *APIEnablementOptions) AddFlags(fs *pflag.FlagSet) {
	fs.Var(&s.RuntimeConfig, "runtime-config", ""+
		"A set of key=value pairs that describe runtime configuration that may be passed "+
		"to apiserver. apis/<groupVersion> key can be used to turn on/off specific api versions. "+
		"apis/<groupVersion>/<resource> can be used to turn on/off specific resources. api/all and "+
		"api/legacy are special keys to control all and legacy api versions respectively.")
}
<commit_msg>kube-apiserver: fix runtime-config flag docs<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package options

import (
	"github.com/spf13/pflag"

	utilflag "k8s.io/apiserver/pkg/util/flag"
)

// APIEnablementOptions contains the options for which resources to turn on and off.
// Given small aggregated API servers, this option isn't required for "normal" API servers
type APIEnablementOptions struct {
	RuntimeConfig utilflag.ConfigurationMap
}

func NewAPIEnablementOptions() *APIEnablementOptions {
	return &APIEnablementOptions{
		RuntimeConfig: make(utilflag.ConfigurationMap),
	}
}

// AddFlags adds flags for a specific APIServer to the specified FlagSet
func (s *APIEnablementOptions) AddFlags(fs *pflag.FlagSet) {
	fs.Var(&s.RuntimeConfig, "runtime-config", ""+
		"A set of key=value pairs that describe runtime configuration that may be passed "+
		"to apiserver. <group>/<version> (or <version> for the core group) key can be used to "+
		"turn on/off specific api versions. <grouop>/<version>/<resource> (or <version>/<resource> "+
		"for the core group) can be used to turn on/off specific resources. api/all and "+
		"api/legacy are special keys to control all and legacy api versions respectively.")
}
<|endoftext|>


<commit_before>package event

import (
	"sync"
)

// TriggerBack is a version of Trigger which returns a channel that
// informs on when all bindables have been called and returned from
// the input event. It is dangerous to use this unless you have a
// very good idea how things will synchronize, as if a triggered
// bindable itself makes a TriggerBack call, this will cause the engine to freeze,
// as the function will never end because the first TriggerBack has control of
// the lock for the event bus, and the first TriggerBack won't give up that lock
// until the function ends.
//
// This inherently means that when you call Trigger, the event will almost
// almost never be immediately triggered but rather will be triggered sometime
// soon in the future.
//
// TriggerBack is right now used by the primary logic loop to dictate logical
// framerate, so EnterFrame events are called through TriggerBack.
func (eb *Bus) TriggerBack(eventName string, data interface{}) chan struct{} {
	ch := make(chan struct{})
	go func(ch chan struct{}, eb *Bus, eventName string, data interface{}) {
		eb.trigger(eventName, data)
		close(ch)
	}(ch, eb, eventName, data)
	return ch
}

// Trigger will scan through the event bus and call all bindables found attached
// to the given event, with the passed in data.
func (eb *Bus) Trigger(eventName string, data interface{}) {
	go func(eb *Bus, eventName string, data interface{}) {
		eb.trigger(eventName, data)
	}(eb, eventName, data)
}

// TriggerCIDBack acts like trigger back, but triggers for a specific cid only.
func (eb *Bus) TriggerCIDBack(cid CID, eventName string, data interface{}) chan struct{} {
	ch := make(chan struct{})
	go func() {
		eb.mutex.RLock()
		if idMap, ok := eb.bindingMap[eventName]; ok {
			if bs, ok := idMap[cid]; ok {
				eb.triggerDefault(bs.sl, cid, eventName, data)
			}
		}
		eb.mutex.RUnlock()
		close(ch)
	}()
	return ch
}

func (eb *Bus) trigger(eventName string, data interface{}) {
	eb.mutex.RLock()
	for id, bs := range eb.bindingMap[eventName] {
		if bs != nil {
			eb.triggerDefault(bs.sl, id, eventName, data)
		}
	}
	eb.mutex.RUnlock()
}

func (eb *Bus) triggerDefault(sl []Bindable, id CID, eventName string, data interface{}) {
	prog := &sync.WaitGroup{}
	prog.Add(len(sl))
	for i, bnd := range sl {
		if bnd == nil {
			prog.Done()
			continue
		}
		go func(bnd Bindable, id CID, eventName string, data interface{}, prog *sync.WaitGroup, index int) {
			eb.handleBindable(bnd, id, data, index, eventName)
			prog.Done()
		}(bnd, id, eventName, data, prog, i)
	}
	prog.Wait()
}

func (eb *Bus) handleBindable(bnd Bindable, id CID, data interface{}, index int, eventName string) {
	if id == 0 || eb.callerMap.HasEntity(id) {
		response := bnd(id, data)
		switch response {
		case UnbindEvent:
			UnbindAll(Event{
				Name:     eventName,
				CallerID: id,
			})
		case UnbindSingle:
			bnd := binding{
				Event: Event{
					Name:     eventName,
					CallerID: id,
				},
				index: index,
			}
			bnd.unbind(eb)
		}
	}
}
<commit_msg>event: remove useless nil check from trigger flow<commit_after>package event

import (
	"sync"
)

// TriggerBack is a version of Trigger which returns a channel that
// informs on when all bindables have been called and returned from
// the input event. It is dangerous to use this unless you have a
// very good idea how things will synchronize, as if a triggered
// bindable itself makes a TriggerBack call, this will cause the engine to freeze,
// as the function will never end because the first TriggerBack has control of
// the lock for the event bus, and the first TriggerBack won't give up that lock
// until the function ends.
//
// This inherently means that when you call Trigger, the event will almost
// almost never be immediately triggered but rather will be triggered sometime
// soon in the future.
//
// TriggerBack is right now used by the primary logic loop to dictate logical
// framerate, so EnterFrame events are called through TriggerBack.
func (eb *Bus) TriggerBack(eventName string, data interface{}) chan struct{} {
	ch := make(chan struct{})
	go func(ch chan struct{}, eb *Bus, eventName string, data interface{}) {
		eb.trigger(eventName, data)
		close(ch)
	}(ch, eb, eventName, data)
	return ch
}

// Trigger will scan through the event bus and call all bindables found attached
// to the given event, with the passed in data.
func (eb *Bus) Trigger(eventName string, data interface{}) {
	go func(eb *Bus, eventName string, data interface{}) {
		eb.trigger(eventName, data)
	}(eb, eventName, data)
}

// TriggerCIDBack acts like trigger back, but triggers for a specific cid only.
func (eb *Bus) TriggerCIDBack(cid CID, eventName string, data interface{}) chan struct{} {
	ch := make(chan struct{})
	go func() {
		eb.mutex.RLock()
		if idMap, ok := eb.bindingMap[eventName]; ok {
			if bs, ok := idMap[cid]; ok {
				eb.triggerDefault(bs.sl, cid, eventName, data)
			}
		}
		eb.mutex.RUnlock()
		close(ch)
	}()
	return ch
}

func (eb *Bus) trigger(eventName string, data interface{}) {
	eb.mutex.RLock()
	for id, bs := range eb.bindingMap[eventName] {
		eb.triggerDefault(bs.sl, id, eventName, data)
	}
	eb.mutex.RUnlock()
}

func (eb *Bus) triggerDefault(sl []Bindable, id CID, eventName string, data interface{}) {
	prog := &sync.WaitGroup{}
	prog.Add(len(sl))
	for i, bnd := range sl {
		if bnd == nil {
			prog.Done()
			continue
		}
		go func(bnd Bindable, id CID, eventName string, data interface{}, prog *sync.WaitGroup, index int) {
			eb.handleBindable(bnd, id, data, index, eventName)
			prog.Done()
		}(bnd, id, eventName, data, prog, i)
	}
	prog.Wait()
}

func (eb *Bus) handleBindable(bnd Bindable, id CID, data interface{}, index int, eventName string) {
	if id == 0 || eb.callerMap.HasEntity(id) {
		response := bnd(id, data)
		switch response {
		case UnbindEvent:
			UnbindAll(Event{
				Name:     eventName,
				CallerID: id,
			})
		case UnbindSingle:
			bnd := binding{
				Event: Event{
					Name:     eventName,
					CallerID: id,
				},
				index: index,
			}
			bnd.unbind(eb)
		}
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/giantswarm/microerror"
	"github.com/giantswarm/micrologger"
)

// Config represents the configuration used to create a new aws service.
type Config struct {
	// Dependencies.
	Clients Clients
	Logger  micrologger.Logger
}

// DefaultConfig provides a default configuration to create a new aws service
// by best effort.
func DefaultConfig() Config {
	return Config{
		// Dependencies.
		Clients: Clients{},
		Logger:  nil,
	}
}

// Service implements the aws service.
type Service struct {
	// Dependencies.
	clients Clients
	logger  micrologger.Logger
}

// New creates a new configured aws service.
func New(config Config) (*Service, error) {
	// Dependencies.
	if config.Logger == nil {
		return nil, microerror.Maskf(invalidConfigError, "config.Logger must not be empty")
	}

	newService := &Service{
		// Dependencies.
		clients: config.Clients,
		logger:  config.Logger,
	}

	return newService, nil
}

// GetAccountID gets the AWS Account ID.
func (s *Service) GetAccountID() (string, error) {
	resp, err := s.clients.STS.GetCallerIdentity(&sts.GetCallerIdentityInput{})
	if err != nil {
		return "", microerror.Mask(err)
	}
	userArn := *resp.Arn
	accountID := strings.Split(userArn, ":")[accountIDIndex]
	if err := ValidateAccountID(accountID); err != nil {
		return "", microerror.Mask(err)
	}

	return accountID, nil
}

// ValidateAccountID validates the AWS Account ID.
func ValidateAccountID(accountID string) error {
	r, _ := regexp.Compile("^[0-9]*$")

	switch {
	case accountID == "":
		return microerror.Mask(emptyAmazonAccountIDError)
	case len(accountID) != accountIDLength:
		return microerror.Mask(wrongAmazonAccountIDLengthError)
	case !r.MatchString(accountID):
		return microerror.Mask(malformedAmazonAccountIDError)
	}

	return nil
}

// GetKeyArn returns the key ARN associated with the given cluster ID.
func (s *Service) GetKeyArn(clusterID string) (string, error) {
	keyAlias := fmt.Sprintf("alias/%s", clusterID)
	input := &kms.DescribeKeyInput{
		KeyId: aws.String(keyAlias),
	}

	output, err := s.clients.KMS.DescribeKey(input)
	if err != nil {
		return "", microerror.Mask(err)
	}

	keyArn := *output.KeyMetadata.Arn

	return keyArn, nil
}
<commit_msg>add deprecation notice (#1706)<commit_after>// NOTE this package is deprecated and can be dropped once legacy/v23 is gone.
package aws

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/giantswarm/microerror"
	"github.com/giantswarm/micrologger"
)

// Config represents the configuration used to create a new aws service.
type Config struct {
	// Dependencies.
	Clients Clients
	Logger  micrologger.Logger
}

// DefaultConfig provides a default configuration to create a new aws service
// by best effort.
func DefaultConfig() Config {
	return Config{
		// Dependencies.
		Clients: Clients{},
		Logger:  nil,
	}
}

// Service implements the aws service.
type Service struct {
	// Dependencies.
	clients Clients
	logger  micrologger.Logger
}

// New creates a new configured aws service.
func New(config Config) (*Service, error) {
	// Dependencies.
	if config.Logger == nil {
		return nil, microerror.Maskf(invalidConfigError, "config.Logger must not be empty")
	}

	newService := &Service{
		// Dependencies.
		clients: config.Clients,
		logger:  config.Logger,
	}

	return newService, nil
}

// GetAccountID gets the AWS Account ID.
func (s *Service) GetAccountID() (string, error) {
	resp, err := s.clients.STS.GetCallerIdentity(&sts.GetCallerIdentityInput{})
	if err != nil {
		return "", microerror.Mask(err)
	}
	userArn := *resp.Arn
	accountID := strings.Split(userArn, ":")[accountIDIndex]
	if err := ValidateAccountID(accountID); err != nil {
		return "", microerror.Mask(err)
	}

	return accountID, nil
}

// ValidateAccountID validates the AWS Account ID.
func ValidateAccountID(accountID string) error {
	r, _ := regexp.Compile("^[0-9]*$")

	switch {
	case accountID == "":
		return microerror.Mask(emptyAmazonAccountIDError)
	case len(accountID) != accountIDLength:
		return microerror.Mask(wrongAmazonAccountIDLengthError)
	case !r.MatchString(accountID):
		return microerror.Mask(malformedAmazonAccountIDError)
	}

	return nil
}

// GetKeyArn returns the key ARN associated with the given cluster ID.
func (s *Service) GetKeyArn(clusterID string) (string, error) {
	keyAlias := fmt.Sprintf("alias/%s", clusterID)
	input := &kms.DescribeKeyInput{
		KeyId: aws.String(keyAlias),
	}

	output, err := s.clients.KMS.DescribeKey(input)
	if err != nil {
		return "", microerror.Mask(err)
	}

	keyArn := *output.KeyMetadata.Arn

	return keyArn, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/flynn/go-discoverd"
)

func main() {
	flag.Parse()
	name := flag.Arg(0)
	port := flag.Arg(1)
	host := flag.Arg(2)

	exit := make(chan os.Signal, 1)
	signal.Notify(exit, os.Interrupt, syscall.SIGTERM)
	var cleanup func()
	go func() {
		<-exit
		log.Println("Shutting down...")
		if cleanup != nil {
			cleanup()
		}
		os.Exit(0)
	}()

	client, err := discoverd.NewClient()
	if err != nil {
		log.Fatal("Error making client: ", err.Error())
	}
	if host != "" {
		client.RegisterWithHost(name, host, port, nil)
		cleanup = func() { client.UnregisterWithHost(name, host, port) }
	} else {
		client.Register(name, port, nil)
		cleanup = func() { client.Unregister(name, port) }
	}
	log.Printf("Registered %s on port %s.\n", name, port)

	set := client.Services(name)
	for {
		log.Println(strings.Join(set.OnlineAddrs(), ", "))
		time.Sleep(1 * time.Second)
	}
}
<commit_msg>discoverd/client: Merge pull request #3 from flynn/fix-example<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/flynn/go-discoverd"
)

func main() {
	flag.Parse()
	name := flag.Arg(0)
	addr := flag.Arg(1)

	client, err := discoverd.NewClient()
	if err != nil {
		log.Fatal("Error making client:", err)
	}
	if err = client.Register(name, addr); err != nil {
		log.Fatal("Error registering:", err)
	}
	log.Printf("Registered %s at %s.\n", name, addr)

	exit := make(chan os.Signal, 1)
	signal.Notify(exit, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-exit
		log.Println("Shutting down...")
		client.Unregister(name, addr)
		os.Exit(0)
	}()

	set, err := client.ServiceSet(name)
	if err != nil {
		log.Fatal("Error getting ServiceSet:", err)
	}
	for _ = range time.Tick(time.Second) {
		log.Println(strings.Join(set.Addrs(), ", "))
	}
}
<|endoftext|>


<commit_before>package rest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"

	"github.com/derekparker/delve/service"
	"github.com/derekparker/delve/service/api"
)

// Client is a REST service.Client.
type RESTClient struct {
	addr       string
	httpClient *http.Client
}

// Ensure the implementation satisfies the interface.
var _ service.Client = &RESTClient{}

// NewClient creates a new RESTClient.
func NewClient(addr string) *RESTClient {
	return &RESTClient{
		addr:       addr,
		httpClient: &http.Client{},
	}
}

func (c *RESTClient) Detach(killProcess bool) error {
	params := [][]string{{"kill", strconv.FormatBool(killProcess)}}
	err := c.doGET("/detach", nil, params...)
	if err != nil {
		return err
	}
	return nil
}

func (c *RESTClient) GetState() (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doGET("/state", &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) Continue() (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doPOST("/command", &api.DebuggerCommand{Name: api.Continue}, &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) Next() (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doPOST("/command", &api.DebuggerCommand{Name: api.Next}, &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) Step() (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doPOST("/command", &api.DebuggerCommand{Name: api.Step}, &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doPOST("/command", &api.DebuggerCommand{
		Name:     api.SwitchThread,
		ThreadID: threadID,
	}, &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) Halt() (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doPOST("/command", &api.DebuggerCommand{Name: api.Halt}, &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) GetBreakPoint(id int) (*api.BreakPoint, error) {
	var breakPoint *api.BreakPoint
	err := c.doGET(fmt.Sprintf("/breakpoints/%d", id), &breakPoint)
	if err != nil {
		return nil, err
	}
	return breakPoint, nil
}

func (c *RESTClient) CreateBreakPoint(breakPoint *api.BreakPoint) (*api.BreakPoint, error) {
	var newBreakPoint *api.BreakPoint
	err := c.doPOST("/breakpoints", breakPoint, &newBreakPoint)
	if err != nil {
		return nil, err
	}
	return newBreakPoint, nil
}

func (c *RESTClient) ListBreakPoints() ([]*api.BreakPoint, error) {
	var breakPoints []*api.BreakPoint
	err := c.doGET("/breakpoints", &breakPoints)
	if err != nil {
		return nil, err
	}
	return breakPoints, nil
}

func (c *RESTClient) ClearBreakPoint(id int) (*api.BreakPoint, error) {
	var breakPoint *api.BreakPoint
	err := c.doDELETE(fmt.Sprintf("/breakpoints/%d", id), &breakPoint)
	if err != nil {
		return nil, err
	}
	return breakPoint, nil
}

func (c *RESTClient) ListThreads() ([]*api.Thread, error) {
	var threads []*api.Thread
	err := c.doGET("/threads", &threads)
	if err != nil {
		return nil, err
	}
	return threads, nil
}

func (c *RESTClient) GetThread(id int) (*api.Thread, error) {
	var thread *api.Thread
	err := c.doGET(fmt.Sprintf("/threads/%d", id), &thread)
	if err != nil {
		return nil, err
	}
	return thread, nil
}

func (c *RESTClient) EvalSymbol(symbol string) (*api.Variable, error) {
	var v *api.Variable
	err := c.doGET(fmt.Sprintf("/eval/%s", symbol), &v)
	if err != nil {
		return nil, err
	}
	return v, nil
}

func (c *RESTClient) EvalSymbolFor(threadID int, symbol string) (*api.Variable, error) {
	var v *api.Variable
	err := c.doGET(fmt.Sprintf("/threads/%d/eval/%s", threadID, symbol), &v)
	if err != nil {
		return nil, err
	}
	return v, nil
}

func (c *RESTClient) ListSources(filter string) ([]string, error) {
	params := [][]string{}
	if len(filter) > 0 {
		params = append(params, []string{"filter", filter})
	}
	var sources []string
	err := c.doGET("/sources", &sources, params...)
	if err != nil {
		return nil, err
	}
	return sources, nil
}

func (c *RESTClient) ListFunctions(filter string) ([]string, error) {
	params := [][]string{}
	if len(filter) > 0 {
		params = append(params, []string{"filter", filter})
	}
	var funcs []string
	err := c.doGET("/functions", &funcs, params...)
	if err != nil {
		return nil, err
	}
	return funcs, nil
}

func (c *RESTClient) ListPackageVariables(filter string) ([]api.Variable, error) {
	params := [][]string{}
	if len(filter) > 0 {
		params = append(params, []string{"filter", filter})
	}
	var vars []api.Variable
	err := c.doGET(fmt.Sprintf("/vars"), &vars, params...)
	if err != nil {
		return nil, err
	}
	return vars, nil
}

func (c *RESTClient) ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) {
	params := [][]string{}
	if len(filter) > 0 {
		params = append(params, []string{"filter", filter})
	}
	var vars []api.Variable
	err := c.doGET(fmt.Sprintf("/threads/%d/vars", threadID), &vars, params...)
	if err != nil {
		return nil, err
	}
	return vars, nil
}

func (c *RESTClient) ListGoroutines() ([]*api.Goroutine, error) {
	var goroutines []*api.Goroutine
	err := c.doGET("/goroutines", &goroutines)
	if err != nil {
		return nil, err
	}
	return goroutines, nil
}

// TODO: how do we use http.Client with a UNIX socket URI?
func (c *RESTClient) url(path string) string {
	return fmt.Sprintf("http://%s%s", c.addr, path)
}

// doGET performs an HTTP GET to path and stores the resulting API object in
// obj. Query parameters are passed as an array of 2-element string arrays
// representing key-value pairs.
func (c *RESTClient) doGET(path string, obj interface{}, params ...[]string) error {
	url, err := url.Parse(c.url(path))
	if err != nil {
		return err
	}

	// Add any supplied query parameters to the URL
	q := url.Query()
	for _, p := range params {
		q.Set(p[0], p[1])
	}
	url.RawQuery = q.Encode()

	// Create the request
	req, err := http.NewRequest("GET", url.String(), nil)
	if err != nil {
		return err
	}
	req.Header.Set("Accept", "application/json")

	// Execute the request
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Extract error text and return
	if resp.StatusCode != http.StatusOK {
		contents, _ := ioutil.ReadAll(resp.Body)
		return fmt.Errorf("%s: %s", resp.Status, contents)
	}

	// Decode result object
	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(&obj)
	if err != nil {
		return err
	}
	return nil
}

// doPOST performs an HTTP POST to path, sending 'out' as the body and storing
// the resulting API object to 'in'.
func (c *RESTClient) doPOST(path string, out interface{}, in interface{}) error {
	jsonString, err := json.Marshal(out)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", c.url(path), bytes.NewBuffer(jsonString))
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusCreated {
		contents, _ := ioutil.ReadAll(resp.Body)
		return fmt.Errorf("%s: %s", resp.Status, contents)
	}

	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(&in)
	if err != nil {
		return err
	}
	return nil
}

// doDELETE performs an HTTP DELETE to path, storing the resulting API object
// to 'obj'.
func (c *RESTClient) doDELETE(path string, obj interface{}) error {
	req, err := http.NewRequest("DELETE", c.url(path), nil)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		contents, _ := ioutil.ReadAll(resp.Body)
		return fmt.Errorf("%s: %s", resp.Status, contents)
	}

	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(&obj)
	if err != nil {
		return err
	}
	return nil
}

// doPUT performs an HTTP PUT to path, sending 'out' as the body and storing
// the resulting API object to 'in'.
func (c *RESTClient) doPUT(path string, out interface{}, in interface{}) error {
	jsonString, err := json.Marshal(out)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", c.url(path), bytes.NewBuffer(jsonString))
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		contents, _ := ioutil.ReadAll(resp.Body)
		return fmt.Errorf("%s: %s", resp.Status, contents)
	}

	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(&in)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Suppress output of HTTP error from client<commit_after>package rest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"

	"github.com/derekparker/delve/service"
	"github.com/derekparker/delve/service/api"
)

// Client is a REST service.Client.
type RESTClient struct {
	addr       string
	httpClient *http.Client
}

// ClientError is an error from the debugger.
type ClientError struct {
	// Message is the specific error from the debugger.
	Message string
	// Status is the HTTP error from the debugger request.
	Status string
}

func (e *ClientError) Error() string { return e.Message }

// Ensure the implementation satisfies the interface.
var _ service.Client = &RESTClient{}

// NewClient creates a new RESTClient.
func NewClient(addr string) *RESTClient {
	return &RESTClient{
		addr:       addr,
		httpClient: &http.Client{},
	}
}

func (c *RESTClient) Detach(killProcess bool) error {
	params := [][]string{{"kill", strconv.FormatBool(killProcess)}}
	err := c.doGET("/detach", nil, params...)
	if err != nil {
		return err
	}
	return nil
}

func (c *RESTClient) GetState() (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doGET("/state", &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) Continue() (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doPOST("/command", &api.DebuggerCommand{Name: api.Continue}, &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) Next() (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doPOST("/command", &api.DebuggerCommand{Name: api.Next}, &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) Step() (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doPOST("/command", &api.DebuggerCommand{Name: api.Step}, &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doPOST("/command", &api.DebuggerCommand{
		Name:     api.SwitchThread,
		ThreadID: threadID,
	}, &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) Halt() (*api.DebuggerState, error) {
	var state *api.DebuggerState
	err := c.doPOST("/command", &api.DebuggerCommand{Name: api.Halt}, &state)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func (c *RESTClient) GetBreakPoint(id int) (*api.BreakPoint, error) {
	var breakPoint *api.BreakPoint
	err := c.doGET(fmt.Sprintf("/breakpoints/%d", id), &breakPoint)
	if err != nil {
		return nil, err
	}
	return breakPoint, nil
}

func (c *RESTClient) CreateBreakPoint(breakPoint *api.BreakPoint) (*api.BreakPoint, error) {
	var newBreakPoint *api.BreakPoint
	err := c.doPOST("/breakpoints", breakPoint, &newBreakPoint)
	if err != nil {
		return nil, err
	}
	return newBreakPoint, nil
}

func (c *RESTClient) ListBreakPoints() ([]*api.BreakPoint, error) {
	var breakPoints []*api.BreakPoint
	err := c.doGET("/breakpoints", &breakPoints)
	if err != nil {
		return nil, err
	}
	return breakPoints, nil
}

func (c *RESTClient) ClearBreakPoint(id int) (*api.BreakPoint, error) {
	var breakPoint *api.BreakPoint
	err := c.doDELETE(fmt.Sprintf("/breakpoints/%d", id), &breakPoint)
	if err != nil {
		return nil, err
	}
	return breakPoint, nil
}

func (c *RESTClient) ListThreads() ([]*api.Thread, error) {
	var threads []*api.Thread
	err := c.doGET("/threads", &threads)
	if err != nil {
		return nil, err
	}
	return threads, nil
}

func (c *RESTClient) GetThread(id int) (*api.Thread, error) {
	var thread *api.Thread
	err := c.doGET(fmt.Sprintf("/threads/%d", id), &thread)
	if err != nil {
		return nil, err
	}
	return thread, nil
}

func (c *RESTClient) EvalSymbol(symbol string) (*api.Variable, error) {
	var v *api.Variable
	err := c.doGET(fmt.Sprintf("/eval/%s", symbol), &v)
	if err != nil {
		return nil, err
	}
	return v, nil
}

func (c *RESTClient) EvalSymbolFor(threadID int, symbol string) (*api.Variable, error) {
	var v *api.Variable
	err := c.doGET(fmt.Sprintf("/threads/%d/eval/%s", threadID, symbol), &v)
	if err != nil {
		return nil, err
	}
	return v, nil
}

func (c *RESTClient) ListSources(filter string) ([]string, error) {
	params := [][]string{}
	if len(filter) > 0 {
		params = append(params, []string{"filter", filter})
	}
	var sources []string
	err := c.doGET("/sources", &sources, params...)
	if err != nil {
		return nil, err
	}
	return sources, nil
}

func (c *RESTClient) ListFunctions(filter string) ([]string, error) {
	params := [][]string{}
	if len(filter) > 0 {
		params = append(params, []string{"filter", filter})
	}
	var funcs []string
	err := c.doGET("/functions", &funcs, params...)
	if err != nil {
		return nil, err
	}
	return funcs, nil
}

func (c *RESTClient) ListPackageVariables(filter string) ([]api.Variable, error) {
	params := [][]string{}
	if len(filter) > 0 {
		params = append(params, []string{"filter", filter})
	}
	var vars []api.Variable
	err := c.doGET(fmt.Sprintf("/vars"), &vars, params...)
	if err != nil {
		return nil, err
	}
	return vars, nil
}

func (c *RESTClient) ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) {
	params := [][]string{}
	if len(filter) > 0 {
		params = append(params, []string{"filter", filter})
	}
	var vars []api.Variable
	err := c.doGET(fmt.Sprintf("/threads/%d/vars", threadID), &vars, params...)
	if err != nil {
		return nil, err
	}
	return vars, nil
}

func (c *RESTClient) ListGoroutines() ([]*api.Goroutine, error) {
	var goroutines []*api.Goroutine
	err := c.doGET("/goroutines", &goroutines)
	if err != nil {
		return nil, err
	}
	return goroutines, nil
}

// TODO: how do we use http.Client with a UNIX socket URI?
func (c *RESTClient) url(path string) string {
	return fmt.Sprintf("http://%s%s", c.addr, path)
}

// doGET performs an HTTP GET to path and stores the resulting API object in
// obj. Query parameters are passed as an array of 2-element string arrays
// representing key-value pairs.
func (c *RESTClient) doGET(path string, obj interface{}, params ...[]string) error {
	url, err := url.Parse(c.url(path))
	if err != nil {
		return err
	}

	// Add any supplied query parameters to the URL
	q := url.Query()
	for _, p := range params {
		q.Set(p[0], p[1])
	}
	url.RawQuery = q.Encode()

	// Create the request
	req, err := http.NewRequest("GET", url.String(), nil)
	if err != nil {
		return err
	}
	req.Header.Set("Accept", "application/json")

	// Execute the request
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Extract error text and return
	if resp.StatusCode != http.StatusOK {
		contents, _ := ioutil.ReadAll(resp.Body)
		return &ClientError{Message: string(contents), Status: resp.Status}
	}

	// Decode result object
	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(&obj)
	if err != nil {
		return err
	}
	return nil
}

// doPOST performs an HTTP POST to path, sending 'out' as the body and storing
// the resulting API object to 'in'.
func (c *RESTClient) doPOST(path string, out interface{}, in interface{}) error {
	jsonString, err := json.Marshal(out)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", c.url(path), bytes.NewBuffer(jsonString))
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusCreated {
		contents, _ := ioutil.ReadAll(resp.Body)
		return &ClientError{Message: string(contents), Status: resp.Status}
	}

	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(&in)
	if err != nil {
		return err
	}
	return nil
}

// doDELETE performs an HTTP DELETE to path, storing the resulting API object
// to 'obj'.
func (c *RESTClient) doDELETE(path string, obj interface{}) error {
	req, err := http.NewRequest("DELETE", c.url(path), nil)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		contents, _ := ioutil.ReadAll(resp.Body)
		return &ClientError{Message: string(contents), Status: resp.Status}
	}

	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(&obj)
	if err != nil {
		return err
	}
	return nil
}

// doPUT performs an HTTP PUT to path, sending 'out' as the body and storing
// the resulting API object to 'in'.
func (c *RESTClient) doPUT(path string, out interface{}, in interface{}) error {
	jsonString, err := json.Marshal(out)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", c.url(path), bytes.NewBuffer(jsonString))
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		contents, _ := ioutil.ReadAll(resp.Body)
		return &ClientError{Message: string(contents), Status: resp.Status}
	}

	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(&in)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package shell

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"

	"../dos"
)

type StatementT struct {
	Args     []string
	RawArgs  []string
	Redirect []*Redirecter
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return fmt.Sprintf("%d", LastErrorLevel)
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

var rxSubstitute = regexp.MustCompile(`^([^\:]+)\:([^\=]+)=(.*)$`)

func ourGetenvSub(name string) (string, bool) {
	m := rxSubstitute.FindStringSubmatch(name)
	if m != nil {
		base, ok := OurGetEnv(m[1])
		if ok {
			return strings.Replace(base, m[2], m[3], 1), true
		} else {
			return "", false
		}
	} else {
		return OurGetEnv(name)
	}
}

func OurGetEnv(name string) (string, bool) {
	value := os.Getenv(name)
	if value != "" {
		return value, true
	} else if m := rxUnicode.FindStringSubmatch(name); m != nil {
		ucode, _ := strconv.ParseInt(m[1], 16, 32)
		return fmt.Sprintf("%c", rune(ucode)), true
	} else if f, ok := PercentFunc[strings.ToUpper(name)]; ok {
		return f(), true
	} else {
		return "", false
	}
}

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

const NOTQUOTED = '\000'

const EMPTY_COMMAND_FOUND = "Empty command found"

func string2word(source_ string, removeQuote bool) string {
	var buffer bytes.Buffer
	source := strings.NewReader(source_)

	lastchar := ' '
	quoteNow := NOTQUOTED
	yenCount := 0
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '~' && unicode.IsSpace(lastchar) {
			if home := dos.GetHome(); home != "" {
				buffer.WriteString(home)
			} else {
				buffer.WriteRune('~')
			}
			lastchar = '~'
			continue
		}
		if ch == '%' && quoteNow != '\'' && yenCount%2 == 0 {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					source.Seek(-int64(nameBuf.Len()), io.SeekCurrent)
					break
				}
				if ch == '%' {
					if value, ok := ourGetenvSub(nameBuf.String()); ok {
						buffer.WriteString(value)
					} else {
						buffer.WriteRune('%')
						source.Seek(-int64(nameBuf.Len()+1), io.SeekCurrent)
					}
					break
				}
				nameBuf.WriteRune(ch)
			}
			continue
		}

		if quoteNow != NOTQUOTED && ch == quoteNow && yenCount%2 == 0 {
			if !removeQuote {
				buffer.WriteRune(ch)
			}
			// Close Quotation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = NOTQUOTED
		} else if (ch == '\'' || ch == '"') && quoteNow == NOTQUOTED && yenCount%2 == 0 {
			if !removeQuote {
				buffer.WriteRune(ch)
			}
			// Open Qutation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = ch
			if ch == lastchar {
				buffer.WriteRune(ch)
			}
		} else {
			if ch == '\\' {
				yenCount++
			} else if ch == '\'' || ch == '"' {
				for ; yenCount >= 2; yenCount -= 2 {
					buffer.WriteRune('\\')
				}
				yenCount = 0
				buffer.WriteRune(ch)
			} else {
				for ; yenCount > 0; yenCount-- {
					buffer.WriteRune('\\')
				}
				buffer.WriteRune(ch)
			}
		}
		lastchar = ch
	}
	for ; yenCount > 0; yenCount-- {
		buffer.WriteRune('\\')
	}
	return buffer.String()
}

func parse1(text string) ([]*StatementT, error) {
	quoteNow := NOTQUOTED
	yenCount := 0
	statements := make([]*StatementT, 0)
	args := make([]string, 0)
	rawArgs := make([]string, 0)
	lastchar := ' '
	var buffer bytes.Buffer
	isNextRedirect := false
	redirect := make([]*Redirecter, 0, 3)

	term_line := func(term string) {
		statement1 := new(StatementT)
		if buffer.Len() > 0 {
			if isNextRedirect && len(redirect) > 0 {
				redirect[len(redirect)-1].SetPath(string2word(buffer.String(), true))
				isNextRedirect = false
				statement1.RawArgs = rawArgs
				statement1.Args = args
			} else {
				statement1.RawArgs = append(rawArgs, string2word(buffer.String(), false))
				statement1.Args = append(args, string2word(buffer.String(), true))
			}
			buffer.Reset()
		} else if len(args) <= 0 {
			return
		} else {
			statement1.RawArgs = rawArgs
			statement1.Args = args
		}
		statement1.Redirect = redirect
		redirect = make([]*Redirecter, 0, 3)
		rawArgs = make([]string, 0)
		args = make([]string, 0)
		statement1.Term = term
		statements = append(statements, statement1)
	}

	term_word := func() {
		if isNextRedirect && len(redirect) > 0 {
			redirect[len(redirect)-1].SetPath(string2word(buffer.String(), true))
		} else {
			if buffer.Len() > 0 {
				rawArgs = append(rawArgs, string2word(buffer.String(), false))
				args = append(args, string2word(buffer.String(), true))
			}
		}
		buffer.Reset()
	}

	reader := strings.NewReader(text)
	for reader.Len() > 0 {
		ch, chSize, chErr := reader.ReadRune()
		if chSize <= 0 {
			break
		}
		if chErr != nil {
			return nil, chErr
		}
		if quoteNow == NOTQUOTED {
			if yenCount%2 == 0 && (ch == '"' || ch == '\'') {
				quoteNow = ch
			}
		} else if yenCount%2 == 0 && ch == quoteNow {
			quoteNow = NOTQUOTED
		}
		if quoteNow != NOTQUOTED {
			buffer.WriteRune(ch)
		} else if ch == ' ' {
			if buffer.Len() > 0 {
				term_word()
				isNextRedirect = false
			}
		} else if lastchar == ' ' && ch == '#' {
			break
		} else if lastchar == ' ' && ch == ';' {
			term_line(";")
		} else if ch == '|' {
			if lastchar == '|' {
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "||"
			} else {
				term_line("|")
			}
		} else if ch == '&' {
			switch lastchar {
			case '&':
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "&&"
			case '|':
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "|&"
			case '>':
				// >&[n]
				ch2, ch2siz, ch2err := reader.ReadRune()
				if ch2err != nil {
					return nil, ch2err
				}
				if ch2siz <= 0 {
					return nil, errors.New("Too Near EOF for >&")
				}
				red := redirect[len(redirect)-1]
				switch ch2 {
				case '1':
					red.DupFrom(1)
				case '2':
					red.DupFrom(2)
				default:
					return nil, errors.New("Syntax error after >&")
				}
				isNextRedirect = false
			default:
				term_line("&")
			}
		} else if ch == '>' {
			switch lastchar {
			case '1':
				// 1>
				chomp(&buffer)
				term_word()
				redirect = append(redirect, NewRedirecter(1))
			case '2':
				// 2>
				chomp(&buffer)
				term_word()
				redirect = append(redirect, NewRedirecter(2))
			case '>':
				// >>
				term_word()
				if len(redirect) >= 0 {
					redirect[len(redirect)-1].SetAppend()
				}
			default:
				// >
				term_word()
				redirect = append(redirect, NewRedirecter(1))
			}
			isNextRedirect = true
		} else if ch == '<' {
			term_word()
			redirect = append(redirect, NewRedirecter(0))
			isNextRedirect = true
		} else {
			buffer.WriteRune(ch)
		}
		if ch == '\\' {
			yenCount++
		} else {
			yenCount = 0
		}
		lastchar = ch
	}
	term_line(" ")
	return statements, nil
}

// Make arrays whose elements are pipelines
func parse2(statements []*StatementT) [][]*StatementT {
	result := make([][]*StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		switch statement1.Term {
		case "|", "|&":

		default:
			result = append(result, make([]*StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) ([][]*StatementT, error) {
	result1, err := parse1(text)
	if err != nil {
		return nil, err
	}
	result2 := parse2(result1)
	return result2, nil
}
<commit_msg>Fix %FOO:X=Y% was replaced once only<commit_after>package shell

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode"

	"../dos"
)

type StatementT struct {
	Args     []string
	RawArgs  []string
	Redirect []*Redirecter
	Term     string
}

var prefix []string = []string{" 0<", " 1>", " 2>"}

var PercentFunc = map[string]func() string{
	"CD": func() string {
		wd, err := os.Getwd()
		if err == nil {
			return wd
		} else {
			return ""
		}
	},
	"ERRORLEVEL": func() string {
		return fmt.Sprintf("%d", LastErrorLevel)
	},
}

var rxUnicode = regexp.MustCompile("^[uU]\\+?([0-9a-fA-F]+)$")

var rxSubstitute = regexp.MustCompile(`^([^\:]+)\:([^\=]+)=(.*)$`)

func ourGetenvSub(name string) (string, bool) {
	m := rxSubstitute.FindStringSubmatch(name)
	if m != nil {
		base, ok := OurGetEnv(m[1])
		if ok {
			return strings.Replace(base, m[2], m[3], -1), true
		} else {
			return "", false
		}
	} else {
		return OurGetEnv(name)
	}
}

func OurGetEnv(name string) (string, bool) {
	value := os.Getenv(name)
	if value != "" {
		return value, true
	} else if m := rxUnicode.FindStringSubmatch(name); m != nil {
		ucode, _ := strconv.ParseInt(m[1], 16, 32)
		return fmt.Sprintf("%c", rune(ucode)), true
	} else if f, ok := PercentFunc[strings.ToUpper(name)]; ok {
		return f(), true
	} else {
		return "", false
	}
}

func chomp(buffer *bytes.Buffer) {
	original := buffer.String()
	buffer.Reset()
	var lastchar rune
	for i, ch := range original {
		if i > 0 {
			buffer.WriteRune(lastchar)
		}
		lastchar = ch
	}
}

const NOTQUOTED = '\000'

const EMPTY_COMMAND_FOUND = "Empty command found"

func string2word(source_ string, removeQuote bool) string {
	var buffer bytes.Buffer
	source := strings.NewReader(source_)

	lastchar := ' '
	quoteNow := NOTQUOTED
	yenCount := 0
	for {
		ch, _, err := source.ReadRune()
		if err != nil {
			break
		}
		if ch == '~' && unicode.IsSpace(lastchar) {
			if home := dos.GetHome(); home != "" {
				buffer.WriteString(home)
			} else {
				buffer.WriteRune('~')
			}
			lastchar = '~'
			continue
		}
		if ch == '%' && quoteNow != '\'' && yenCount%2 == 0 {
			var nameBuf bytes.Buffer
			for {
				ch, _, err = source.ReadRune()
				if err != nil {
					buffer.WriteRune('%')
					source.Seek(-int64(nameBuf.Len()), io.SeekCurrent)
					break
				}
				if ch == '%' {
					if value, ok := ourGetenvSub(nameBuf.String()); ok {
						buffer.WriteString(value)
					} else {
						buffer.WriteRune('%')
						source.Seek(-int64(nameBuf.Len()+1), io.SeekCurrent)
					}
					break
				}
				nameBuf.WriteRune(ch)
			}
			continue
		}

		if quoteNow != NOTQUOTED && ch == quoteNow && yenCount%2 == 0 {
			if !removeQuote {
				buffer.WriteRune(ch)
			}
			// Close Quotation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = NOTQUOTED
		} else if (ch == '\'' || ch == '"') && quoteNow == NOTQUOTED && yenCount%2 == 0 {
			if !removeQuote {
				buffer.WriteRune(ch)
			}
			// Open Qutation.
			for ; yenCount >= 2; yenCount -= 2 {
				buffer.WriteRune('\\')
			}
			quoteNow = ch
			if ch == lastchar {
				buffer.WriteRune(ch)
			}
		} else {
			if ch == '\\' {
				yenCount++
			} else if ch == '\'' || ch == '"' {
				for ; yenCount >= 2; yenCount -= 2 {
					buffer.WriteRune('\\')
				}
				yenCount = 0
				buffer.WriteRune(ch)
			} else {
				for ; yenCount > 0; yenCount-- {
					buffer.WriteRune('\\')
				}
				buffer.WriteRune(ch)
			}
		}
		lastchar = ch
	}
	for ; yenCount > 0; yenCount-- {
		buffer.WriteRune('\\')
	}
	return buffer.String()
}

func parse1(text string) ([]*StatementT, error) {
	quoteNow := NOTQUOTED
	yenCount := 0
	statements := make([]*StatementT, 0)
	args := make([]string, 0)
	rawArgs := make([]string, 0)
	lastchar := ' '
	var buffer bytes.Buffer
	isNextRedirect := false
	redirect := make([]*Redirecter, 0, 3)

	term_line := func(term string) {
		statement1 := new(StatementT)
		if buffer.Len() > 0 {
			if isNextRedirect && len(redirect) > 0 {
				redirect[len(redirect)-1].SetPath(string2word(buffer.String(), true))
				isNextRedirect = false
				statement1.RawArgs = rawArgs
				statement1.Args = args
			} else {
				statement1.RawArgs = append(rawArgs, string2word(buffer.String(), false))
				statement1.Args = append(args, string2word(buffer.String(), true))
			}
			buffer.Reset()
		} else if len(args) <= 0 {
			return
		} else {
			statement1.RawArgs = rawArgs
			statement1.Args = args
		}
		statement1.Redirect = redirect
		redirect = make([]*Redirecter, 0, 3)
		rawArgs = make([]string, 0)
		args = make([]string, 0)
		statement1.Term = term
		statements = append(statements, statement1)
	}

	term_word := func() {
		if isNextRedirect && len(redirect) > 0 {
			redirect[len(redirect)-1].SetPath(string2word(buffer.String(), true))
		} else {
			if buffer.Len() > 0 {
				rawArgs = append(rawArgs, string2word(buffer.String(), false))
				args = append(args, string2word(buffer.String(), true))
			}
		}
		buffer.Reset()
	}

	reader := strings.NewReader(text)
	for reader.Len() > 0 {
		ch, chSize, chErr := reader.ReadRune()
		if chSize <= 0 {
			break
		}
		if chErr != nil {
			return nil, chErr
		}
		if quoteNow == NOTQUOTED {
			if yenCount%2 == 0 && (ch == '"' || ch == '\'') {
				quoteNow = ch
			}
		} else if yenCount%2 == 0 && ch == quoteNow {
			quoteNow = NOTQUOTED
		}
		if quoteNow != NOTQUOTED {
			buffer.WriteRune(ch)
		} else if ch == ' ' {
			if buffer.Len() > 0 {
				term_word()
				isNextRedirect = false
			}
		} else if lastchar == ' ' && ch == '#' {
			break
		} else if lastchar == ' ' && ch == ';' {
			term_line(";")
		} else if ch == '|' {
			if lastchar == '|' {
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "||"
			} else {
				term_line("|")
			}
		} else if ch == '&' {
			switch lastchar {
			case '&':
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "&&"
			case '|':
				if len(statements) <= 0 {
					return nil, errors.New(EMPTY_COMMAND_FOUND)
				}
				statements[len(statements)-1].Term = "|&"
			case '>':
				// >&[n]
				ch2, ch2siz, ch2err := reader.ReadRune()
				if ch2err != nil {
					return nil, ch2err
				}
				if ch2siz <= 0 {
					return nil, errors.New("Too Near EOF for >&")
				}
				red := redirect[len(redirect)-1]
				switch ch2 {
				case '1':
					red.DupFrom(1)
				case '2':
					red.DupFrom(2)
				default:
					return nil, errors.New("Syntax error after >&")
				}
				isNextRedirect = false
			default:
				term_line("&")
			}
		} else if ch == '>' {
			switch lastchar {
			case '1':
				// 1>
				chomp(&buffer)
				term_word()
				redirect = append(redirect, NewRedirecter(1))
			case '2':
				// 2>
				chomp(&buffer)
				term_word()
				redirect = append(redirect, NewRedirecter(2))
			case '>':
				// >>
				term_word()
				if len(redirect) >= 0 {
					redirect[len(redirect)-1].SetAppend()
				}
			default:
				// >
				term_word()
				redirect = append(redirect, NewRedirecter(1))
			}
			isNextRedirect = true
		} else if ch == '<' {
			term_word()
			redirect = append(redirect, NewRedirecter(0))
			isNextRedirect = true
		} else {
			buffer.WriteRune(ch)
		}
		if ch == '\\' {
			yenCount++
		} else {
			yenCount = 0
		}
		lastchar = ch
	}
	term_line(" ")
	return statements, nil
}

// Make arrays whose elements are pipelines
func parse2(statements []*StatementT) [][]*StatementT {
	result := make([][]*StatementT, 1)
	for _, statement1 := range statements {
		result[len(result)-1] = append(result[len(result)-1], statement1)
		switch statement1.Term {
		case "|", "|&":

		default:
			result = append(result, make([]*StatementT, 0))
		}
	}
	if len(result[len(result)-1]) <= 0 {
		result = result[0 : len(result)-1]
	}
	return result
}

func Parse(text string) ([][]*StatementT, error) {
	result1, err := parse1(text)
	if err != nil {
		return nil, err
	}
	result2 := parse2(result1)
	return result2, nil
}
<|endoftext|>