123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- /*
- interactive terminal interface for local clients
- */
- package ui
- import (
- "fmt"
- termbox "github.com/nsf/termbox-go"
- "time"
- )
- const (
- fgColor = termbox.ColorWhite
- bgColor = termbox.ColorDefault
- )
- func clear() {
- w, h := termbox.Size()
- for i := 0; i < w; i++ {
- for j := 0; j < h; j++ {
- termbox.SetCell(i, j, ' ', fgColor, bgColor)
- }
- }
- }
- func printfAttr(x, y int, fg termbox.Attribute, arg0 string, args ...interface{}) {
- s := fmt.Sprintf(arg0, args...)
- for i, ch := range s {
- termbox.SetCell(x+i, y, ch, fg, bgColor)
- }
- }
- func printf(x, y int, arg0 string, args ...interface{}) {
- printfAttr(x, y, fgColor, arg0, args...)
- }
- type Term struct {
- ui *Ui
- statusColorMap map[string]termbox.Attribute
- updates chan (State)
- }
- func NewTerm() *Term {
- return &Term{
- statusColorMap: map[string]termbox.Attribute{
- "connecting": termbox.ColorCyan,
- "reconnecting": termbox.ColorRed,
- "online": termbox.ColorGreen,
- },
- updates: make(chan State),
- }
- }
- func (t *Term) SetUi(ui *Ui) {
- t.ui = ui
- go t.run()
- }
- func (t *Term) run() {
- // make sure we shut down cleanly
- t.ui.Wait.Add(1)
- defer t.ui.Wait.Done()
- // open channels for incoming application state changes
- // and broadbasts
- t.updates = t.ui.Updates.Reg()
- // init/close termbox library
- termbox.Init()
- defer termbox.Close()
- go t.input()
- t.draw()
- }
- func (t *Term) draw() {
- var state State
- for {
- select {
- case newState := <-t.updates:
- if newState != nil {
- state = newState
- }
- if state == nil {
- // log.Info("Got update to draw, but no state to draw with")
- continue
- }
- // program is shutting down
- if state.IsStopping() {
- return
- }
- clear()
- x, _ := termbox.Size()
- quitMsg := "(Ctrl+C to quit)"
- printf(x-len(quitMsg), 0, quitMsg)
- printfAttr(0, 0, termbox.ColorBlue|termbox.AttrBold, "ngrok")
- msec := float64(time.Millisecond)
- printfAttr(0, 2, t.statusColorMap[state.GetStatus()], "%-30s%s", "Tunnel Status", state.GetStatus())
- printf(0, 3, "%-30s%s", "Version", state.GetVersion())
- printf(0, 4, "%-30s%s", "Protocol", state.GetProtocol())
- printf(0, 5, "%-30s%s -> %s", "Forwarding", state.GetPublicUrl(), state.GetLocalAddr())
- printf(0, 6, "%-30s%s", "HTTP Dashboard", "http://127.0.0.1:9999")
- connMeter, connTimer := state.GetConnectionMetrics()
- printf(0, 7, "%-30s%d", "# Conn", connMeter.Count())
- printf(0, 8, "%-30s%.2fms", "Avg Conn Time", connTimer.Mean()/msec)
- if state.GetProtocol() == "http" {
- printf(0, 10, "HTTP Requests")
- printf(0, 11, "-------------")
- for i, http := range state.GetHistory() {
- req := http.GetRequest()
- resp := http.GetResponse()
- printf(0, 12+i, "%s %v", req.Method, req.URL)
- if resp != nil {
- printf(30, 12+i, "%s", resp.Status)
- }
- }
- }
- termbox.Flush()
- }
- }
- }
- func (t *Term) input() {
- for {
- ev := termbox.PollEvent()
- switch ev.Type {
- case termbox.EventKey:
- switch ev.Key {
- case termbox.KeyCtrlC:
- t.ui.Cmds <- Command{QUIT, ""}
- return
- }
- case termbox.EventResize:
- t.updates <- nil
- case termbox.EventError:
- panic(ev.Err)
- }
- }
- }
|