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

// +build !appengine

// Package socket implements an WebSocket-based playground backend.
// Clients connect to a websocket handler and send run/kill commands, and
// the server sends the output and exit status of the running processes.
// Multiple clients running multiple processes may be served concurrently.
// The wire format is JSON and is described by the Message type.
//
// This will not run on App Engine as WebSockets are not supported there.
package socket

import (
	"bytes"
	"encoding/json"
	"errors"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"code.google.com/p/go.net/websocket"
)

// RunScripts specifies whether the socket handler should execute shell scripts
// (snippets that start with a shebang).
var RunScripts = true

// Environ provides an environment when a binary, such as the go tool, is
// invoked.
var Environ func() []string = os.Environ

const (
	// The maximum number of messages to send per session (avoid flooding).
	msgLimit = 1000

	// Batch messages sent in this interval and send as a single message.
	msgDelay = 10 * time.Millisecond
)

// Message is the wire format for the websocket connection to the browser.
// It is used for both sending output messages and receiving commands, as
// distinguished by the Kind field.
type Message struct {
	Id      string // client-provided unique id for the process
	Kind    string // in: "run", "kill" out: "stdout", "stderr", "end"
	Body    string
	Options *Options `json:",omitempty"`
}

// Options specify additional message options.
type Options struct {
	Race bool // use -race flag when building code (for "run" only)
}

// NewHandler returns a websocket server which checks the origin of requests.
func NewHandler(origin *url.URL) websocket.Server {
	return websocket.Server{
		Config:    websocket.Config{Origin: origin},
		Handshake: handshake,
		Handler:   websocket.Handler(socketHandler),
	}
}

// handshake checks the origin of a request during the websocket handshake.
func handshake(c *websocket.Config, req *http.Request) error {
	o, err := websocket.Origin(c, req)
	if err != nil {
		log.Println("bad websocket origin:", err)
		return websocket.ErrBadWebSocketOrigin
	}
	_, port, err := net.SplitHostPort(c.Origin.Host)
	if err != nil {
		log.Println("bad websocket origin:", err)
		return websocket.ErrBadWebSocketOrigin
	}
	ok := c.Origin.Scheme == o.Scheme && (c.Origin.Host == o.Host || c.Origin.Host == net.JoinHostPort(o.Host, port))
	if !ok {
		log.Println("bad websocket origin:", o)
		return websocket.ErrBadWebSocketOrigin
	}
	return nil
}

// socketHandler handles the websocket connection for a given present session.
// It handles transcoding Messages to and from JSON format, and starting
// and killing processes.
func socketHandler(c *websocket.Conn) {
	in, out := make(chan *Message), make(chan *Message)
	errc := make(chan error, 1)

	// Decode messages from client and send to the in channel.
	go func() {
		dec := json.NewDecoder(c)
		for {
			var m Message
			if err := dec.Decode(&m); err != nil {
				errc <- err
				return
			}
			in <- &m
		}
	}()

	// Receive messages from the out channel and encode to the client.
	go func() {
		enc := json.NewEncoder(c)
		for m := range out {
			if err := enc.Encode(m); err != nil {
				errc <- err
				return
			}
		}
	}()

	// Start and kill processes and handle errors.
	proc := make(map[string]*process)
	for {
		select {
		case m := <-in:
			switch m.Kind {
			case "run":
				proc[m.Id].Kill()
				lOut := limiter(in, out)
				proc[m.Id] = startProcess(m.Id, m.Body, lOut, m.Options)
			case "kill":
				proc[m.Id].Kill()
			}
		case err := <-errc:
			if err != io.EOF {
				// A encode or decode has failed; bail.
				log.Println(err)
			}
			// Shut down any running processes.
			for _, p := range proc {
				p.Kill()
			}
			return
		}
	}
}

// process represents a running process.
type process struct {
	id   string
	out  chan<- *Message
	done chan struct{} // closed when wait completes
	run  *exec.Cmd
	bin  string
}

// startProcess builds and runs the given program, sending its output
// and end event as Messages on the provided channel.
func startProcess(id, body string, out chan<- *Message, opt *Options) *process {
	p := &process{
		id:   id,
		out:  out,
		done: make(chan struct{}),
	}
	var err error
	if path, args := shebang(body); RunScripts && path != "" {
		err = p.startProcess(path, args, body)
	} else {
		err = p.start(body, opt)
	}
	if err != nil {
		p.end(err)
		return nil
	}
	go p.wait()
	return p
}

// Kill stops the process if it is running and waits for it to exit.
func (p *process) Kill() {
	if p == nil {
		return
	}
	p.run.Process.Kill()
	<-p.done // block until process exits
}

// shebang looks for a shebang ('#!') at the beginning of the passed string.
// If found, it returns the path and args after the shebang.
// args includes the command as args[0].
func shebang(body string) (path string, args []string) {
	body = strings.TrimSpace(body)
	if !strings.HasPrefix(body, "#!") {
		return "", nil
	}
	if i := strings.Index(body, "\n"); i >= 0 {
		body = body[:i]
	}
	fs := strings.Fields(body[2:])
	return fs[0], fs
}

