terminal.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. /*
  2. interactive terminal interface for local clients
  3. */
  4. package ui
  5. import (
  6. "fmt"
  7. termbox "github.com/nsf/termbox-go"
  8. "time"
  9. )
  10. const (
  11. fgColor = termbox.ColorWhite
  12. bgColor = termbox.ColorDefault
  13. )
  14. func clear() {
  15. w, h := termbox.Size()
  16. for i := 0; i < w; i++ {
  17. for j := 0; j < h; j++ {
  18. termbox.SetCell(i, j, ' ', fgColor, bgColor)
  19. }
  20. }
  21. }
  22. func printfAttr(x, y int, fg termbox.Attribute, arg0 string, args ...interface{}) {
  23. s := fmt.Sprintf(arg0, args...)
  24. for i, ch := range s {
  25. termbox.SetCell(x+i, y, ch, fg, bgColor)
  26. }
  27. }
  28. func printf(x, y int, arg0 string, args ...interface{}) {
  29. printfAttr(x, y, fgColor, arg0, args...)
  30. }
  31. type Term struct {
  32. ui *Ui
  33. statusColorMap map[string]termbox.Attribute
  34. }
  35. func NewTerm() *Term {
  36. return &Term{
  37. statusColorMap: map[string]termbox.Attribute{
  38. "connecting": termbox.ColorCyan,
  39. "reconnecting": termbox.ColorRed,
  40. "online": termbox.ColorGreen,
  41. },
  42. }
  43. }
  44. func (t *Term) SetUi(ui *Ui) {
  45. t.ui = ui
  46. go t.run()
  47. }
  48. func (t *Term) run() {
  49. // make sure we shut down cleanly
  50. t.ui.Wait.Add(1)
  51. defer t.ui.Wait.Done()
  52. // open channels for incoming application state changes
  53. // and broadbasts
  54. updates := t.ui.Updates.Reg()
  55. // init/close termbox library
  56. termbox.Init()
  57. defer termbox.Close()
  58. go t.input()
  59. t.draw(updates)
  60. }
  61. func (t *Term) draw(updates chan State) {
  62. for {
  63. select {
  64. case state := <-updates:
  65. // program is shutting down
  66. if state.IsStopping() {
  67. return
  68. }
  69. clear()
  70. printfAttr(0, 0, termbox.ColorBlue|termbox.AttrBold, "ngrok")
  71. msec := float64(time.Millisecond)
  72. printf(0, 2, "%-30s%s", "Version", state.GetVersion())
  73. printf(0, 3, "%-30s%s", "Public URL", state.GetPublicUrl())
  74. printf(0, 4, "%-30s%s", "Local Address", state.GetLocalAddr())
  75. printfAttr(0, 5, t.statusColorMap[state.GetStatus()], "%-30s%s", "Tunnel Status", state.GetStatus())
  76. connMeter, connTimer := state.GetConnectionMetrics()
  77. printf(0, 6, "%-30s%d", "# Conn", connMeter.Count())
  78. printf(0, 7, "%-30s%.2fms", "Mean Conn Time", connTimer.Mean()/msec)
  79. printf(0, 8, "%-30s%.2fms", "Conn Time 95th PCTL", connTimer.Percentile(0.95)/msec)
  80. bytesInCount, bytesIn := state.GetBytesInMetrics()
  81. printf(0, 9, "%-30s%d", "Bytes In", bytesInCount.Count())
  82. printf(0, 10, "%-30s%.2f", "Avg Bytes/req", bytesIn.Mean())
  83. bytesOutCount, bytesOut := state.GetBytesOutMetrics()
  84. printf(0, 11, "%-30s%d", "Bytes Out", bytesOutCount.Count())
  85. printf(0, 12, "%-30s%.2f", "Bytes Out/req", bytesOut.Mean())
  86. printf(0, 14, "Last HTTP Requests")
  87. for i, http := range state.GetHistory() {
  88. req := http.GetRequest()
  89. resp := http.GetResponse()
  90. printf(0, 15+i, "%s %v", req.Method, req.URL)
  91. if resp != nil {
  92. printf(30, 15+i, "%s", resp.Status)
  93. }
  94. }
  95. termbox.Flush()
  96. }
  97. }
  98. }
  99. func (t *Term) input() {
  100. for {
  101. ev := termbox.PollEvent()
  102. switch ev.Type {
  103. case termbox.EventKey:
  104. switch ev.Key {
  105. case termbox.KeyCtrlC:
  106. t.ui.Cmds <- Command{QUIT, ""}
  107. return
  108. }
  109. }
  110. }
  111. }