main.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package client
  2. import (
  3. log "code.google.com/p/log4go"
  4. "crypto/rand"
  5. "fmt"
  6. "net"
  7. "ngrok/client/ui"
  8. "ngrok/conn"
  9. nlog "ngrok/log"
  10. "ngrok/proto"
  11. "runtime"
  12. "time"
  13. )
  14. /**
  15. * Connect to the ngrok server
  16. */
  17. func connect(addr string, typ string) (c conn.Conn, err error) {
  18. var (
  19. tcpAddr *net.TCPAddr
  20. tcpConn *net.TCPConn
  21. )
  22. if tcpAddr, err = net.ResolveTCPAddr("tcp", addr); err != nil {
  23. return
  24. }
  25. log.Debug("Dialing %v", addr)
  26. if tcpConn, err = net.DialTCP("tcp", nil, tcpAddr); err != nil {
  27. return
  28. }
  29. c = conn.NewTCP(tcpConn, typ)
  30. c.Debug("Connected to: %v", tcpAddr)
  31. return c, nil
  32. }
  33. /**
  34. * Establishes and manages a tunnel proxy connection with the server
  35. */
  36. func proxy(proxyAddr string, s *State) {
  37. start := time.Now()
  38. remoteConn, err := connect(proxyAddr, "pxy")
  39. if err != nil {
  40. panic(err)
  41. }
  42. defer remoteConn.Close()
  43. err = proto.WriteMsg(remoteConn, &proto.RegProxyMsg{Url: s.publicUrl})
  44. if err != nil {
  45. panic(err)
  46. }
  47. localConn, err := connect(s.opts.localaddr, "prv")
  48. if err != nil {
  49. remoteConn.Warn("Failed to open private leg %s: %v", s.opts.localaddr, err)
  50. return
  51. }
  52. defer localConn.Close()
  53. m := s.metrics
  54. m.proxySetupTimer.Update(time.Since(start))
  55. m.connMeter.Mark(1)
  56. s.Update()
  57. m.connTimer.Time(func() {
  58. if s.opts.protocol == "http" {
  59. teeConn := conn.NewTee(remoteConn)
  60. remoteConn = teeConn
  61. go conn.ParseHttp(teeConn, s.history.reqs, s.history.resps)
  62. }
  63. bytesIn, bytesOut := conn.Join(localConn, remoteConn)
  64. m.bytesIn.Update(bytesIn)
  65. m.bytesOut.Update(bytesOut)
  66. m.bytesInCount.Inc(bytesIn)
  67. m.bytesOutCount.Inc(bytesOut)
  68. })
  69. s.Update()
  70. }
  71. /**
  72. * Establishes and manages a tunnel control connection with the server
  73. */
  74. func control(s *State) {
  75. defer func() {
  76. if r := recover(); r != nil {
  77. log.Error("Recovering from failure %v, attempting to reconnect to server after 10 seconds . . .", r)
  78. s.status = "reconnecting"
  79. s.Update()
  80. time.Sleep(10 * time.Second)
  81. go control(s)
  82. }
  83. }()
  84. // establish control channel
  85. conn, err := connect(s.opts.server, "ctl")
  86. if err != nil {
  87. panic(err)
  88. }
  89. defer conn.Close()
  90. // register with the server
  91. err = proto.WriteMsg(conn, &proto.RegMsg{
  92. Protocol: s.opts.protocol,
  93. OS: runtime.GOOS,
  94. Hostname: s.opts.hostname,
  95. Subdomain: s.opts.subdomain,
  96. ClientId: s.id,
  97. })
  98. if err != nil {
  99. panic(err)
  100. }
  101. // wait for the server to ack our register
  102. var regAck proto.RegAckMsg
  103. if err = proto.ReadMsgInto(conn, &regAck); err != nil {
  104. panic(err)
  105. }
  106. // update UI state
  107. conn.Info("Tunnel established at %v", regAck.Url)
  108. //state.version = regAck.Version
  109. s.publicUrl = regAck.Url
  110. s.status = "online"
  111. s.Update()
  112. // main control loop
  113. for {
  114. var msg proto.Message
  115. if msg, err = proto.ReadMsg(conn); err != nil {
  116. panic(err)
  117. }
  118. switch msg.GetType() {
  119. case "ReqProxyMsg":
  120. go proxy(regAck.ProxyAddr, s)
  121. case "PingMsg":
  122. proto.WriteMsg(conn, &proto.PongMsg{})
  123. }
  124. }
  125. }
  126. // create a random identifier for this client
  127. func mkid() string {
  128. b := make([]byte, 8)
  129. _, err := rand.Read(b)
  130. if err != nil {
  131. panic(fmt.Sprintf("Couldn't create random client identifier, %v", err))
  132. }
  133. return fmt.Sprintf("%x", b)
  134. }
  135. func Main() {
  136. // XXX: should do this only if they ask us too
  137. nlog.LogToFile()
  138. // parse options
  139. opts := parseArgs()
  140. // init terminal, http UI
  141. termView := ui.NewTerm()
  142. httpView := ui.NewHttp(9999)
  143. // init client state
  144. s := &State{
  145. // unique client id
  146. id: mkid(),
  147. // ui communication channels
  148. ui: ui.NewUi(termView, httpView),
  149. //ui: ui.NewUi(httpView),
  150. // command-line options
  151. opts: opts,
  152. // metrics
  153. metrics: NewClientMetrics(),
  154. }
  155. // request history
  156. // XXX: don't use a callback, use a channel
  157. // and define it inline in the struct
  158. s.history = NewRequestHistory(opts.historySize, s.metrics, func(history []*RequestHistoryEntry) {
  159. s.historyEntries = history
  160. s.Update()
  161. })
  162. // set initial ui state
  163. s.status = "connecting"
  164. s.Update()
  165. go control(s)
  166. s.ui.Wait.Add(1)
  167. go func() {
  168. defer s.ui.Wait.Done()
  169. for {
  170. select {
  171. case cmd := <-s.ui.Cmds:
  172. switch cmd {
  173. case ui.QUIT:
  174. s.stopping = true
  175. s.Update()
  176. return
  177. }
  178. }
  179. }
  180. }()
  181. s.ui.Wait.Wait()
  182. }