Browse Source

split id generation function into a separate fn that can be called without risk of panic. fix race condition calling termbox.Flush in term/view

Alan Shreve 12 years ago
parent
commit
7562049e40

+ 1 - 1
src/ngrok/client/main.go

@@ -253,7 +253,7 @@ func Main() {
 		status: "connecting",
 
 		// unique client id
-		id: util.RandId(),
+		id: util.RandIdOrPanic(8),
 
 		// command-line options
 		opts: opts,

+ 4 - 2
src/ngrok/client/views/term/http.go

@@ -15,6 +15,7 @@ type HttpView struct {
 	httpProto    *proto.Http
 	HttpRequests *util.Ring
 	shutdown     chan int
+	flush        chan int
 	*area
 	log.Logger
 }
@@ -32,12 +33,13 @@ func colorFor(status string) termbox.Attribute {
 	return termbox.ColorWhite
 }
 
-func NewHttp(proto *proto.Http, shutdown chan int, x, y int) *HttpView {
+func NewHttp(proto *proto.Http, flush, shutdown chan int, x, y int) *HttpView {
 	v := &HttpView{
 		httpProto:    proto,
 		HttpRequests: util.NewRing(size),
 		area:         NewArea(x, y, 70, size+5),
 		shutdown:     shutdown,
+		flush:        flush,
 		Logger:       log.NewPrefixLogger(),
 	}
 	v.AddLogPrefix("view")
@@ -76,5 +78,5 @@ func (v *HttpView) Render() {
 			v.APrintf(colorFor(txn.Resp.Status), 30, 3+i, "%s", txn.Resp.Status)
 		}
 	}
-	termbox.Flush()
+	v.flush <- 1
 }

+ 6 - 1
src/ngrok/client/views/term/view.go

@@ -15,6 +15,7 @@ import (
 type TermView struct {
 	ctl      *ui.Controller
 	updates  chan interface{}
+	flush    chan int
 	subviews []ui.View
 	state    ui.State
 	log.Logger
@@ -33,6 +34,7 @@ func New(ctl *ui.Controller, state ui.State) *TermView {
 	v := &TermView{
 		ctl:      ctl,
 		updates:  ctl.Updates.Reg(),
+		flush:    make(chan int),
 		subviews: make([]ui.View, 0),
 		state:    state,
 		Logger:   log.NewPrefixLogger(),
@@ -44,7 +46,7 @@ func New(ctl *ui.Controller, state ui.State) *TermView {
 
 	switch p := state.GetProtocol().(type) {
 	case *proto.Http:
-		v.subviews = append(v.subviews, NewHttp(p, ctl.Shutdown, 0, 10))
+		v.subviews = append(v.subviews, NewHttp(p, v.flush, ctl.Shutdown, 0, 10))
 	default:
 	}
 
@@ -109,6 +111,9 @@ func (v *TermView) run() {
 	for {
 		v.Debug("Waiting for update")
 		select {
+		case <-v.flush:
+			termbox.Flush()
+
 		case obj := <-v.updates:
 			v.state = obj.(ui.State)
 			v.Render()

+ 8 - 1
src/ngrok/client/views/web/http.go

@@ -10,6 +10,7 @@ import (
 	"net/url"
 	"ngrok/client/ui"
 	"ngrok/client/views/web/static"
+	"ngrok/log"
 	"ngrok/proto"
 	"ngrok/util"
 	"strings"
@@ -65,7 +66,13 @@ func (whv *WebHttpView) update() {
 			htxn.Req.URL.Scheme = "http"
 
 			if htxn.Resp == nil {
-				whtxn := &WebHttpTxn{Id: util.RandId(), HttpTxn: htxn}
+				id, err := util.RandId(8)
+				if err != nil {
+					log.Error("Failed to generate txn identifier for web storage: %v", err)
+					continue
+				}
+
+				whtxn := &WebHttpTxn{Id: id, HttpTxn: htxn}
 
 				// XXX: unsafe map access from multiple go routines
 				whv.idToTxn[whtxn.Id] = whtxn

+ 21 - 5
src/ngrok/util/id.go

@@ -6,11 +6,27 @@ import (
 )
 
 // create a random identifier for this client
-func RandId() string {
-	b := make([]byte, 8)
-	_, err := rand.Read(b)
+func RandId(idlen int) (id string, err error) {
+	b := make([]byte, idlen)
+	n, err := rand.Read(b)
+
+	if n != idlen {
+		err = fmt.Errorf("Only generated %d random bytes, %d requested", n, idlen)
+		return
+	}
+
+	if err != nil {
+		return
+	}
+
+	id = fmt.Sprintf("%x", b)
+	return
+}
+
+func RandIdOrPanic(idlen int) string {
+	id, err := RandId(idlen)
 	if err != nil {
-		panic(fmt.Sprintf("rand.Rand failed trying to create identifier, %v", err))
+		panic(err)
 	}
-	return fmt.Sprintf("%x", b)
+	return id
 }