Add generated file
This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
524
vendor/golang.org/x/tools/playground/socket/socket.go
generated
vendored
Normal file
524
vendor/golang.org/x/tools/playground/socket/socket.go
generated
vendored
Normal file
@@ -0,0 +1,524 @@
|
||||
// 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 "golang.org/x/tools/playground/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"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/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
|
||||
}
|
||||
log.Println("accepting connection from:", req.RemoteAddr)
|
||||
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
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer close(out)
|
||||
|
||||
// Start and kill processes and handle errors.
|
||||
proc := make(map[string]*process)
|
||||
for {
|
||||
select {
|
||||
case m := <-in:
|
||||
switch m.Kind {
|
||||
case "run":
|
||||
log.Println("running snippet from:", c.Request().RemoteAddr)
|
||||
proc[m.Id].Kill()
|
||||
proc[m.Id] = startProcess(m.Id, m.Body, out, 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 {
|
||||
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, dest chan<- *Message, opt *Options) *process {
|
||||
var (
|
||||
done = make(chan struct{})
|
||||
out = make(chan *Message)
|
||||
p = &process{out: out, done: done}
|
||||
)
|
||||
go func() {
|
||||
defer close(done)
|
||||
for m := range buffer(limiter(out, p), time.After) {
|
||||
m.Id = id
|
||||
dest <- m
|
||||
}
|
||||
}()
|
||||
var err error
|
||||
if path, args := shebang(body); path != "" {
|
||||
if RunScripts {
|
||||
err = p.startProcess(path, args, body)
|
||||
} else {
|
||||
err = errors.New("script execution is not allowed")
|
||||
}
|
||||
} else {
|
||||
err = p.start(body, opt)
|
||||
}
|
||||
if err != nil {
|
||||
p.end(err)
|
||||
return nil
|
||||
}
|
||||
go func() {
|
||||
p.end(p.run.Wait())
|
||||
}()
|
||||
return p
|
||||
}
|
||||
|
||||
// end sends an "end" message to the client, containing the process id and the
|
||||
// given error value. It also removes the binary, if present.
|
||||
func (p *process) end(err error) {
|
||||
if p.bin != "" {
|
||||
defer os.Remove(p.bin)
|
||||
}
|
||||
m := &Message{Kind: "end"}
|
||||
if err != nil {
|
||||
m.Body = err.Error()
|
||||
}
|
||||
p.out <- m
|
||||
close(p.out)
|
||||
}
|
||||
|
||||
// A killer provides a mechanism to terminate a process.
|
||||
// The Kill method returns only once the process has exited.
|
||||
type killer interface {
|
||||
Kill()
|
||||
}
|
||||
|
||||
// limiter returns a channel that wraps the given channel.
|
||||
// It receives Messages from the given channel and sends them to the returned
|
||||
// channel until it passes msgLimit messages, at which point it will kill the
|
||||
// process and pass only the "end" message.
|
||||
// When the given channel is closed, or when the "end" message is received,
|
||||
// it closes the returned channel.
|
||||
func limiter(in <-chan *Message, p killer) <-chan *Message {
|
||||
out := make(chan *Message)
|
||||
go func() {
|
||||
defer close(out)
|
||||
n := 0
|
||||
for m := range in {
|
||||
switch {
|
||||
case n < msgLimit || m.Kind == "end":
|
||||
out <- m
|
||||
if m.Kind == "end" {
|
||||
return
|
||||
}
|
||||
case n == msgLimit:
|
||||
// Kill in a goroutine as Kill will not return
|
||||
// until the process' output has been
|
||||
// processed, and we're doing that in this loop.
|
||||
go p.Kill()
|
||||
default:
|
||||
continue // don't increment
|
||||
}
|
||||
n++
|
||||
}
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
// buffer returns a channel that wraps the given channel. It receives messages
|
||||
// from the given channel and sends them to the returned channel.
|
||||
// Message bodies are gathered over the period msgDelay and coalesced into a
|
||||
// single Message before they are passed on. Messages of the same kind are
|
||||
// coalesced; when a message of a different kind is received, any buffered
|
||||
// messages are flushed. When the given channel is closed, buffer flushes the
|
||||
// remaining buffered messages and closes the returned channel.
|
||||
// The timeAfter func should be time.After. It exists for testing.
|
||||
func buffer(in <-chan *Message, timeAfter func(time.Duration) <-chan time.Time) <-chan *Message {
|
||||
out := make(chan *Message)
|
||||
go func() {
|
||||
defer close(out)
|
||||
var (
|
||||
tc <-chan time.Time
|
||||
buf []byte
|
||||
kind string
|
||||
flush = func() {
|
||||
if len(buf) == 0 {
|
||||
return
|
||||
}
|
||||
out <- &Message{Kind: kind, Body: safeString(buf)}
|
||||
buf = buf[:0] // recycle buffer
|
||||
kind = ""
|
||||
}
|
||||
)
|
||||
for {
|
||||
select {
|
||||
case m, ok := <-in:
|
||||
if !ok {
|
||||
flush()
|
||||
return
|
||||
}
|
||||
if m.Kind == "end" {
|
||||
flush()
|
||||
out <- m
|
||||
return
|
||||
}
|
||||
if kind != m.Kind {
|
||||
flush()
|
||||
kind = m.Kind
|
||||
if tc == nil {
|
||||
tc = timeAfter(msgDelay)
|
||||
}
|
||||
}
|
||||
buf = append(buf, m.Body...)
|
||||
case <-tc:
|
||||
flush()
|
||||
tc = nil
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
// Kill stops the process if it is running and waits for it to exit.
|
||||
func (p *process) Kill() {
|
||||
if p == nil || p.run == 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{kind: "stdout", out: p.out},
|
||||
Stderr: &messageWriter{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{
|
||||
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
|
||||
}
|
||||
|
||||
// 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{kind: "stdout", out: p.out}
|
||||
cmd.Stderr = &messageWriter{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 args []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=amd64p32")
|
||||
args = []string{"sel_ldr_x86_64"}
|
||||
case "386":
|
||||
env = append(env, "NACLENV_GOARCH=386")
|
||||
args = []string{"sel_ldr_x86_32"}
|
||||
case "arm":
|
||||
env = append(env, "NACLENV_GOARCH=arm")
|
||||
selLdr, err := exec.LookPath("sel_ldr_arm")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args = []string{"nacl_helper_bootstrap_arm", selLdr, "--reserved_at_zero=0xXXXXXXXXXXXXXXXX"}
|
||||
default:
|
||||
return nil, errors.New("native client does not support GOARCH=" + runtime.GOARCH)
|
||||
}
|
||||
|
||||
cmd := p.cmd("", append(args, "-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 {
|
||||
kind string
|
||||
out chan<- *Message
|
||||
}
|
||||
|
||||
func (w *messageWriter) Write(b []byte) (n int, err error) {
|
||||
w.out <- &Message{Kind: w.kind, Body: safeString(b)}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}()
|
||||
}
|
Reference in New Issue
Block a user