You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

524 lines
13 KiB

  1. // Copyright 2012 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build !appengine
  5. // Package socket implements an WebSocket-based playground backend.
  6. // Clients connect to a websocket handler and send run/kill commands, and
  7. // the server sends the output and exit status of the running processes.
  8. // Multiple clients running multiple processes may be served concurrently.
  9. // The wire format is JSON and is described by the Message type.
  10. //
  11. // This will not run on App Engine as WebSockets are not supported there.
  12. package socket // import "golang.org/x/tools/playground/socket"
  13. import (
  14. "bytes"
  15. "encoding/json"
  16. "errors"
  17. "go/parser"
  18. "go/token"
  19. "io"
  20. "io/ioutil"
  21. "log"
  22. "net"
  23. "net/http"
  24. "net/url"
  25. "os"
  26. "os/exec"
  27. "path/filepath"
  28. "runtime"
  29. "strconv"
  30. "strings"
  31. "time"
  32. "unicode/utf8"
  33. "golang.org/x/net/websocket"
  34. )
  35. // RunScripts specifies whether the socket handler should execute shell scripts
  36. // (snippets that start with a shebang).
  37. var RunScripts = true
  38. // Environ provides an environment when a binary, such as the go tool, is
  39. // invoked.
  40. var Environ func() []string = os.Environ
  41. const (
  42. // The maximum number of messages to send per session (avoid flooding).
  43. msgLimit = 1000
  44. // Batch messages sent in this interval and send as a single message.
  45. msgDelay = 10 * time.Millisecond
  46. )
  47. // Message is the wire format for the websocket connection to the browser.
  48. // It is used for both sending output messages and receiving commands, as
  49. // distinguished by the Kind field.
  50. type Message struct {
  51. Id string // client-provided unique id for the process
  52. Kind string // in: "run", "kill" out: "stdout", "stderr", "end"
  53. Body string
  54. Options *Options `json:",omitempty"`
  55. }
  56. // Options specify additional message options.
  57. type Options struct {
  58. Race bool // use -race flag when building code (for "run" only)
  59. }
  60. // NewHandler returns a websocket server which checks the origin of requests.
  61. func NewHandler(origin *url.URL) websocket.Server {
  62. return websocket.Server{
  63. Config: websocket.Config{Origin: origin},
  64. Handshake: handshake,
  65. Handler: websocket.Handler(socketHandler),
  66. }
  67. }
  68. // handshake checks the origin of a request during the websocket handshake.
  69. func handshake(c *websocket.Config, req *http.Request) error {
  70. o, err := websocket.Origin(c, req)
  71. if err != nil {
  72. log.Println("bad websocket origin:", err)
  73. return websocket.ErrBadWebSocketOrigin
  74. }
  75. _, port, err := net.SplitHostPort(c.Origin.Host)
  76. if err != nil {
  77. log.Println("bad websocket origin:", err)
  78. return websocket.ErrBadWebSocketOrigin
  79. }
  80. ok := c.Origin.Scheme == o.Scheme && (c.Origin.Host == o.Host || c.Origin.Host == net.JoinHostPort(o.Host, port))
  81. if !ok {
  82. log.Println("bad websocket origin:", o)
  83. return websocket.ErrBadWebSocketOrigin
  84. }
  85. log.Println("accepting connection from:", req.RemoteAddr)
  86. return nil
  87. }
  88. // socketHandler handles the websocket connection for a given present session.
  89. // It handles transcoding Messages to and from JSON format, and starting
  90. // and killing processes.
  91. func socketHandler(c *websocket.Conn) {
  92. in, out := make(chan *Message), make(chan *Message)
  93. errc := make(chan error, 1)
  94. // Decode messages from client and send to the in channel.
  95. go func() {
  96. dec := json.NewDecoder(c)
  97. for {
  98. var m Message
  99. if err := dec.Decode(&m); err != nil {
  100. errc <- err
  101. return
  102. }
  103. in <- &m
  104. }
  105. }()
  106. // Receive messages from the out channel and encode to the client.
  107. go func() {
  108. enc := json.NewEncoder(c)
  109. for m := range out {
  110. if err := enc.Encode(m); err != nil {
  111. errc <- err
  112. return
  113. }
  114. }
  115. }()
  116. defer close(out)
  117. // Start and kill processes and handle errors.
  118. proc := make(map[string]*process)
  119. for {
  120. select {
  121. case m := <-in:
  122. switch m.Kind {
  123. case "run":
  124. log.Println("running snippet from:", c.Request().RemoteAddr)
  125. proc[m.Id].Kill()
  126. proc[m.Id] = startProcess(m.Id, m.Body, out, m.Options)
  127. case "kill":
  128. proc[m.Id].Kill()
  129. }
  130. case err := <-errc:
  131. if err != io.EOF {
  132. // A encode or decode has failed; bail.
  133. log.Println(err)
  134. }
  135. // Shut down any running processes.
  136. for _, p := range proc {
  137. p.Kill()
  138. }
  139. return
  140. }
  141. }
  142. }
  143. // process represents a running process.
  144. type process struct {
  145. out chan<- *Message
  146. done chan struct{} // closed when wait completes
  147. run *exec.Cmd
  148. bin string
  149. }
  150. // startProcess builds and runs the given program, sending its output
  151. // and end event as Messages on the provided channel.
  152. func startProcess(id, body string, dest chan<- *Message, opt *Options) *process {
  153. var (
  154. done = make(chan struct{})
  155. out = make(chan *Message)
  156. p = &process{out: out, done: done}
  157. )
  158. go func() {
  159. defer close(done)
  160. for m := range buffer(limiter(out, p), time.After) {
  161. m.Id = id
  162. dest <- m
  163. }
  164. }()
  165. var err error
  166. if path, args := shebang(body); path != "" {
  167. if RunScripts {
  168. err = p.startProcess(path, args, body)
  169. } else {
  170. err = errors.New("script execution is not allowed")
  171. }
  172. } else {
  173. err = p.start(body, opt)
  174. }
  175. if err != nil {
  176. p.end(err)
  177. return nil
  178. }
  179. go func() {
  180. p.end(p.run.Wait())
  181. }()
  182. return p
  183. }
  184. // end sends an "end" message to the client, containing the process id and the
  185. // given error value. It also removes the binary, if present.
  186. func (p *process) end(err error) {
  187. if p.bin != "" {
  188. defer os.Remove(p.bin)
  189. }
  190. m := &Message{Kind: "end"}
  191. if err != nil {
  192. m.Body = err.Error()
  193. }
  194. p.out <- m
  195. close(p.out)
  196. }
  197. // A killer provides a mechanism to terminate a process.
  198. // The Kill method returns only once the process has exited.
  199. type killer interface {
  200. Kill()
  201. }
  202. // limiter returns a channel that wraps the given channel.
  203. // It receives Messages from the given channel and sends them to the returned
  204. // channel until it passes msgLimit messages, at which point it will kill the
  205. // process and pass only the "end" message.
  206. // When the given channel is closed, or when the "end" message is received,
  207. // it closes the returned channel.
  208. func limiter(in <-chan *Message, p killer) <-chan *Message {
  209. out := make(chan *Message)
  210. go func() {
  211. defer close(out)
  212. n := 0
  213. for m := range in {
  214. switch {
  215. case n < msgLimit || m.Kind == "end":
  216. out <- m
  217. if m.Kind == "end" {
  218. return
  219. }
  220. case n == msgLimit:
  221. // Kill in a goroutine as Kill will not return
  222. // until the process' output has been
  223. // processed, and we're doing that in this loop.
  224. go p.Kill()
  225. default:
  226. continue // don't increment
  227. }
  228. n++
  229. }
  230. }()
  231. return out
  232. }
  233. // buffer returns a channel that wraps the given channel. It receives messages
  234. // from the given channel and sends them to the returned channel.
  235. // Message bodies are gathered over the period msgDelay and coalesced into a
  236. // single Message before they are passed on. Messages of the same kind are
  237. // coalesced; when a message of a different kind is received, any buffered
  238. // messages are flushed. When the given channel is closed, buffer flushes the
  239. // remaining buffered messages and closes the returned channel.
  240. // The timeAfter func should be time.After. It exists for testing.
  241. func buffer(in <-chan *Message, timeAfter func(time.Duration) <-chan time.Time) <-chan *Message {
  242. out := make(chan *Message)
  243. go func() {
  244. defer close(out)
  245. var (
  246. tc <-chan time.Time
  247. buf []byte
  248. kind string
  249. flush = func() {
  250. if len(buf) == 0 {
  251. return
  252. }
  253. out <- &Message{Kind: kind, Body: safeString(buf)}
  254. buf = buf[:0] // recycle buffer
  255. kind = ""
  256. }
  257. )
  258. for {
  259. select {
  260. case m, ok := <-in:
  261. if !ok {
  262. flush()
  263. return
  264. }
  265. if m.Kind == "end" {
  266. flush()
  267. out <- m
  268. return
  269. }
  270. if kind != m.Kind {
  271. flush()
  272. kind = m.Kind
  273. if tc == nil {
  274. tc = timeAfter(msgDelay)
  275. }
  276. }
  277. buf = append(buf, m.Body...)
  278. case <-tc:
  279. flush()
  280. tc = nil
  281. }
  282. }
  283. }()
  284. return out
  285. }
  286. // Kill stops the process if it is running and waits for it to exit.
  287. func (p *process) Kill() {
  288. if p == nil || p.run == nil {
  289. return
  290. }
  291. p.run.Process.Kill()
  292. <-p.done // block until process exits
  293. }
  294. // shebang looks for a shebang ('#!') at the beginning of the passed string.
  295. // If found, it returns the path and args after the shebang.
  296. // args includes the command as args[0].
  297. func shebang(body string) (path string, args []string) {
  298. body = strings.TrimSpace(body)
  299. if !strings.HasPrefix(body, "#!") {
  300. return "", nil
  301. }
  302. if i := strings.Index(body, "\n"); i >= 0 {
  303. body = body[:i]
  304. }
  305. fs := strings.Fields(body[2:])
  306. return fs[0], fs
  307. }
  308. // startProcess starts a given program given its path and passing the given body
  309. // to the command standard input.
  310. func (p *process) startProcess(path string, args []string, body string) error {
  311. cmd := &exec.Cmd{
  312. Path: path,
  313. Args: args,
  314. Stdin: strings.NewReader(body),
  315. Stdout: &messageWriter{kind: "stdout", out: p.out},
  316. Stderr: &messageWriter{kind: "stderr", out: p.out},
  317. }
  318. if err := cmd.Start(); err != nil {
  319. return err
  320. }
  321. p.run = cmd
  322. return nil
  323. }
  324. // start builds and starts the given program, sending its output to p.out,
  325. // and stores the running *exec.Cmd in the run field.
  326. func (p *process) start(body string, opt *Options) error {
  327. // We "go build" and then exec the binary so that the
  328. // resultant *exec.Cmd is a handle to the user's program
  329. // (rather than the go tool process).
  330. // This makes Kill work.
  331. bin := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
  332. src := bin + ".go"
  333. if runtime.GOOS == "windows" {
  334. bin += ".exe"
  335. }
  336. // write body to x.go
  337. defer os.Remove(src)
  338. err := ioutil.WriteFile(src, []byte(body), 0666)
  339. if err != nil {
  340. return err
  341. }
  342. // build x.go, creating x
  343. p.bin = bin // to be removed by p.end
  344. dir, file := filepath.Split(src)
  345. args := []string{"go", "build", "-tags", "OMIT"}
  346. if opt != nil && opt.Race {
  347. p.out <- &Message{
  348. Kind: "stderr",
  349. Body: "Running with race detector.\n",
  350. }
  351. args = append(args, "-race")
  352. }
  353. args = append(args, "-o", bin, file)
  354. cmd := p.cmd(dir, args...)
  355. cmd.Stdout = cmd.Stderr // send compiler output to stderr
  356. if err := cmd.Run(); err != nil {
  357. return err
  358. }
  359. // run x
  360. if isNacl() {
  361. cmd, err = p.naclCmd(bin)
  362. if err != nil {
  363. return err
  364. }
  365. } else {
  366. cmd = p.cmd("", bin)
  367. }
  368. if opt != nil && opt.Race {
  369. cmd.Env = append(cmd.Env, "GOMAXPROCS=2")
  370. }
  371. if err := cmd.Start(); err != nil {
  372. // If we failed to exec, that might be because they built
  373. // a non-main package instead of an executable.
  374. // Check and report that.
  375. if name, err := packageName(body); err == nil && name != "main" {
  376. return errors.New(`executable programs must use "package main"`)
  377. }
  378. return err
  379. }
  380. p.run = cmd
  381. return nil
  382. }
  383. // cmd builds an *exec.Cmd that writes its standard output and error to the
  384. // process' output channel.
  385. func (p *process) cmd(dir string, args ...string) *exec.Cmd {
  386. cmd := exec.Command(args[0], args[1:]...)
  387. cmd.Dir = dir
  388. cmd.Env = Environ()
  389. cmd.Stdout = &messageWriter{kind: "stdout", out: p.out}
  390. cmd.Stderr = &messageWriter{kind: "stderr", out: p.out}
  391. return cmd
  392. }
  393. func isNacl() bool {
  394. for _, v := range append(Environ(), os.Environ()...) {
  395. if v == "GOOS=nacl" {
  396. return true
  397. }
  398. }
  399. return false
  400. }
  401. // naclCmd returns an *exec.Cmd that executes bin under native client.
  402. func (p *process) naclCmd(bin string) (*exec.Cmd, error) {
  403. pwd, err := os.Getwd()
  404. if err != nil {
  405. return nil, err
  406. }
  407. var args []string
  408. env := []string{
  409. "NACLENV_GOOS=" + runtime.GOOS,
  410. "NACLENV_GOROOT=/go",
  411. "NACLENV_NACLPWD=" + strings.Replace(pwd, runtime.GOROOT(), "/go", 1),
  412. }
  413. switch runtime.GOARCH {
  414. case "amd64":
  415. env = append(env, "NACLENV_GOARCH=amd64p32")
  416. args = []string{"sel_ldr_x86_64"}
  417. case "386":
  418. env = append(env, "NACLENV_GOARCH=386")
  419. args = []string{"sel_ldr_x86_32"}
  420. case "arm":
  421. env = append(env, "NACLENV_GOARCH=arm")
  422. selLdr, err := exec.LookPath("sel_ldr_arm")
  423. if err != nil {
  424. return nil, err
  425. }
  426. args = []string{"nacl_helper_bootstrap_arm", selLdr, "--reserved_at_zero=0xXXXXXXXXXXXXXXXX"}
  427. default:
  428. return nil, errors.New("native client does not support GOARCH=" + runtime.GOARCH)
  429. }
  430. cmd := p.cmd("", append(args, "-l", "/dev/null", "-S", "-e", bin)...)
  431. cmd.Env = append(cmd.Env, env...)
  432. return cmd, nil
  433. }
  434. func packageName(body string) (string, error) {
  435. f, err := parser.ParseFile(token.NewFileSet(), "prog.go",
  436. strings.NewReader(body), parser.PackageClauseOnly)
  437. if err != nil {
  438. return "", err
  439. }
  440. return f.Name.String(), nil
  441. }
  442. // messageWriter is an io.Writer that converts all writes to Message sends on
  443. // the out channel with the specified id and kind.
  444. type messageWriter struct {
  445. kind string
  446. out chan<- *Message
  447. }
  448. func (w *messageWriter) Write(b []byte) (n int, err error) {
  449. w.out <- &Message{Kind: w.kind, Body: safeString(b)}
  450. return len(b), nil
  451. }
  452. // safeString returns b as a valid UTF-8 string.
  453. func safeString(b []byte) string {
  454. if utf8.Valid(b) {
  455. return string(b)
  456. }
  457. var buf bytes.Buffer
  458. for len(b) > 0 {
  459. r, size := utf8.DecodeRune(b)
  460. b = b[size:]
  461. buf.WriteRune(r)
  462. }
  463. return buf.String()
  464. }
  465. var tmpdir string
  466. func init() {
  467. // find real path to temporary directory
  468. var err error
  469. tmpdir, err = filepath.EvalSymlinks(os.TempDir())
  470. if err != nil {
  471. log.Fatal(err)
  472. }
  473. }
  474. var uniq = make(chan int) // a source of numbers for naming temporary files
  475. func init() {
  476. go func() {
  477. for i := 0; ; i++ {
  478. uniq <- i
  479. }
  480. }()
  481. }