// startProcess starts a given program given its path and passing the given body
// to the command standard input.
func (p *process) startProcess(path string, args []string, body string) error {
	cmd := &exec.Cmd{
		Path:   path,
		Args:   args,
		Stdin:  strings.NewReader(body),
		Stdout: &messageWriter{id: p.id, kind: "stdout", out: p.out},
		Stderr: &messageWriter{id: p.id, kind: "stderr", out: p.out},
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	p.run = cmd
	return nil
}

// start builds and starts the given program, sending its output to p.out,
// and stores the running *exec.Cmd in the run field.
func (p *process) start(body string, opt *Options) error {
	// We "go build" and then exec the binary so that the
	// resultant *exec.Cmd is a handle to the user's program
	// (rather than the go tool process).
	// This makes Kill work.

	bin := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
	src := bin + ".go"
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// write body to x.go
	defer os.Remove(src)
	err := ioutil.WriteFile(src, []byte(body), 0666)
	if err != nil {
		return err
	}

	// build x.go, creating x
	p.bin = bin // to be removed by p.end
	dir, file := filepath.Split(src)
	args := []string{"go", "build", "-tags", "OMIT"}
	if opt != nil && opt.Race {
		p.out <- &Message{
			Id: p.id, Kind: "stderr",
			Body: "Running with race detector.\n",
		}
		args = append(args, "-race")
	}
	args = append(args, "-o", bin, file)
	cmd := p.cmd(dir, args...)
	cmd.Stdout = cmd.Stderr // send compiler output to stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	// run x
	cmd = p.cmd("", bin)
	if opt != nil && opt.Race {
		cmd.Env = append(cmd.Env, "GOMAXPROCS=2")
	}
	if err := cmd.Start(); err != nil {
		// If we failed to exec, that might be because they built
		// a non-main package instead of an executable.
		// Check and report that.
		if name, err := packageName(body); err == nil && name != "main" {
			return errors.New(`executable programs must use "package main"`)
		}
		return err
	}
	p.run = cmd
	return nil
}

// wait waits for the running process to complete
// and sends its error state to the client.
func (p *process) wait() {
	p.end(p.run.Wait())
	close(p.done) // unblock waiting Kill calls
}

// end sends an "end" message to the client, containing the process id and the
// given error value. It also removes the binary.
func (p *process) end(err error) {
	if p.bin != "" {
		defer os.Remove(p.bin)
	}
	m := &Message{Id: p.id, Kind: "end"}
	if err != nil {
		m.Body = err.Error()
	}
	// Wait for any outstanding reads to finish (potential race here).
	time.AfterFunc(msgDelay, func() { p.out <- m })
}

// cmd builds an *exec.Cmd that writes its standard output and error to the
// process' output channel.
func (p *process) cmd(dir string, args ...string) *exec.Cmd {
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = dir
	cmd.Env = Environ()
	cmd.Stdout = &messageWriter{id: p.id, kind: "stdout", out: p.out}
	cmd.Stderr = &messageWriter{id: p.id, kind: "stderr", out: p.out}
	return cmd
}

func packageName(body string) (string, error) {
	f, err := parser.ParseFile(token.NewFileSet(), "prog.go",
		strings.NewReader(body), parser.PackageClauseOnly)
	if err != nil {
		return "", err
	}
	return f.Name.String(), nil
}

// messageWriter is an io.Writer that converts all writes to Message sends on
// the out channel with the specified id and kind.
type messageWriter struct {
	id, kind string
	out      chan<- *Message

	mu   sync.Mutex
	buf  []byte
	send *time.Timer
}

func (w *messageWriter) Write(b []byte) (n int, err error) {
	// Buffer writes that occur in a short period to send as one Message.
	w.mu.Lock()
	w.buf = append(w.buf, b...)
	if w.send == nil {
		w.send = time.AfterFunc(msgDelay, w.sendNow)
	}
	w.mu.Unlock()
	return len(b), nil
}

func (w *messageWriter) sendNow() {
	w.mu.Lock()
	body := safeString(w.buf)
	w.buf, w.send = nil, nil
	w.mu.Unlock()
	w.out <- &Message{Id: w.id, Kind: w.kind, Body: body}
}

// safeString returns b as a valid UTF-8 string.
func safeString(b []byte) string {
	if utf8.Valid(b) {
		return string(b)
	}
	var buf bytes.Buffer
	for len(b) > 0 {
		r, size := utf8.DecodeRune(b)
		b = b[size:]
		buf.WriteRune(r)
	}
	return buf.String()
}

// limiter returns a channel that wraps dest. Messages sent to the channel are
// sent to dest. After msgLimit Messages have been passed on, a "kill" Message
// is sent to the kill channel, and only "end" messages are passed.
func limiter(kill chan<- *Message, dest chan<- *Message) chan<- *Message {
	ch := make(chan *Message)
	go func() {
		n := 0
		for m := range ch {
			switch {
			case n < msgLimit || m.Kind == "end":
				dest <- m
				if m.Kind == "end" {
					return
				}
			case n == msgLimit:
				// process produced too much output. Kill it.
				kill <- &Message{Id: m.Id, Kind: "kill"}
			}
			n++
		}
	}()
	return ch
}

var tmpdir string

func init() {
	// find real path to temporary directory
	var err error
	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
	if err != nil {
		log.Fatal(err)
	}
}

var uniq = make(chan int) // a source of numbers for naming temporary files

func init() {
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()
}
<commit_msg>go.tools/playground/socket: add Native Client wrapper handling<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !appengine

// Package socket implements an WebSocket-based playground backend.
// Clients connect to a websocket handler and send run/kill commands, and
// the server sends the output and exit status of the running processes.
// Multiple clients running multiple processes may be served concurrently.
// The wire format is JSON and is described by the Message type.
//
// This will not run on App Engine as WebSockets are not supported there.
package socket

import (
	"bytes"
	"encoding/json"
	"errors"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"code.google.com/p/go.net/websocket"
)

// RunScripts specifies whether the socket handler should execute shell scripts
// (snippets that start with a shebang).
var RunScripts = true

// Environ provides an environment when a binary, such as the go tool, is
// invoked.
var Environ func() []string = os.Environ

const (
	// The maximum number of messages to send per session (avoid flooding).
	msgLimit = 1000

	// Batch messages sent in this interval and send as a single message.
	msgDelay = 10 * time.Millisecond
)

// Message is the wire format for the websocket connection to the browser.
// It is used for both sending output messages and receiving commands, as
// distinguished by the Kind field.
type Message struct {
	Id      string // client-provided unique id for the process
	Kind    string // in: "run", "kill" out: "stdout", "stderr", "end"
	Body    string
	Options *Options `json:",omitempty"`
}

// Options specify additional message options.
type Options struct {
	Race bool // use -race flag when building code (for "run" only)
}

// NewHandler returns a websocket server which checks the origin of requests.
func NewHandler(origin *url.URL) websocket.Server {
	return websocket.Server{
		Config:    websocket.Config{Origin: origin},
		Handshake: handshake,
		Handler:   websocket.Handler(socketHandler),
	}
}

// handshake checks the origin of a request during the websocket handshake.
func handshake(c *websocket.Config, req *http.Request) error {
	o, err := websocket.Origin(c, req)
	if err != nil {
		log.Println("bad websocket origin:", err)
		return websocket.ErrBadWebSocketOrigin
	}
	_, port, err := net.SplitHostPort(c.Origin.Host)
	if err != nil {
		log.Println("bad websocket origin:", err)
		return websocket.ErrBadWebSocketOrigin
	}
	ok := c.Origin.Scheme == o.Scheme && (c.Origin.Host == o.Host || c.Origin.Host == net.JoinHostPort(o.Host, port))
	if !ok {
		log.Println("bad websocket origin:", o)
		return websocket.ErrBadWebSocketOrigin
	}
	return nil
}

// socketHandler handles the websocket connection for a given present session.
// It handles transcoding Messages to and from JSON format, and starting
// and killing processes.
func socketHandler(c *websocket.Conn) {
	in, out := make(chan *Message), make(chan *Message)
	errc := make(chan error, 1)

	// Decode messages from client and send to the in channel.
	go func() {
		dec := json.NewDecoder(c)
		for {
			var m Message
			if err := dec.Decode(&m); err != nil {
				errc <- err
				return
			}
			in <- &m
		}
	}()

	// Receive messages from the out channel and encode to the client.
	go func() {
		enc := json.NewEncoder(c)
		for m := range out {
			if err := enc.Encode(m); err != nil {
				errc <- err
				return
			}
		}
	}()

	// Start and kill processes and handle errors.
	proc := make(map[string]*process)
	for {
		select {
		case m := <-in:
			switch m.Kind {
			case "run":
				proc[m.Id].Kill()
				lOut := limiter(in, out)
				proc[m.Id] = startProcess(m.Id, m.Body, lOut, m.Options)
			case "kill":
				proc[m.Id].Kill()
			}
		case err := <-errc:
			if err != io.EOF {
				// A encode or decode has failed; bail.
				log.Println(err)
			}
			// Shut down any running processes.
			for _, p := range proc {
				p.Kill()
			}
			return
		}
	}
}

// process represents a running process.
type process struct {
	id   string
	out  chan<- *Message
	done chan struct{} // closed when wait completes
	run  *exec.Cmd
	bin  string
}

// startProcess builds and runs the given program, sending its output
// and end event as Messages on the provided channel.
func startProcess(id, body string, out chan<- *Message, opt *Options) *process {
	p := &process{
		id:   id,
		out:  out,
		done: make(chan struct{}),
	}
	var err error
	if path, args := shebang(body); RunScripts && path != "" {
		err = p.startProcess(path, args, body)
	} else {
		err = p.start(body, opt)
	}
	if err != nil {
		p.end(err)
		return nil
	}
	go p.wait()
	return p
}

// Kill stops the process if it is running and waits for it to exit.
func (p *process) Kill() {
	if p == nil {
		return
	}
	p.run.Process.Kill()
	<-p.done // block until process exits
}

// shebang looks for a shebang ('#!') at the beginning of the passed string.
// If found, it returns the path and args after the shebang.
// args includes the command as args[0].
func shebang(body string) (path string, args []string) {
	body = strings.TrimSpace(body)
	if !strings.HasPrefix(body, "#!") {
		return "", nil
	}
	if i := strings.Index(body, "\n"); i >= 0 {
		body = body[:i]
	}
	fs := strings.Fields(body[2:])
	return fs[0], fs
}

// startProcess starts a given program given its path and passing the given body
// to the command standard input.
func (p *process) startProcess(path string, args []string, body string) error {
	cmd := &exec.Cmd{
		Path:   path,
		Args:   args,
		Stdin:  strings.NewReader(body),
		Stdout: &messageWriter{id: p.id, kind: "stdout", out: p.out},
		Stderr: &messageWriter{id: p.id, kind: "stderr", out: p.out},
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	p.run = cmd
	return nil
}

// start builds and starts the given program, sending its output to p.out,
// and stores the running *exec.Cmd in the run field.
func (p *process) start(body string, opt *Options) error {
	// We "go build" and then exec the binary so that the
	// resultant *exec.Cmd is a handle to the user's program
	// (rather than the go tool process).
	// This makes Kill work.

	bin := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
	src := bin + ".go"
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}

	// write body to x.go
	defer os.Remove(src)
	err := ioutil.WriteFile(src, []byte(body), 0666)
	if err != nil {
		return err
	}

	// build x.go, creating x
	p.bin = bin // to be removed by p.end
	dir, file := filepath.Split(src)
	args := []string{"go", "build", "-tags", "OMIT"}
	if opt != nil && opt.Race {
		p.out <- &Message{
			Id: p.id, Kind: "stderr",
			Body: "Running with race detector.\n",
		}
		args = append(args, "-race")
	}
	args = append(args, "-o", bin, file)
	cmd := p.cmd(dir, args...)
	cmd.Stdout = cmd.Stderr // send compiler output to stderr
	if err := cmd.Run(); err != nil {
		return err
	}

	// run x
	if isNacl() {
		cmd, err = p.naclCmd(bin)
		if err != nil {
			return err
		}
	} else {
		cmd = p.cmd("", bin)
	}
	if opt != nil && opt.Race {
		cmd.Env = append(cmd.Env, "GOMAXPROCS=2")
	}
	if err := cmd.Start(); err != nil {
		// If we failed to exec, that might be because they built
		// a non-main package instead of an executable.
		// Check and report that.
		if name, err := packageName(body); err == nil && name != "main" {
			return errors.New(`executable programs must use "package main"`)
		}
		return err
	}
	p.run = cmd
	return nil
}

// wait waits for the running process to complete
// and sends its error state to the client.
func (p *process) wait() {
	p.end(p.run.Wait())
	close(p.done) // unblock waiting Kill calls
}

// end sends an "end" message to the client, containing the process id and the
// given error value. It also removes the binary.
func (p *process) end(err error) {
	if p.bin != "" {
		defer os.Remove(p.bin)
	}
	m := &Message{Id: p.id, Kind: "end"}
	if err != nil {
		m.Body = err.Error()
	}
	// Wait for any outstanding reads to finish (potential race here).
	time.AfterFunc(msgDelay, func() { p.out <- m })
}

// cmd builds an *exec.Cmd that writes its standard output and error to the
// process' output channel.
func (p *process) cmd(dir string, args ...string) *exec.Cmd {
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Dir = dir
	cmd.Env = Environ()
	cmd.Stdout = &messageWriter{id: p.id, kind: "stdout", out: p.out}
	cmd.Stderr = &messageWriter{id: p.id, kind: "stderr", out: p.out}
	return cmd
}

func isNacl() bool {
	for _, v := range append(Environ(), os.Environ()...) {
		if v == "GOOS=nacl" {
			return true
		}
	}
	return false
}

// naclCmd returns an *exec.Cmd that executes bin under native client.
func (p *process) naclCmd(bin string) (*exec.Cmd, error) {
	pwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	var ldr string
	env := []string{
		"NACLENV_GOOS=" + runtime.GOOS,
		"NACLENV_GOROOT=/go",
		"NACLENV_NACLPWD=" + strings.Replace(pwd, runtime.GOROOT(), "/go", 1),
	}
	switch runtime.GOARCH {
	case "amd64":
		env = append(env, "NACLENV_GOARCH=amd64")
		ldr = "sel_ldr_x86_64"
	case "386":
		env = append(env, "NACLENV_GOARCH=386")
		ldr = "sel_ldr_x86_32"
	default:
		return nil, errors.New("native client does not support GOARCH=" + runtime.GOARCH)
	}

	cmd := p.cmd("", ldr, "-l", "/dev/null", "-S", "-e", bin)
	cmd.Env = append(cmd.Env, env...)

	return cmd, nil
}

func packageName(body string) (string, error) {
	f, err := parser.ParseFile(token.NewFileSet(), "prog.go",
		strings.NewReader(body), parser.PackageClauseOnly)
	if err != nil {
		return "", err
	}
	return f.Name.String(), nil
}

// messageWriter is an io.Writer that converts all writes to Message sends on
// the out channel with the specified id and kind.
type messageWriter struct {
	id, kind string
	out      chan<- *Message

	mu   sync.Mutex
	buf  []byte
	send *time.Timer
}

func (w *messageWriter) Write(b []byte) (n int, err error) {
	// Buffer writes that occur in a short period to send as one Message.
	w.mu.Lock()
	w.buf = append(w.buf, b...)
	if w.send == nil {
		w.send = time.AfterFunc(msgDelay, w.sendNow)
	}
	w.mu.Unlock()
	return len(b), nil
}

func (w *messageWriter) sendNow() {
	w.mu.Lock()
	body := safeString(w.buf)
	w.buf, w.send = nil, nil
	w.mu.Unlock()
	w.out <- &Message{Id: w.id, Kind: w.kind, Body: body}
}

// safeString returns b as a valid UTF-8 string.
func safeString(b []byte) string {
	if utf8.Valid(b) {
		return string(b)
	}
	var buf bytes.Buffer
	for len(b) > 0 {
		r, size := utf8.DecodeRune(b)
		b = b[size:]
		buf.WriteRune(r)
	}
	return buf.String()
}

// limiter returns a channel that wraps dest. Messages sent to the channel are
// sent to dest. After msgLimit Messages have been passed on, a "kill" Message
// is sent to the kill channel, and only "end" messages are passed.
func limiter(kill chan<- *Message, dest chan<- *Message) chan<- *Message {
	ch := make(chan *Message)
	go func() {
		n := 0
		for m := range ch {
			switch {
			case n < msgLimit || m.Kind == "end":
				dest <- m
				if m.Kind == "end" {
					return
				}
			case n == msgLimit:
				// process produced too much output. Kill it.
				kill <- &Message{Id: m.Id, Kind: "kill"}
			}
			n++
		}
	}()
	return ch
}

var tmpdir string

func init() {
	// find real path to temporary directory
	var err error
	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
	if err != nil {
		log.Fatal(err)
	}
}

var uniq = make(chan int) // a source of numbers for naming temporary files

func init() {
	go func() {
		for i := 0; ; i++ {
			uniq <- i
		}
	}()
}
<|endoftext|>


<commit_before>package pagerduty

/*
 * Copyright 2016 Albert P. Tobey <atobey@netflix.com>
 *
 * 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.
 */

import (
	"bytes"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/netflix/hal-9001/hal"
)

func Register() {
	pg := hal.Plugin{
		Name:  "page",
		Func:  page,
		Regex: "^[[:space:]]*!page",
	}
	pg.Register()

	oc := hal.Plugin{
		Name:  "oncall",
		Func:  oncall,
		Regex: "^[[:space:]]*!oncall",
	}
	oc.Register()
}

// the hal.secrets key that should contain the pagerduty auth token
const PagerdutyTokenKey = `pagerduty.token`

// the hal.secrets key that should contain the pagerduty account domain
const PagerdutyDomainKey = `pagerduty.domain`

// the key name used for caching the full escalation policy
const PolicyCacheKey = `pagerduty.policy_cache`

const PageUsage = `!page <alias> [optional message]

Send an alert via Pagerduty with an optional custom message.

Aliases that have a comma-separated list of service keys will result in one page going to each service key when the alias is paged.

!page core
!page core <message>
!pagecore HELP ME YOU ARE MY ONLY HOPE

!page add <alias> <service key>
!page add <alias> <service key>,<service_key>,<service_key>,...
!page rm <alias>
!page list
`

const OncallUsage = `!oncall <alias>

Find out who is oncall. If only one argument is provided, it must match
a known alias for a Pagerduty service. Otherwise, it is expected to be
a subcommand.

!oncall core
`

const PageDefaultMessage = `HAL: your presence is requested in the chat room.`

const cacheExpire = time.Minute * 10

func page(msg hal.Evt) {
	parts := msg.BodyAsArgv()

	// detect concatenated command + team name & split them
	// e.g. "!pagecore" -> {"!page", "core"}
	if strings.HasPrefix(parts[0], "!page") && len(parts[0]) > 5 {
		team := strings.TrimPrefix(parts[0], "!page")
		parts = append([]string{"!page", team}, parts[1:]...)
	}

	// should be 2 parts now, "!page" and the target team
	if parts[0] != "!page" || len(parts) < 2 {
		msg.Reply(PageUsage)
		return
	}

	switch parts[1] {
	case "h", "help":
		msg.Reply(PageUsage)
	case "add":
		addAlias(msg, parts[2:])
	case "rm":
		rmAlias(msg, parts[2:])
	case "list":
		listAlias(msg)
	default:
		pageAlias(msg, parts[1:])
	}
}

func pageAlias(msg hal.Evt, parts []string) {
	pageMessage := PageDefaultMessage
	if len(parts) > 1 {
		pageMessage = strings.Join(parts, " ")
	}

	// map alias name to PD token via prefs
	key := aliasKey(parts[0])
	pref := msg.AsPref().FindKey(key).One()

	// make sure the query succeeded
	if !pref.Success {
		log.Println("%s", pref.String())
		msg.Replyf("Unable to access preferences: %q", pref.Error)
		return
	}

	// if qpref.Get returned the default, the alias was not found
	if pref.Value == "" {
		msg.Replyf("Alias %q not recognized. Try !page add <alias> <service key>", parts[0])
		return
	}

	// the value can be a list of tokens, separated by commas
	response := bytes.NewBuffer([]byte{})
	for _, svckey := range strings.Split(pref.Value, ",") {
		// get the Pagerduty auth token from the secrets API
		secrets := hal.Secrets()
		token := secrets.Get(PagerdutyTokenKey)
		if token == "" {
			msg.Replyf("Your Pagerduty auth token does not seem to be configured. Please add the %q secret.",
				PagerdutyTokenKey)
			return
		}

		// create the event and send it
		pde := NewTrigger(svckey, pageMessage) // in ./pagerduty.go
		resp, err := pde.Send(token)
		if err != nil {
			msg.Replyf("Error while communicating with Pagerduty. %d %s", resp.StatusCode, resp.Message)
			return
		}

		fmt.Fprintf(response, "%s\n", resp.Message)
	}

	// TODO: add some boilerplate around this
	msg.Reply(response.String())
}

func addAlias(msg hal.Evt, parts []string) {
	if len(parts) < 2 {
		msg.Replyf("!page add requires 2 arguments, e.g. !page add sysadmins XXXXXXX")
		return
	} else if len(parts) > 2 {
		keys := strings.Replace(strings.Join(parts[1:], ","), ",,", ",", len(parts)-2)
		parts = []string{parts[0], keys}
	}

	pref := msg.AsPref()
	pref.User = "" // filled in by AsPref and unwanted
	pref.Key = aliasKey(parts[0])
	pref.Value = parts[1]

	err := pref.Set()
	if err != nil {
		msg.Replyf("Write failed: %s", err)
	} else {
		msg.Replyf("Added alias: %q -> %q", parts[0], parts[1])
	}
}

func rmAlias(msg hal.Evt, parts []string) {
	if len(parts) != 1 {
		msg.Replyf("!page rm requires 1 argument, e.g. !page rm sysadmins")
		return
	}

	pref := msg.AsPref()
	pref.User = "" // filled in by AsPref and unwanted
	pref.Key = aliasKey(parts[0])
	pref.Delete()

	msg.Replyf("Removed alias %q", parts[0])
}

func listAlias(msg hal.Evt) {
	pref := msg.AsPref()
	pref.User = "" // filled in by AsPref and unwanted
	prefs := pref.GetPrefs()
	data := prefs.Table()
	msg.ReplyTable(data[0], data[1:])
}

func aliasKey(alias string) string {
	return fmt.Sprintf("alias.%s", alias)
}

func oncall(msg hal.Evt) {
	parts := msg.BodyAsArgv()

	if len(parts) == 1 {
		msg.Reply(OncallUsage)
		return
	} else if len(parts) != 2 {
		msg.Replyf("%s: invalid command.\n%s", parts[0], OncallUsage)
		return
	}

	secrets := hal.Secrets()
	token := secrets.Get(PagerdutyTokenKey)
	if token == "" {
		msg.Replyf("Your Pagerduty auth token does not seem to be configured. Please add the %q secret.",
			PagerdutyTokenKey)
		return
	}

	domain := secrets.Get(PagerdutyDomainKey)
	if domain == "" {
		msg.Replyf("Your Pagerduty domain does not seem to be configured. Please add the %q secret.",
			PagerdutyDomainKey)
		return
	}

	// TODO: look at the aliases set up for !page and try for an exact match
	// before doing fuzzy search -- move fuzzy search to a "search" subcommand
	// so it's clear that it is not precise

	// see if there's a copy cached
	policies := []EscalationPolicy{}
	if hal.Cache().Exists(PolicyCacheKey) {
		ttl, _ := hal.Cache().Get(PolicyCacheKey, &policies)
		// TODO: maybe hal.Cache().Get should be careful to not modify the pointer if the ttl is expired...
		if ttl == 0 {
			policies = []EscalationPolicy{}
		}
	}

	// when the cache fails, hit the API
	if len(policies) == 0 {
		msg.Reply("No cache available. Please wait, downloading policies from PagerDuty...")
		// get all of the defined policies
		var err error
		policies, err = GetEscalationPolicies(token, domain)
		if err != nil {
			msg.Replyf("REST call to Pagerduty failed: %s", err)
			return
		}

		// TODO: make this configurable via prefs
		hal.Cache().Set(PolicyCacheKey, &policies, cacheExpire)
	}

	want := strings.ToLower(parts[1])
	matches := make([]EscalationPolicy, 0)

	// search over all policies looking for matching policy name, escalation
	// rule name, or service name
	for _, policy := range policies {
		// try matching the policy name
		lname := strings.ToLower(policy.Name)
		if strings.Contains(lname, want) {
			matches = append(matches, policy)
			continue
		}

		// try matching the escalation rule names
		for _, rule := range policy.EscalationRules {
			lname = strings.ToLower(rule.RuleObject.Name)
			if strings.Contains(lname, want) {
				matches = append(matches, policy)
				continue
			}
		}

		// try matching service names
		for _, svc := range policy.Services {
			lname = strings.ToLower(svc.Name)
			if strings.Contains(lname, want) {
				matches = append(matches, policy)
				continue
			}
		}
	}

	reply := formatOncallReply(want, matches)
	msg.Reply(reply)
}

func formatOncallReply(wanted string, policies []EscalationPolicy) string {
	age := int(hal.Cache().Age(PolicyCacheKey).Seconds())

	buf := bytes.NewBufferString(fmt.Sprintf("Results for %q (%d seconds ago)\n", wanted, age))

	for _, policy := range policies {
		buf.WriteString(policy.Name)
		buf.WriteString("\n")

		for _, oncall := range policy.OnCall {
			times := formatTimes(oncall.Start, oncall.End)
			indent := strings.Repeat("  ", oncall.Level) // indent deeper per level
			user := fmt.Sprintf("  %s%s: %s %s\n", indent, oncall.User.Name, oncall.User.Email, times)
			buf.WriteString(user)
		}

		buf.WriteString("\n")
	}

	return buf.String()
}

func formatTimes(st, et *time.Time) string {
	var start, end string
	if st != nil {
		start = st.Local().Format("2006-01-02")
	} else {
		return "always on call"
	}

	if et != nil {
		end = et.Local().Format("2006-01-02")
	} else {
		return "always on call"
	}

	return fmt.Sprintf("%s - %s", start, end)
}
<commit_msg>implement automatic caching of pagerduty escalation policies<commit_after>package pagerduty

/*
 * Copyright 2016 Albert P. Tobey <atobey@netflix.com>
 *
 * 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.
 */

import (
	"bytes"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/netflix/hal-9001/hal"
)

func Register() {
	pg := hal.Plugin{
		Name:  "page",
		Func:  page,
		Regex: "^[[:space:]]*!page",
	}
	pg.Register()

	oc := hal.Plugin{
		Name:  "oncall",
		Func:  oncall,
		Init:  cacheInit,
		Regex: "^[[:space:]]*!oncall",
	}
	oc.Register()
}

// the hal.secrets key that should contain the pagerduty auth token
const PagerdutyTokenKey = `pagerduty.token`

// the hal.secrets key that should contain the pagerduty account domain
const PagerdutyDomainKey = `pagerduty.domain`

// the key name used for caching the full escalation policy
const PolicyCacheKey = `pagerduty.policy_cache`

const PageUsage = `!page <alias> [optional message]

Send an alert via Pagerduty with an optional custom message.

Aliases that have a comma-separated list of service keys will result in one page going to each service key when the alias is paged.

!page core
!page core <message>
!pagecore HELP ME YOU ARE MY ONLY HOPE

!page add <alias> <service key>
!page add <alias> <service key>,<service_key>,<service_key>,...
!page rm <alias>
!page list
`

const OncallUsage = `!oncall <alias>

Find out who is oncall. If only one argument is provided, it must match
a known alias for a Pagerduty service. Otherwise, it is expected to be
a subcommand.

!oncall core
`

const PageDefaultMessage = `HAL: your presence is requested in the chat room.`

const cacheExpire = time.Minute * 10

// TODO: for now there is just one cache and periodic function, but once that is changed
// to allow multiple pagerduty domains/tokens, this will need to be split as well
const PeriodicFuncName = "pagerduty-cache-update-frequency"

const DefaultCacheInterval = "1h"

func page(msg hal.Evt) {
	parts := msg.BodyAsArgv()

	// detect concatenated command + team name & split them
	// e.g. "!pagecore" -> {"!page", "core"}
	if strings.HasPrefix(parts[0], "!page") && len(parts[0]) > 5 {
		team := strings.TrimPrefix(parts[0], "!page")
		parts = append([]string{"!page", team}, parts[1:]...)
	}

	// should be 2 parts now, "!page" and the target team
	if parts[0] != "!page" || len(parts) < 2 {
		msg.Reply(PageUsage)
		return
	}

	switch parts[1] {
	case "h", "help":
		msg.Reply(PageUsage)
	case "add":
		addAlias(msg, parts[2:])
	case "rm":
		rmAlias(msg, parts[2:])
	case "list":
		listAlias(msg)
	default:
		pageAlias(msg, parts[1:])
	}
}

func pageAlias(msg hal.Evt, parts []string) {
	pageMessage := PageDefaultMessage
	if len(parts) > 1 {
		pageMessage = strings.Join(parts, " ")
	}

	// map alias name to PD token via prefs
	key := aliasKey(parts[0])
	pref := msg.AsPref().FindKey(key).One()

	// make sure the query succeeded
	if !pref.Success {
		log.Println("%s", pref.String())
		msg.Replyf("Unable to access preferences: %q", pref.Error)
		return
	}

	// if qpref.Get returned the default, the alias was not found
	if pref.Value == "" {
		msg.Replyf("Alias %q not recognized. Try !page add <alias> <service key>", parts[0])
		return
	}

	// make sure the hal secrets are set up
	err := checkSecrets()
	if err != nil {
		msg.Error(err)
		return
	}

	// the value can be a list of tokens, separated by commas
	response := bytes.NewBuffer([]byte{})
	for _, svckey := range strings.Split(pref.Value, ",") {
		// get the Pagerduty auth token from the secrets API
		secrets := hal.Secrets()
		token := secrets.Get(PagerdutyTokenKey)
		if token == "" {
			msg.Replyf("Your Pagerduty auth token does not seem to be configured. Please add the %q secret.",
				PagerdutyTokenKey)
			return
		}

		// create the event and send it
		pde := NewTrigger(svckey, pageMessage) // in ./pagerduty.go
		resp, err := pde.Send(token)
		if err != nil {
			msg.Replyf("Error while communicating with Pagerduty. %d %s", resp.StatusCode, resp.Message)
			return
		}

		fmt.Fprintf(response, "%s\n", resp.Message)
	}

	// TODO: add some boilerplate around this
	msg.Reply(response.String())
}

func addAlias(msg hal.Evt, parts []string) {
	if len(parts) < 2 {
		msg.Replyf("!page add requires 2 arguments, e.g. !page add sysadmins XXXXXXX")
		return
	} else if len(parts) > 2 {
		keys := strings.Replace(strings.Join(parts[1:], ","), ",,", ",", len(parts)-2)
		parts = []string{parts[0], keys}
	}

	pref := msg.AsPref()
	pref.User = "" // filled in by AsPref and unwanted
	pref.Key = aliasKey(parts[0])
	pref.Value = parts[1]

	err := pref.Set()
	if err != nil {
		msg.Replyf("Write failed: %s", err)
	} else {
		msg.Replyf("Added alias: %q -> %q", parts[0], parts[1])
	}
}

func rmAlias(msg hal.Evt, parts []string) {
	if len(parts) != 1 {
		msg.Replyf("!page rm requires 1 argument, e.g. !page rm sysadmins")
		return
	}

	pref := msg.AsPref()
	pref.User = "" // filled in by AsPref and unwanted
	pref.Key = aliasKey(parts[0])
	pref.Delete()

	msg.Replyf("Removed alias %q", parts[0])
}

func listAlias(msg hal.Evt) {
	pref := msg.AsPref()
	pref.User = "" // filled in by AsPref and unwanted
	prefs := pref.GetPrefs()
	data := prefs.Table()
	msg.ReplyTable(data[0], data[1:])
}

func aliasKey(alias string) string {
	return fmt.Sprintf("alias.%s", alias)
}

func oncall(msg hal.Evt) {
	parts := msg.BodyAsArgv()

	if len(parts) == 1 {
		msg.Reply(OncallUsage)
		return
	} else if len(parts) != 2 {
		msg.Replyf("%s: invalid command.\n%s", parts[0], OncallUsage)
		return
	}

	// make sure the pagerduty token and domain are setup in hal.Secrets
	err := checkSecrets()
	if err != nil {
		msg.Error(err)
		return
	}

	msg.Replyf("Command: %q", parts[1])

	if parts[1] == "cache-now" {
		msg.Reply("Updating Pagerduty policy cache now.")
		cacheNow()
		msg.Reply("Pagerduty policy cache update complete.")
		return
	} else if parts[1] == "cache-status" {
		age := int(hal.Cache().Age(PolicyCacheKey).Seconds())
		next := time.Time{}
		status := "broken"
		pf := hal.GetPeriodicFunc(PeriodicFuncName)
		if pf != nil {
			next = pf.Last().Add(pf.Interval)
			status = pf.Status()
		}
		msg.Replyf("The cache is %d seconds old. Auto-update is %s and its next update is at %s.",
			age, status, next.Format(time.UnixDate))
		return
	}

	// TODO: look at the aliases set up for !page and try for an exact match
	// before doing fuzzy search -- move fuzzy search to a "search" subcommand
	// so it's clear that it is not precise
	want := strings.ToLower(parts[1])
	matches := make([]EscalationPolicy, 0)
	policies := getPolicyCache(false)

	// search over all policies looking for matching policy name, escalation
	// rule name, or service name
	for _, policy := range policies {
		// try matching the policy name
		lname := strings.ToLower(policy.Name)
		if strings.Contains(lname, want) {
			matches = append(matches, policy)
			continue
		}

		// try matching the escalation rule names
		for _, rule := range policy.EscalationRules {
			lname = strings.ToLower(rule.RuleObject.Name)
			if strings.Contains(lname, want) {
				matches = append(matches, policy)
				continue
			}
		}

		// try matching service names
		for _, svc := range policy.Services {
			lname = strings.ToLower(svc.Name)
			if strings.Contains(lname, want) {
				matches = append(matches, policy)
				continue
			}
		}
	}

	reply := formatOncallReply(want, matches)
	msg.Reply(reply)
}

func checkSecrets() error {
	secrets := hal.Secrets()
	token := secrets.Get(PagerdutyTokenKey)
	if token == "" {
		return fmt.Errorf("Your Pagerduty auth token does not seem to be configured. Please add the %q secret.", PagerdutyTokenKey)
	}

	domain := secrets.Get(PagerdutyDomainKey)
	if domain == "" {
		return fmt.Errorf("Your Pagerduty domain does not seem to be configured. Please add the %q secret.", PagerdutyDomainKey)
	}

	return nil
}

func getPolicyCache(forceUpdate bool) []EscalationPolicy {
	// see if there's a copy cached
	policies := []EscalationPolicy{}
	if hal.Cache().Exists(PolicyCacheKey) {
		ttl, _ := hal.Cache().Get(PolicyCacheKey, &policies)
		// TODO: maybe hal.Cache().Get should be careful to not modify the pointer if the ttl is expired...
		if ttl == 0 || forceUpdate {
			policies = []EscalationPolicy{}
		}
	}

	// the cache exists and is still valid, return it now
	if len(policies) > 0 {
		return policies
	}

	// TODO: consider making the token key per-room so different rooms can use different tokens
	// doing this will require a separate cache object per token...
	secrets := hal.Secrets()
	token := secrets.Get(PagerdutyTokenKey)
	domain := secrets.Get(PagerdutyDomainKey)

	// log and noop if the secrets aren't configured (yet)
	// the user-facing commands will report if they are missing
	if token == "" || domain == "" {
		log.Printf("pagerduty: Either the %s or %s is not set up in hal.Secrets. Returning empty list.",
			PagerdutyTokenKey, PagerdutyDomainKey)
		return []EscalationPolicy{}
	}

	// get all of the defined policies
	var err error
	policies, err = GetEscalationPolicies(token, domain)
	if err != nil {
		log.Printf("Returning empty list. REST call to Pagerduty failed: %s", err)
		return []EscalationPolicy{}
	}

	// TODO: make this configurable via prefs
	hal.Cache().Set(PolicyCacheKey, &policies, cacheExpire)

	return policies
}

func cacheInit(i *hal.Instance) {
	freqPref := hal.GetPref("", "", i.RoomId, "pagerduty", "cache-update-frequency", DefaultCacheInterval)

	td, err := time.ParseDuration(freqPref.Value)
	if err != nil {
		log.Panicf("BUG: could not parse freq stored in db: %q", freqPref.Value)
	}

	log.Printf("cacheInit called for pagerduty...")

	pf := hal.GetPeriodicFunc(PeriodicFuncName)
	if pf != nil {
		if pf.Status() != "running" {
			pf.Start()
		}
	} else {
		pf = &hal.PeriodicFunc{
			Name:     PeriodicFuncName,
			Interval: td,
			Function: cacheNow,
		}
		pf.Register()
		pf.Start()
	}

	// TODO: add a command to stop, etc.
}

func cacheNow() {
	getPolicyCache(true)
}

func formatOncallReply(wanted string, policies []EscalationPolicy) string {
	age := int(hal.Cache().Age(PolicyCacheKey).Seconds())

	buf := bytes.NewBufferString(fmt.Sprintf("Results for %q (%d seconds ago)\n", wanted, age))

	for _, policy := range policies {
		buf.WriteString(policy.Name)
		buf.WriteString("\n")

		for _, oncall := range policy.OnCall {
			times := formatTimes(oncall.Start, oncall.End)
			indent := strings.Repeat("  ", oncall.Level) // indent deeper per level
			user := fmt.Sprintf("  %s%s: %s %s\n", indent, oncall.User.Name, oncall.User.Email, times)
			buf.WriteString(user)
		}

		buf.WriteString("\n")
	}

	return buf.String()
}

func formatTimes(st, et *time.Time) string {
	var start, end string
	if st != nil {
		start = st.Local().Format("2006-01-02")
	} else {
		return "always on call"
	}

	if et != nil {
		end = et.Local().Format("2006-01-02")
	} else {
		return "always on call"
	}

	return fmt.Sprintf("%s - %s", start, end)
}
<|endoftext|>


<commit_before>package crypto

import (
	"crypto/rand"
	"errors"

	"github.com/NebulousLabs/ed25519"
)

const (
	EntropySize   = ed25519.EntropySize
	PublicKeySize = ed25519.PublicKeySize
	SecretKeySize = ed25519.SecretKeySize
	SignatureSize = ed25519.SignatureSize
)

type (
	PublicKey [ed25519.PublicKeySize]byte
	SecretKey [ed25519.SecretKeySize]byte
	Signature [ed25519.SignatureSize]byte
)

var (
	ErrInvalidSignature = errors.New("invalid signature")
	ErrRandUnexpected   = errors.New("unexpected result from random number generator")
)

// KeyPairGenerator is an interface that allows the caller to generate
// public-secret key pairs.
type KeyPairGenerator interface {
	Generate() (SecretKey, PublicKey, error)
	GenerateDetermistic(entropy [EntropySize]byte) (SecretKey, PublicKey)
}

// readBytesFunc is a function pointer that reads bytes into a buffer and
// returns the total number of bytes written to the buffer.
type readBytesFunc func([]byte) (int, error)

// deriveEd25519Func is a function pointer that matches the signature of
// ed25519.GenerateKey.
type deriveEd25519Func func([EntropySize]byte) (ed25519.SecretKey, ed25519.PublicKey)

// SignatureKeyGenerator is an implementation of KeyPairGenerator.
type SignatureKeyGenerator struct {
	readRandBytes readBytesFunc
	deriveKeyPair deriveEd25519Func
}

// NewSignatureKeyGenerator creates a new SignatureKeyGenerator type that uses
// random data and depends on the ed25519.GenerateKey function for deriving
// key pairs.
func NewSignatureKeyGenerator() SignatureKeyGenerator {
	return SignatureKeyGenerator{rand.Read, ed25519.GenerateKey}
}

// Generate creates a public-secret keypair that can be used to sign and verify
// messages.
func (skg SignatureKeyGenerator) Generate() (sk SecretKey, pk PublicKey, err error) {
	var entropy [EntropySize]byte
	written, err := skg.readRandBytes(entropy[:])
	if err != nil {
		return
	}
	if written != EntropySize {
		// readRandBytes did not fill the buffer. This should never happen.
		return sk, pk, ErrRandUnexpected
	}

	skPointer, pkPointer := skg.deriveKeyPair(entropy)
	return *skPointer, *pkPointer, nil
}

// GenerateDeterministic generates keys deterministically using the input
// entropy. The input entropy must be 32 bytes in length.
func (skg SignatureKeyGenerator) GenerateDeterministic(entropy [EntropySize]byte) (SecretKey, PublicKey) {
	skPointer, pkPointer := skg.deriveKeyPair(entropy)
	return *skPointer, *pkPointer
}

// SignHash signs a message using a secret key.
func SignHash(data Hash, sk SecretKey) (sig Signature, err error) {
	skNorm := [SecretKeySize]byte(sk)
	sig = *ed25519.Sign(&skNorm, data[:])
	return sig, nil
}

// VerifyHash uses a public key and input data to verify a signature.
func VerifyHash(data Hash, pk PublicKey, sig Signature) error {
	pkNorm := [PublicKeySize]byte(pk)
	sigNorm := [SignatureSize]byte(sig)
	verifies := ed25519.Verify(&pkNorm, data[:], &sigNorm)
	if !verifies {
		return ErrInvalidSignature
	}
	return nil
}

// PublicKey returns the public key that corresponds to a secret key.
func (sk SecretKey) PublicKey() (pk PublicKey) {
	copy(pk[:], sk[32:])
	return
}
<commit_msg>Revert "Removing unused ErrNilInput"<commit_after>package crypto

import (
	"crypto/rand"
	"errors"

	"github.com/NebulousLabs/ed25519"
)

const (
	EntropySize   = ed25519.EntropySize
	PublicKeySize = ed25519.PublicKeySize
	SecretKeySize = ed25519.SecretKeySize
	SignatureSize = ed25519.SignatureSize
)

type (
	PublicKey [ed25519.PublicKeySize]byte
	SecretKey [ed25519.SecretKeySize]byte
	Signature [ed25519.SignatureSize]byte
)

var (
	ErrNilInput         = errors.New("cannot use nil input")
	ErrInvalidSignature = errors.New("invalid signature")
	ErrRandUnexpected   = errors.New("unexpected result from random number generator")
)

// KeyPairGenerator is an interface that allows the caller to generate
// public-secret key pairs.
type KeyPairGenerator interface {
	Generate() (SecretKey, PublicKey, error)
	GenerateDetermistic(entropy [EntropySize]byte) (SecretKey, PublicKey)
}

// readBytesFunc is a function pointer that reads bytes into a buffer and
// returns the total number of bytes written to the buffer.
type readBytesFunc func([]byte) (int, error)

// deriveEd25519Func is a function pointer that matches the signature of
// ed25519.GenerateKey.
type deriveEd25519Func func([EntropySize]byte) (ed25519.SecretKey, ed25519.PublicKey)

// SignatureKeyGenerator is an implementation of KeyPairGenerator.
type SignatureKeyGenerator struct {
	readRandBytes readBytesFunc
	deriveKeyPair deriveEd25519Func
}

// NewSignatureKeyGenerator creates a new SignatureKeyGenerator type that uses
// random data and depends on the ed25519.GenerateKey function for deriving
// key pairs.
func NewSignatureKeyGenerator() SignatureKeyGenerator {
	return SignatureKeyGenerator{rand.Read, ed25519.GenerateKey}
}

// Generate creates a public-secret keypair that can be used to sign and verify
// messages.
func (skg SignatureKeyGenerator) Generate() (sk SecretKey, pk PublicKey, err error) {
	var entropy [EntropySize]byte
	written, err := skg.readRandBytes(entropy[:])
	if err != nil {
		return
	}
	if written != EntropySize {
		// readRandBytes did not fill the buffer. This should never happen.
		return sk, pk, ErrRandUnexpected
	}

	skPointer, pkPointer := skg.deriveKeyPair(entropy)
	return *skPointer, *pkPointer, nil
}

// GenerateDeterministic generates keys deterministically using the input
// entropy. The input entropy must be 32 bytes in length.
func (skg SignatureKeyGenerator) GenerateDeterministic(entropy [EntropySize]byte) (SecretKey, PublicKey) {
	skPointer, pkPointer := skg.deriveKeyPair(entropy)
	return *skPointer, *pkPointer
}

// SignHash signs a message using a secret key.
func SignHash(data Hash, sk SecretKey) (sig Signature, err error) {
	skNorm := [SecretKeySize]byte(sk)
	sig = *ed25519.Sign(&skNorm, data[:])
	return sig, nil
}

// VerifyHash uses a public key and input data to verify a signature.
func VerifyHash(data Hash, pk PublicKey, sig Signature) error {
	pkNorm := [PublicKeySize]byte(pk)
	sigNorm := [SignatureSize]byte(sig)
	verifies := ed25519.Verify(&pkNorm, data[:], &sigNorm)
	if !verifies {
		return ErrInvalidSignature
	}
	return nil
}

// PublicKey returns the public key that corresponds to a secret key.
func (sk SecretKey) PublicKey() (pk PublicKey) {
	copy(pk[:], sk[32:])
	return
}
<|endoftext|>


<commit_before>package ifconfig

import (
    "strings"
    "io/ioutil"
    "regexp"
    "strconv"
//    "fmt"
)

var myregexp = regexp.MustCompile(`(.+?):\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)`)

func net_io_counters() (iface_counter_map map[string] Counters) {
    ra, _ := ioutil.ReadFile("/proc/net/dev")
    list := strings.Split(strings.TrimSpace(string(ra)),"\n")

    iface_counter_map = make(map[string]Counters)

    for index,value := range list {
        if index > 1 {
            counter := Counters{}

            list_a := myregexp.FindStringSubmatch(strings.TrimSpace(value))

            counter.BytesRecv, _ = strconv.Atoi(list_a[2])
            counter.PacketsRecv, _ = strconv.Atoi(list_a[3])
            counter.Errin, _        = strconv.Atoi(list_a[4])
            counter.Dropin, _       = strconv.Atoi(list_a[5])
            counter.BytesSent, _   = strconv.Atoi(list_a[9])
            counter.PacketsSent, _ = strconv.Atoi(list_a[10])
            counter.Errout, _       = strconv.Atoi(list_a[11])
            counter.Dropout, _      = strconv.Atoi(list_a[12])

            iface_counter_map[list_a[1]] = counter
//            fmt.Println(list_a[1])
        }
    }
//    fmt.Println(iface_counter_map)

    return iface_counter_map
}
<commit_msg>fix: error array index<commit_after>package ifconfig

import (
    "strings"
    "io/ioutil"
    "regexp"
    "strconv"
//    "fmt"
)

var myregexp = regexp.MustCompile(`(.+?):\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)`)

func net_io_counters() (iface_counter_map map[string] Counters) {
    ra, _ := ioutil.ReadFile("/proc/net/dev")
    list := strings.Split(strings.TrimSpace(string(ra)),"\n")

    iface_counter_map = make(map[string]Counters)

    for index,value := range list {
        if index > 1 {
            counter := Counters{}

            list_a := myregexp.FindStringSubmatch(strings.TrimSpace(value))

            counter.BytesRecv, _ = strconv.Atoi(list_a[2])
            counter.PacketsRecv, _ = strconv.Atoi(list_a[3])
            counter.Errin, _        = strconv.Atoi(list_a[4])
            counter.Dropin, _       = strconv.Atoi(list_a[5])
            counter.BytesSent, _   = strconv.Atoi(list_a[10])
            counter.PacketsSent, _ = strconv.Atoi(list_a[11])
            counter.Errout, _       = strconv.Atoi(list_a[12])
            counter.Dropout, _      = strconv.Atoi(list_a[13])

            iface_counter_map[list_a[1]] = counter
//            fmt.Println(list_a[1])
        }
    }
//    fmt.Println(iface_counter_map)

    return iface_counter_map
}
<|endoftext|>


<commit_before>// Package hypergo is a client for HyperDex.
package hypergo

/*
#cgo LDFLAGS: -lhyperdex-client
#include <netinet/in.h>
#include "hyperdex/client.h"
#include "hyperdex/datastructures.h"
*/
import "C"

import (
	"fmt"
	"io"
	"log"
	"runtime"
)

// CHANNEL_BUFFER_SIZE is the size of all the returned channels' buffer.
// You can set it to 0 if you want unbuffered channels.
var CHANNEL_BUFFER_SIZE = 1

// Timeout in miliseconds.
// Negative timeout means no timeout.
var TIMEOUT = -1

// Predicates
const (
	FAIL                 = C.HYPERPREDICATE_FAIL
	EQUALS               = C.HYPERPREDICATE_EQUALS
	LESS_EQUAL           = C.HYPERPREDICATE_LESS_EQUAL
	GREATER_EQUAL        = C.HYPERPREDICATE_GREATER_EQUAL
	CONTAINS_LESS_THAN   = C.HYPERPREDICATE_CONTAINS_LESS_THAN // alias of LENGTH_LESS_EQUAL
	REGEX                = C.HYPERPREDICATE_REGEX
	LENGTH_EQUALS        = C.HYPERPREDICATE_LENGTH_EQUALS
	LENGTH_LESS_EQUAL    = C.HYPERPREDICATE_LENGTH_LESS_EQUAL
	LENGTH_GREATER_EQUAL = C.HYPERPREDICATE_LENGTH_GREATER_EQUAL
	CONTAINS             = C.HYPERPREDICATE_CONTAINS
)

// Client is the hyperdex client used to make requests to hyperdex.
type Client struct {
	ptr       *C.struct_hyperdex_client
	arena     *C.struct_hyperdex_ds_arena
	requests  []request
	closeChan chan struct{}
}

// Attributes represents a map of key-value attribute pairs.
//
// The value must be either a string or an int64-compatible integer
// (int, int8, int16, int32, int64, uint8, uint16, uint32).
// An incompatible type will NOT result in a panic but in a regular error return.
//
// Please note that there is no support for uint64 since its negative might be incorrectly evaluated.
// Support for uint has been dropped because it is unspecified whether it is 32 or 64 bits.

// Correspond to an array of hyperdex_client_attribute
type Attributes map[string]interface{}

type Map map[interface{}]interface{}

type List []interface{}

type Set []interface{}

type MapItem struct {
	Key   interface{}
	Value interface{}
}

// A hyperdex object.
// Err contains any error that happened when trying to retrieve
// this object.
type Object struct {
	Err   error
	Key   string
	Attrs Attributes
}

// Read-only channel of objects
type ObjectChannel <-chan Object

// Read-only channel of errors
type ErrorChannel <-chan error

// Correspond to a hyperdex_client_attribute_check
type Condition struct {
	Attr      string
	Value     interface{}
	Predicate int
}

// An outstanding request that contains three callback functions:
// success, failure, and complete.  success() is called when
// hyperdex returns HYPERDEX_CLIENT_SUCCESS, and failure is called
// in all other cases.
//
// The boolean flag isIterator signifies whether the request is
// that of an iterator, namely hyperdex_client_search and its
// variants.
//
// complete() is called after success() if the request is not
// an iterator, or after receiving HYPREDEX_SEARCH_DONE if the
// request is an iterator.
type request struct {
	id         int64
	isIterator bool
	success    func()
	failure    func(C.enum_hyperdex_client_returncode, string)
	complete   func()
	status     *C.enum_hyperdex_client_returncode
}

// A custom error type that allows for examining HyperDex error code.
type HyperError struct {
	returnCode C.enum_hyperdex_client_returncode
	msg        string
}

func (e HyperError) Error() string {
	return fmt.Sprintf("Error %d: %s", e.returnCode, e.msg)
}

// Set output of log.  Simply a wrapper around log.SetOutput.
func SetLogOutput(w io.Writer) {
	log.SetOutput(w)
}

// NewClient initializes a hyperdex client ready to use.
//
// For every call to NewClient, there must be a call to Destroy.
//
// Panics when the internal looping goroutine receives an error from hyperdex.
//
// Example:
// 		client, err := hyperdex_client.NewClient("127.0.0.1", 1234)
// 		if err != nil {
//			//handle error
//		}
//		defer client.Destroy()
//		// use client
func NewClient(ip string, port int) (*Client, error) {
	C_client := C.hyperdex_client_create(C.CString(ip), C.uint16_t(port))
	C_arena := C.hyperdex_ds_arena_create()

	if C_client == nil {
		return nil, fmt.Errorf("Could not create hyperdex_client (ip=%s, port=%d)", ip, port)
	}
	client := &Client{
		C_client,
		C_arena,
		make([]request, 0, 8), // No reallocation within 8 concurrent requests to hyperdex_client_loop
		make(chan struct{}, 1),
	}

	go func() {
		for {
			select {
			// quit goroutine when client is destroyed
			case <-client.closeChan:
				return
			default:
				// check if there are pending requests
				// and only if there are, call hyperdex_client_loop
				if len(client.requests) > 0 {
					var status C.enum_hyperdex_client_returncode
					ret := int64(C.hyperdex_client_loop(client.ptr, C.int(TIMEOUT), &status))
					//log.Printf("hyperdex_client_loop(%X, %d, %X) -> %d\n", unsafe.Pointer(client.ptr), hyperdex_client_loop_timeout, unsafe.Pointer(&status), ret)
					if ret < 0 {
						panic(newInternalError(status,
							C.GoString(C.hyperdex_client_error_message(client.ptr))).Error())
					}
					// find processed request among pending requests
					for i, req := range client.requests {
						if req.id == ret {
							log.Printf("Processing request %v\n", req.id)
							log.Printf("Loop status: %v\n", status)
							log.Printf("Request status: %v\n", *req.status)
							if status == C.HYPERDEX_CLIENT_SUCCESS {
								switch *req.status {
								case C.HYPERDEX_CLIENT_SUCCESS:
									log.Println("Request success")
									if req.success != nil {
										req.success()
									}
									if req.isIterator {
										// We want to break out at here so that the
										// request won't get removed
										goto SKIP_DELETING_REQUEST
									} else if req.complete != nil {
										// We want to break out at here so that the
										// request won't get removed
										req.complete()
									}
								case C.HYPERDEX_CLIENT_SEARCHDONE:
									log.Println("Request search done")
									if req.complete != nil {
										req.complete()
									}
								case C.HYPERDEX_CLIENT_CMPFAIL:
									log.Println("Comparison failure")
									if req.failure != nil {
										req.failure(*req.status,
											C.GoString(C.hyperdex_client_error_message(client.ptr)))
									}
								default:
									log.Println("Request failure")
									if req.failure != nil {
										req.failure(*req.status,
											C.GoString(C.hyperdex_client_error_message(client.ptr)))
									}
								}
							} else if req.failure != nil {
								req.failure(status,
									C.GoString(C.hyperdex_client_error_message(client.ptr)))
							}
							client.requests = append(client.requests[:i], client.requests[i+1:]...)
						SKIP_DELETING_REQUEST:
							break
						}
					}
				}
				// prevent other goroutines from starving
				runtime.Gosched()
			}
		}
		panic("Should not be reached: end of infinite loop")
	}()

	return client, nil
}

// Destroy closes the connection between the Client and hyperdex. It has to be used on a client that is not used anymore.
//
// For every call to NewClient, there must be a call to Destroy.
func (client *Client) Destroy() {
	close(client.closeChan)
	C.hyperdex_client_destroy(client.ptr)
	//log.Printf("hyperdex_client_destroy(%X)\n", unsafe.Pointer(client.ptr))
}
<commit_msg>Get rid of a useless struct<commit_after>// Package hypergo is a client for HyperDex.
package hypergo

/*
#cgo LDFLAGS: -lhyperdex-client
#include <netinet/in.h>
#include "hyperdex/client.h"
#include "hyperdex/datastructures.h"
*/
import "C"

import (
	"fmt"
	"io"
	"log"
	"runtime"
)

// CHANNEL_BUFFER_SIZE is the size of all the returned channels' buffer.
// You can set it to 0 if you want unbuffered channels.
var CHANNEL_BUFFER_SIZE = 1

// Timeout in miliseconds.
// Negative timeout means no timeout.
var TIMEOUT = -1

// Predicates
const (
	FAIL                 = C.HYPERPREDICATE_FAIL
	EQUALS               = C.HYPERPREDICATE_EQUALS
	LESS_EQUAL           = C.HYPERPREDICATE_LESS_EQUAL
	GREATER_EQUAL        = C.HYPERPREDICATE_GREATER_EQUAL
	CONTAINS_LESS_THAN   = C.HYPERPREDICATE_CONTAINS_LESS_THAN // alias of LENGTH_LESS_EQUAL
	REGEX                = C.HYPERPREDICATE_REGEX
	LENGTH_EQUALS        = C.HYPERPREDICATE_LENGTH_EQUALS
	LENGTH_LESS_EQUAL    = C.HYPERPREDICATE_LENGTH_LESS_EQUAL
	LENGTH_GREATER_EQUAL = C.HYPERPREDICATE_LENGTH_GREATER_EQUAL
	CONTAINS             = C.HYPERPREDICATE_CONTAINS
)

// Client is the hyperdex client used to make requests to hyperdex.
type Client struct {
	ptr       *C.struct_hyperdex_client
	arena     *C.struct_hyperdex_ds_arena
	requests  []request
	closeChan chan struct{}
}

// Attributes represents a map of key-value attribute pairs.
//
// The value must be either a string or an int64-compatible integer
// (int, int8, int16, int32, int64, uint8, uint16, uint32).
// An incompatible type will NOT result in a panic but in a regular error return.
//
// Please note that there is no support for uint64 since its negative might be incorrectly evaluated.
// Support for uint has been dropped because it is unspecified whether it is 32 or 64 bits.

// Correspond to an array of hyperdex_client_attribute
type Attributes map[string]interface{}

type Map map[interface{}]interface{}

type List []interface{}

type Set []interface{}

// A hyperdex object.
// Err contains any error that happened when trying to retrieve
// this object.
type Object struct {
	Err   error
	Key   string
	Attrs Attributes
}

// Read-only channel of objects
type ObjectChannel <-chan Object

// Read-only channel of errors
type ErrorChannel <-chan error

// Correspond to a hyperdex_client_attribute_check
type Condition struct {
	Attr      string
	Value     interface{}
	Predicate int
}

// An outstanding request that contains three callback functions:
// success, failure, and complete.  success() is called when
// hyperdex returns HYPERDEX_CLIENT_SUCCESS, and failure is called
// in all other cases.
//
// The boolean flag isIterator signifies whether the request is
// that of an iterator, namely hyperdex_client_search and its
// variants.
//
// complete() is called after success() if the request is not
// an iterator, or after receiving HYPREDEX_SEARCH_DONE if the
// request is an iterator.
type request struct {
	id         int64
	isIterator bool
	success    func()
	failure    func(C.enum_hyperdex_client_returncode, string)
	complete   func()
	status     *C.enum_hyperdex_client_returncode
}

// A custom error type that allows for examining HyperDex error code.
type HyperError struct {
	returnCode C.enum_hyperdex_client_returncode
	msg        string
}

func (e HyperError) Error() string {
	return fmt.Sprintf("Error %d: %s", e.returnCode, e.msg)
}

// Set output of log.  Simply a wrapper around log.SetOutput.
func SetLogOutput(w io.Writer) {
	log.SetOutput(w)
}

// NewClient initializes a hyperdex client ready to use.
//
// For every call to NewClient, there must be a call to Destroy.
//
// Panics when the internal looping goroutine receives an error from hyperdex.
//
// Example:
// 		client, err := hyperdex_client.NewClient("127.0.0.1", 1234)
// 		if err != nil {
//			//handle error
//		}
//		defer client.Destroy()
//		// use client
func NewClient(ip string, port int) (*Client, error) {
	C_client := C.hyperdex_client_create(C.CString(ip), C.uint16_t(port))
	C_arena := C.hyperdex_ds_arena_create()

	if C_client == nil {
		return nil, fmt.Errorf("Could not create hyperdex_client (ip=%s, port=%d)", ip, port)
	}
	client := &Client{
		C_client,
		C_arena,
		make([]request, 0, 8), // No reallocation within 8 concurrent requests to hyperdex_client_loop
		make(chan struct{}, 1),
	}

	go func() {
		for {
			select {
			// quit goroutine when client is destroyed
			case <-client.closeChan:
				return
			default:
				// check if there are pending requests
				// and only if there are, call hyperdex_client_loop
				if len(client.requests) > 0 {
					var status C.enum_hyperdex_client_returncode
					ret := int64(C.hyperdex_client_loop(client.ptr, C.int(TIMEOUT), &status))
					//log.Printf("hyperdex_client_loop(%X, %d, %X) -> %d\n", unsafe.Pointer(client.ptr), hyperdex_client_loop_timeout, unsafe.Pointer(&status), ret)
					if ret < 0 {
						panic(newInternalError(status,
							C.GoString(C.hyperdex_client_error_message(client.ptr))).Error())
					}
					// find processed request among pending requests
					for i, req := range client.requests {
						if req.id == ret {
							log.Printf("Processing request %v\n", req.id)
							log.Printf("Loop status: %v\n", status)
							log.Printf("Request status: %v\n", *req.status)
							if status == C.HYPERDEX_CLIENT_SUCCESS {
								switch *req.status {
								case C.HYPERDEX_CLIENT_SUCCESS:
									log.Println("Request success")
									if req.success != nil {
										req.success()
									}
									if req.isIterator {
										// We want to break out at here so that the
										// request won't get removed
										goto SKIP_DELETING_REQUEST
									} else if req.complete != nil {
										// We want to break out at here so that the
										// request won't get removed
										req.complete()
									}
								case C.HYPERDEX_CLIENT_SEARCHDONE:
									log.Println("Request search done")
									if req.complete != nil {
										req.complete()
									}
								case C.HYPERDEX_CLIENT_CMPFAIL:
									log.Println("Comparison failure")
									if req.failure != nil {
										req.failure(*req.status,
											C.GoString(C.hyperdex_client_error_message(client.ptr)))
									}
								default:
									log.Println("Request failure")
									if req.failure != nil {
										req.failure(*req.status,
											C.GoString(C.hyperdex_client_error_message(client.ptr)))
									}
								}
							} else if req.failure != nil {
								req.failure(status,
									C.GoString(C.hyperdex_client_error_message(client.ptr)))
							}
							client.requests = append(client.requests[:i], client.requests[i+1:]...)
						SKIP_DELETING_REQUEST:
							break
						}
					}
				}
				// prevent other goroutines from starving
				runtime.Gosched()
			}
		}
		panic("Should not be reached: end of infinite loop")
	}()

	return client, nil
}

// Destroy closes the connection between the Client and hyperdex. It has to be used on a client that is not used anymore.
//
// For every call to NewClient, there must be a call to Destroy.
func (client *Client) Destroy() {
	close(client.closeChan)
	C.hyperdex_client_destroy(client.ptr)
	//log.Printf("hyperdex_client_destroy(%X)\n", unsafe.Pointer(client.ptr))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"imposm3/cache"
	golog "log"
	"os"
	"runtime"

	"imposm3/cache/query"
	"imposm3/config"
	"imposm3/diff"
	"imposm3/geom/limit"
	"imposm3/import_"
	"imposm3/logging"
	"imposm3/stats"
)

var log = logging.NewLogger("")

func printCmds() {
	fmt.Fprintf(os.Stderr, "Usage: %s COMMAND [args]\n\n", os.Args[0])
	fmt.Println("Available commands:")
	fmt.Println("\timport")
	fmt.Println("\tdiff")
	fmt.Println("\tquery-cache")
	fmt.Println("\tversion")
}

func main() {

	golog.SetFlags(golog.LstdFlags | golog.Lshortfile)
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	if len(os.Args) <= 1 {
		printCmds()
		logging.Shutdown()
		os.Exit(1)
	}

	switch os.Args[1] {
	case "import":
		config.ParseImport(os.Args[2:])
		if config.BaseOptions.Httpprofile != "" {
			stats.StartHttpPProf(config.BaseOptions.Httpprofile)
		}
		import_.Import()
	case "diff":
		config.ParseDiffImport(os.Args[2:])

		if config.BaseOptions.Httpprofile != "" {
			stats.StartHttpPProf(config.BaseOptions.Httpprofile)
		}

		if config.BaseOptions.Quiet {
			logging.SetQuiet(true)
		}

		var geometryLimiter *limit.Limiter
		if config.BaseOptions.LimitTo != "" {
			var err error
			step := log.StartStep("Reading limitto geometries")
			geometryLimiter, err = limit.NewFromGeoJsonWithBuffered(
				config.BaseOptions.LimitTo,
				config.BaseOptions.LimitToCacheBuffer,
			)
			if err != nil {
				log.Fatal(err)
			}
			log.StopStep(step)
		}
		osmCache := cache.NewOSMCache(config.BaseOptions.CacheDir)
		err := osmCache.Open()
		if err != nil {
			log.Fatal("osm cache: ", err)
		}
		defer osmCache.Close()

		diffCache := cache.NewDiffCache(config.BaseOptions.CacheDir)
		err = diffCache.Open()
		if err != nil {
			log.Fatal("diff cache: ", err)
		}

		for _, oscFile := range config.DiffFlags.Args() {
			err := diff.Update(oscFile, geometryLimiter, nil, osmCache, diffCache, false)
			if err != nil {
				osmCache.Close()
				diffCache.Close()
				log.Fatal(err)
			}
		}
		// explicitly Close since os.Exit prevents defers
		osmCache.Close()
		diffCache.Close()

	case "query-cache":
		query.Query(os.Args[2:])
	case "version":
		fmt.Println(imposmVersion)
		os.Exit(0)
	default:
		printCmds()
		log.Fatalf("invalid command: '%s'", os.Args[1])
	}
	logging.Shutdown()
	os.Exit(0)

}
<commit_msg>make main callable from outside for testing<commit_after>package main

import (
	"fmt"
	"imposm3/cache"
	golog "log"
	"os"
	"runtime"

	"imposm3/cache/query"
	"imposm3/config"
	"imposm3/diff"
	"imposm3/geom/limit"
	"imposm3/import_"
	"imposm3/logging"
	"imposm3/stats"
)

var log = logging.NewLogger("")

func PrintCmds() {
	fmt.Fprintf(os.Stderr, "Usage: %s COMMAND [args]\n\n", os.Args[0])
	fmt.Println("Available commands:")
	fmt.Println("\timport")
	fmt.Println("\tdiff")
	fmt.Println("\tquery-cache")
	fmt.Println("\tversion")
}

func main() {
	Main(PrintCmds)
}

func Main(usage func()) {
	golog.SetFlags(golog.LstdFlags | golog.Lshortfile)
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	if len(os.Args) <= 1 {
		usage()
		logging.Shutdown()
		os.Exit(1)
	}

	switch os.Args[1] {
	case "import":
		config.ParseImport(os.Args[2:])
		if config.BaseOptions.Httpprofile != "" {
			stats.StartHttpPProf(config.BaseOptions.Httpprofile)
		}
		import_.Import()
	case "diff":
		config.ParseDiffImport(os.Args[2:])

		if config.BaseOptions.Httpprofile != "" {
			stats.StartHttpPProf(config.BaseOptions.Httpprofile)
		}

		if config.BaseOptions.Quiet {
			logging.SetQuiet(true)
		}

		var geometryLimiter *limit.Limiter
		if config.BaseOptions.LimitTo != "" {
			var err error
			step := log.StartStep("Reading limitto geometries")
			geometryLimiter, err = limit.NewFromGeoJsonWithBuffered(
				config.BaseOptions.LimitTo,
				config.BaseOptions.LimitToCacheBuffer,
			)
			if err != nil {
				log.Fatal(err)
			}
			log.StopStep(step)
		}
		osmCache := cache.NewOSMCache(config.BaseOptions.CacheDir)
		err := osmCache.Open()
		if err != nil {
			log.Fatal("osm cache: ", err)
		}
		defer osmCache.Close()

		diffCache := cache.NewDiffCache(config.BaseOptions.CacheDir)
		err = diffCache.Open()
		if err != nil {
			log.Fatal("diff cache: ", err)
		}

		for _, oscFile := range config.DiffFlags.Args() {
			err := diff.Update(oscFile, geometryLimiter, nil, osmCache, diffCache, false)
			if err != nil {
				osmCache.Close()
				diffCache.Close()
				log.Fatal(err)
			}
		}
		// explicitly Close since os.Exit prevents defers
		osmCache.Close()
		diffCache.Close()

	case "query-cache":
		query.Query(os.Args[2:])
	case "version":
		fmt.Println(imposmVersion)
		os.Exit(0)
	default:
		usage()
		log.Fatalf("invalid command: '%s'", os.Args[1])
	}
	logging.Shutdown()
	os.Exit(0)

}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tchannel

import (
	"errors"
	"fmt"
	"io"
	"time"

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

var errInboundRequestAlreadyActive = errors.New("inbound request is already active; possible duplicate client id")

// handleCallReq handles an incoming call request, registering a message
// exchange to receive further fragments for that call, and dispatching it in
// another goroutine
func (c *Connection) handleCallReq(frame *Frame) bool {
	switch state := c.readState(); state {
	case connectionActive:
		break
	case connectionStartClose, connectionInboundClosed, connectionClosed:
		c.SendSystemError(frame.Header.ID, nil, ErrChannelClosed)
		return true
	case connectionWaitingToRecvInitReq, connectionWaitingToSendInitReq, connectionWaitingToRecvInitRes:
		c.SendSystemError(frame.Header.ID, nil, NewSystemError(ErrCodeDeclined, "connection not ready"))
		return true
	default:
		panic(fmt.Errorf("unknown connection state for call req: %v", state))
	}

	callReq := new(callReq)
	initialFragment, err := parseInboundFragment(c.framePool, frame, callReq)
	if err != nil {
		// TODO(mmihic): Probably want to treat this as a protocol error
		c.log.Errorf("could not decode %s: %v", frame.Header, err)
		return true
	}

	c.log.Debugf("span=%s", callReq.Tracing)
	call := new(InboundCall)
	ctx, cancel := newIncomingContext(call, callReq.TimeToLive, &callReq.Tracing)

	mex, err := c.inbound.newExchange(ctx, c.framePool, callReq.messageType(), frame.Header.ID, 512)
	if err != nil {
		if err == errDuplicateMex {
			err = errInboundRequestAlreadyActive
		}
		c.log.Errorf("could not register exchange for %s", frame.Header)
		c.SendSystemError(frame.Header.ID, nil, err)
		return true
	}

	// Close may have been called between the time we checked the state and us creating the exchange.
	if c.readState() != connectionActive {
		mex.shutdown()
		return true
	}

	call.AddBinaryAnnotation(BinaryAnnotation{Key: "cn", Value: callReq.Headers[CallerName]})
	call.AddBinaryAnnotation(BinaryAnnotation{Key: "as", Value: callReq.Headers[ArgScheme]})
	call.AddAnnotation(AnnotationKeyServerReceive)
	response := new(InboundCallResponse)
	response.mex = mex
	response.conn = c
	response.contents = newFragmentingWriter(response, initialFragment.checksumType.New())
	response.cancel = cancel
	response.span = callReq.Tracing
	response.log = c.log.WithFields(LogField{"In-Response", callReq.ID()})
	response.headers = transportHeaders{}
	response.messageForFragment = func(initial bool) message {
		if initial {
			call.AddAnnotation(AnnotationKeyServerSend)
			targetEndpoint := TargetEndpoint{
				HostPort:    c.localPeerInfo.HostPort,
				ServiceName: call.serviceName,
				Operation:   string(call.Operation()),
			}
			call.Report(callReq.Tracing, targetEndpoint, c.traceReporter)

			callRes := new(callRes)
			callRes.Headers = response.headers
			callRes.ResponseCode = responseOK
			if response.applicationError {
				callRes.ResponseCode = responseApplicationError
			}
			return callRes
		}

		return new(callResContinue)
	}

	call.mex = mex
	call.initialFragment = initialFragment
	call.serviceName = string(callReq.Service)
	call.headers = callReq.Headers
	call.span = callReq.Tracing
	call.response = response
	call.log = c.log.WithFields(LogField{"In-Call", callReq.ID()})
	call.messageForFragment = func(initial bool) message { return new(callReqContinue) }
	call.contents = newFragmentingReader(call)
	call.statsReporter = c.statsReporter
	call.createStatsTags(c.commonStatsTags)

	response.statsReporter = c.statsReporter
	response.commonStatsTags = call.commonStatsTags

	setResponseHeaders(call.headers, response.headers)
	go c.dispatchInbound(c.connID, callReq.ID(), call)
	return false
}

// handleCallReqContinue handles the continuation of a call request, forwarding
// it to the request channel for that request, where it can be pulled during
// defragmentation
func (c *Connection) handleCallReqContinue(frame *Frame) bool {
	if err := c.inbound.forwardPeerFrame(frame); err != nil {
		c.inbound.removeExchange(frame.Header.ID)
		return true
	}
	return false
}

// createStatsTags creates the common stats tags, if they are not already created.
func (call *InboundCall) createStatsTags(connectionTags map[string]string) {
	call.commonStatsTags = map[string]string{
		"calling-service": call.CallerName(),
	}
	for k, v := range connectionTags {
		call.commonStatsTags[k] = v
	}
}

// dispatchInbound ispatches an inbound call to the appropriate handler
func (c *Connection) dispatchInbound(_ uint32, _ uint32, call *InboundCall) {
	c.log.Debugf("Received incoming call for %s from %s", call.ServiceName(), c.remotePeerInfo)

	if err := call.readOperation(); err != nil {
		c.log.Errorf("Could not read operation from %s: %v", c.remotePeerInfo, err)
		return
	}

	call.commonStatsTags["endpoint"] = string(call.operation)
	call.statsReporter.IncCounter("inbound.calls.recvd", call.commonStatsTags, 1)
	call.response.calledAt = timeNow()

	// NB(mmihic): Don't cast operation name to string here - this will
	// create a copy of the byte array, where as aliasing to string in the
	// map look up can be optimized by the compiler to avoid the copy.  See
	// https://github.com/golang/go/issues/3512
	h := c.handlers.find(call.ServiceName(), call.Operation())
	if h == nil {
		// CHeck the subchannel map to see if we find one there
		c.log.Debugf("Checking the subchannel's handlers for %s:%s", call.ServiceName(), call.Operation())
		h = c.subchannels.find(call.ServiceName(), call.Operation())
	}
	if h == nil {
		c.log.Errorf("Could not find handler for %s:%s", call.ServiceName(), call.Operation())
		call.mex.shutdown()
		call.Response().SendSystemError(
			NewSystemError(ErrCodeBadRequest, "no handler for service %q and operation %q", call.ServiceName(), call.Operation()))
		return
	}

	// TODO(prashant): This is an expensive way to check for cancellation. Use a heap for timeouts.
	go func() {
		if <-call.mex.ctx.Done(); call.mex.ctx.Err() == context.DeadlineExceeded {
			call.mex.inboundTimeout()
		}
	}()

	c.log.Debugf("Dispatching %s:%s from %s", call.ServiceName(), call.Operation(), c.remotePeerInfo)
	h.Handle(call.mex.ctx, call)
}

// An InboundCall is an incoming call from a peer
type InboundCall struct {
	Annotations
	reqResReader

	response        *InboundCallResponse
	serviceName     string
	operation       []byte
	headers         transportHeaders
	span            Span
	statsReporter   StatsReporter
	commonStatsTags map[string]string
}

// ServiceName returns the name of the service being called
func (call *InboundCall) ServiceName() string {
	return call.serviceName
}

// Operation returns the operation being called
func (call *InboundCall) Operation() []byte {
	return call.operation
}

// Format the format of the request from the ArgScheme transport header.
func (call *InboundCall) Format() Format {
	return Format(call.headers[ArgScheme])
}

// CallerName returns the caller name from the CallerName transport header.
func (call *InboundCall) CallerName() string {
	return call.headers[CallerName]
}

// ShardKey returns the shard key from the ShardKey transport header.
func (call *InboundCall) ShardKey() string {
	return call.headers[ShardKey]
}

// Reads the entire operation name (arg1) from the request stream.
func (call *InboundCall) readOperation() error {
	var arg1 []byte
	if err := NewArgReader(call.arg1Reader()).Read(&arg1); err != nil {
		return call.failed(err)
	}

	call.operation = arg1
	return nil
}

// Arg2Reader returns an io.ReadCloser to read the second argument.
// The ReadCloser must be closed once the argument has been read.
func (call *InboundCall) Arg2Reader() (io.ReadCloser, error) {
	return call.arg2Reader()
}

// Arg3Reader returns an io.ReadCloser to read the last argument.
// The ReadCloser must be closed once the argument has been read.
func (call *InboundCall) Arg3Reader() (io.ReadCloser, error) {
	return call.arg3Reader()
}

// Response provides access to the InboundCallResponse object which can be used
// to write back to the calling peer
func (call *InboundCall) Response() *InboundCallResponse {
	return call.response
}

func (call *InboundCall) doneReading() {}

// An InboundCallResponse is used to send the response back to the calling peer
type InboundCallResponse struct {
	reqResWriter

	cancel context.CancelFunc
	// calledAt is the time the inbound call was routed to the application.
	calledAt         time.Time
	applicationError bool
	headers          transportHeaders
	span             Span
	statsReporter    StatsReporter
	commonStatsTags  map[string]string
}

// SendSystemError returns a system error response to the peer.  The call is considered
// complete after this method is called, and no further data can be written.
func (response *InboundCallResponse) SendSystemError(err error) error {
	// Fail all future attempts to read fragments
	response.cancel()
	response.state = reqResWriterComplete

	return response.conn.SendSystemError(response.mex.msgID, CurrentSpan(response.mex.ctx), err)
}

// SetApplicationError marks the response as being an application error.  This method can
// only be called before any arguments have been sent to the calling peer.
func (response *InboundCallResponse) SetApplicationError() error {
	if response.state > reqResWriterPreArg2 {
		return response.failed(errReqResWriterStateMismatch{
			state:         response.state,
			expectedState: reqResWriterPreArg2,
		})
	}
	response.applicationError = true
	return nil
}

// Arg2Writer returns a WriteCloser that can be used to write the second argument.
// The returned writer must be closed once the write is complete.
func (response *InboundCallResponse) Arg2Writer() (ArgWriter, error) {
	if err := NewArgWriter(response.arg1Writer()).Write(nil); err != nil {
		return nil, err
	}
	return response.arg2Writer()
}

// Arg3Writer returns a WriteCloser that can be used to write the last argument.
// The returned writer must be closed once the write is complete.
func (response *InboundCallResponse) Arg3Writer() (ArgWriter, error) {
	return response.arg3Writer()
}

// doneSending shuts down the message exchange for this call.
// For incoming calls, the last message is sending the call response.
func (response *InboundCallResponse) doneSending() {
	if response.applicationError {
		response.statsReporter.IncCounter("inbound.calls.app-errors", response.commonStatsTags, 1)
	} else {
		response.statsReporter.IncCounter("inbound.calls.success", response.commonStatsTags, 1)
	}
	latency := timeNow().Sub(response.calledAt)
	response.statsReporter.RecordTimer("inbound.calls.latency", response.commonStatsTags, latency)

	// The message exchange is still open if there are no errors, call shutdown.
	if response.err == nil {
		response.mex.shutdown()
	}
}

// errorSending shuts down the message exhcnage for this call, and records counters.
func (response *InboundCallResponse) errorSending() {
	response.mex.shutdown()
}
<commit_msg>Close message exchange on system errors.<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tchannel

import (
	"errors"
	"fmt"
	"io"
	"time"

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

var errInboundRequestAlreadyActive = errors.New("inbound request is already active; possible duplicate client id")

// handleCallReq handles an incoming call request, registering a message
// exchange to receive further fragments for that call, and dispatching it in
// another goroutine
func (c *Connection) handleCallReq(frame *Frame) bool {
	switch state := c.readState(); state {
	case connectionActive:
		break
	case connectionStartClose, connectionInboundClosed, connectionClosed:
		c.SendSystemError(frame.Header.ID, nil, ErrChannelClosed)
		return true
	case connectionWaitingToRecvInitReq, connectionWaitingToSendInitReq, connectionWaitingToRecvInitRes:
		c.SendSystemError(frame.Header.ID, nil, NewSystemError(ErrCodeDeclined, "connection not ready"))
		return true
	default:
		panic(fmt.Errorf("unknown connection state for call req: %v", state))
	}

	callReq := new(callReq)
	initialFragment, err := parseInboundFragment(c.framePool, frame, callReq)
	if err != nil {
		// TODO(mmihic): Probably want to treat this as a protocol error
		c.log.Errorf("could not decode %s: %v", frame.Header, err)
		return true
	}

	c.log.Debugf("span=%s", callReq.Tracing)
	call := new(InboundCall)
	ctx, cancel := newIncomingContext(call, callReq.TimeToLive, &callReq.Tracing)

	mex, err := c.inbound.newExchange(ctx, c.framePool, callReq.messageType(), frame.Header.ID, 512)
	if err != nil {
		if err == errDuplicateMex {
			err = errInboundRequestAlreadyActive
		}
		c.log.Errorf("could not register exchange for %s", frame.Header)
		c.SendSystemError(frame.Header.ID, nil, err)
		return true
	}

	// Close may have been called between the time we checked the state and us creating the exchange.
	if c.readState() != connectionActive {
		mex.shutdown()
		return true
	}

	call.AddBinaryAnnotation(BinaryAnnotation{Key: "cn", Value: callReq.Headers[CallerName]})
	call.AddBinaryAnnotation(BinaryAnnotation{Key: "as", Value: callReq.Headers[ArgScheme]})
	call.AddAnnotation(AnnotationKeyServerReceive)
	response := new(InboundCallResponse)
	response.mex = mex
	response.conn = c
	response.contents = newFragmentingWriter(response, initialFragment.checksumType.New())
	response.cancel = cancel
	response.span = callReq.Tracing
	response.log = c.log.WithFields(LogField{"In-Response", callReq.ID()})
	response.headers = transportHeaders{}
	response.messageForFragment = func(initial bool) message {
		if initial {
			call.AddAnnotation(AnnotationKeyServerSend)
			targetEndpoint := TargetEndpoint{
				HostPort:    c.localPeerInfo.HostPort,
				ServiceName: call.serviceName,
				Operation:   string(call.Operation()),
			}
			call.Report(callReq.Tracing, targetEndpoint, c.traceReporter)

			callRes := new(callRes)
			callRes.Headers = response.headers
			callRes.ResponseCode = responseOK
			if response.applicationError {
				callRes.ResponseCode = responseApplicationError
			}
			return callRes
		}

		return new(callResContinue)
	}

	call.mex = mex
	call.initialFragment = initialFragment
	call.serviceName = string(callReq.Service)
	call.headers = callReq.Headers
	call.span = callReq.Tracing
	call.response = response
	call.log = c.log.WithFields(LogField{"In-Call", callReq.ID()})
	call.messageForFragment = func(initial bool) message { return new(callReqContinue) }
	call.contents = newFragmentingReader(call)
	call.statsReporter = c.statsReporter
	call.createStatsTags(c.commonStatsTags)

	response.statsReporter = c.statsReporter
	response.commonStatsTags = call.commonStatsTags

	setResponseHeaders(call.headers, response.headers)
	go c.dispatchInbound(c.connID, callReq.ID(), call)
	return false
}

// handleCallReqContinue handles the continuation of a call request, forwarding
// it to the request channel for that request, where it can be pulled during
// defragmentation
func (c *Connection) handleCallReqContinue(frame *Frame) bool {
	if err := c.inbound.forwardPeerFrame(frame); err != nil {
		c.inbound.removeExchange(frame.Header.ID)
		return true
	}
	return false
}

// createStatsTags creates the common stats tags, if they are not already created.
func (call *InboundCall) createStatsTags(connectionTags map[string]string) {
	call.commonStatsTags = map[string]string{
		"calling-service": call.CallerName(),
	}
	for k, v := range connectionTags {
		call.commonStatsTags[k] = v
	}
}

// dispatchInbound ispatches an inbound call to the appropriate handler
func (c *Connection) dispatchInbound(_ uint32, _ uint32, call *InboundCall) {
	c.log.Debugf("Received incoming call for %s from %s", call.ServiceName(), c.remotePeerInfo)

	if err := call.readOperation(); err != nil {
		c.log.Errorf("Could not read operation from %s: %v", c.remotePeerInfo, err)
		return
	}

	call.commonStatsTags["endpoint"] = string(call.operation)
	call.statsReporter.IncCounter("inbound.calls.recvd", call.commonStatsTags, 1)
	call.response.calledAt = timeNow()

	// NB(mmihic): Don't cast operation name to string here - this will
	// create a copy of the byte array, where as aliasing to string in the
	// map look up can be optimized by the compiler to avoid the copy.  See
	// https://github.com/golang/go/issues/3512
	h := c.handlers.find(call.ServiceName(), call.Operation())
	if h == nil {
		// CHeck the subchannel map to see if we find one there
		c.log.Debugf("Checking the subchannel's handlers for %s:%s", call.ServiceName(), call.Operation())
		h = c.subchannels.find(call.ServiceName(), call.Operation())
	}
	if h == nil {
		c.log.Errorf("Could not find handler for %s:%s", call.ServiceName(), call.Operation())
		call.mex.shutdown()
		call.Response().SendSystemError(
			NewSystemError(ErrCodeBadRequest, "no handler for service %q and operation %q", call.ServiceName(), call.Operation()))
		return
	}

	// TODO(prashant): This is an expensive way to check for cancellation. Use a heap for timeouts.
	go func() {
		if <-call.mex.ctx.Done(); call.mex.ctx.Err() == context.DeadlineExceeded {
			call.mex.inboundTimeout()
		}
	}()

	c.log.Debugf("Dispatching %s:%s from %s", call.ServiceName(), call.Operation(), c.remotePeerInfo)
	h.Handle(call.mex.ctx, call)
}

// An InboundCall is an incoming call from a peer
type InboundCall struct {
	Annotations
	reqResReader

	response        *InboundCallResponse
	serviceName     string
	operation       []byte
	headers         transportHeaders
	span            Span
	statsReporter   StatsReporter
	commonStatsTags map[string]string
}

// ServiceName returns the name of the service being called
func (call *InboundCall) ServiceName() string {
	return call.serviceName
}

// Operation returns the operation being called
func (call *InboundCall) Operation() []byte {
	return call.operation
}

// Format the format of the request from the ArgScheme transport header.
func (call *InboundCall) Format() Format {
	return Format(call.headers[ArgScheme])
}

// CallerName returns the caller name from the CallerName transport header.
func (call *InboundCall) CallerName() string {
	return call.headers[CallerName]
}

// ShardKey returns the shard key from the ShardKey transport header.
func (call *InboundCall) ShardKey() string {
	return call.headers[ShardKey]
}

// Reads the entire operation name (arg1) from the request stream.
func (call *InboundCall) readOperation() error {
	var arg1 []byte
	if err := NewArgReader(call.arg1Reader()).Read(&arg1); err != nil {
		return call.failed(err)
	}

	call.operation = arg1
	return nil
}

// Arg2Reader returns an io.ReadCloser to read the second argument.
// The ReadCloser must be closed once the argument has been read.
func (call *InboundCall) Arg2Reader() (io.ReadCloser, error) {
	return call.arg2Reader()
}

// Arg3Reader returns an io.ReadCloser to read the last argument.
// The ReadCloser must be closed once the argument has been read.
func (call *InboundCall) Arg3Reader() (io.ReadCloser, error) {
	return call.arg3Reader()
}

// Response provides access to the InboundCallResponse object which can be used
// to write back to the calling peer
func (call *InboundCall) Response() *InboundCallResponse {
	return call.response
}

func (call *InboundCall) doneReading() {}

// An InboundCallResponse is used to send the response back to the calling peer
type InboundCallResponse struct {
	reqResWriter

	cancel context.CancelFunc
	// calledAt is the time the inbound call was routed to the application.
	calledAt         time.Time
	applicationError bool
	systemError      bool
	headers          transportHeaders
	span             Span
	statsReporter    StatsReporter
	commonStatsTags  map[string]string
}

// SendSystemError returns a system error response to the peer.  The call is considered
// complete after this method is called, and no further data can be written.
func (response *InboundCallResponse) SendSystemError(err error) error {
	// Fail all future attempts to read fragments
	response.cancel()
	response.state = reqResWriterComplete
	response.systemError = true
	response.doneSending()
	return response.conn.SendSystemError(response.mex.msgID, CurrentSpan(response.mex.ctx), err)
}

// SetApplicationError marks the response as being an application error.  This method can
// only be called before any arguments have been sent to the calling peer.
func (response *InboundCallResponse) SetApplicationError() error {
	if response.state > reqResWriterPreArg2 {
		return response.failed(errReqResWriterStateMismatch{
			state:         response.state,
			expectedState: reqResWriterPreArg2,
		})
	}
	response.applicationError = true
	return nil
}

// Arg2Writer returns a WriteCloser that can be used to write the second argument.
// The returned writer must be closed once the write is complete.
func (response *InboundCallResponse) Arg2Writer() (ArgWriter, error) {
	if err := NewArgWriter(response.arg1Writer()).Write(nil); err != nil {
		return nil, err
	}
	return response.arg2Writer()
}

// Arg3Writer returns a WriteCloser that can be used to write the last argument.
// The returned writer must be closed once the write is complete.
func (response *InboundCallResponse) Arg3Writer() (ArgWriter, error) {
	return response.arg3Writer()
}

// doneSending shuts down the message exchange for this call.
// For incoming calls, the last message is sending the call response.
func (response *InboundCallResponse) doneSending() {
	latency := timeNow().Sub(response.calledAt)
	response.statsReporter.RecordTimer("inbound.calls.latency", response.commonStatsTags, latency)

	if response.systemError {
		// TODO(prashant): Report the error code type as per metrics doc and enable.
		// response.statsReporter.IncCounter("inbound.calls.system-errors", response.commonStatsTags, 1)
	} else if response.applicationError {
		response.statsReporter.IncCounter("inbound.calls.app-errors", response.commonStatsTags, 1)
	} else {
		response.statsReporter.IncCounter("inbound.calls.success", response.commonStatsTags, 1)
	}

	// The message exchange is still open if there are no errors, call shutdown.
	if response.err == nil {
		response.mex.shutdown()
	}
}
<|endoftext|>