tunnel.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package tunnel
  2. import (
  3. "fmt"
  4. "net/http"
  5. "time"
  6. )
  7. const (
  8. // DefaultWebInterface is the default web interface for ngrok.
  9. DefaultWebInterface = "http://127.0.0.1:4040"
  10. // DefaultAddr is the default local web server address will be set
  11. // if tunnel's Addr field is missing.
  12. DefaultAddr = "localhost:8080"
  13. )
  14. // DefaultNameGenerator is a function which should set
  15. // the application's name if Tunnel's Name field is missing.
  16. //
  17. // This name can be used to stop a tunnel as well.
  18. var DefaultNameGenerator = func(tunnelIndex int) string {
  19. return fmt.Sprintf("app-%d-%s", tunnelIndex+1, time.Now().Format(http.TimeFormat))
  20. }
  21. // StartError is a custom error type which provides
  22. // details about the started and failed to start tunnels
  23. // so the caller can decide to retry the failed once or
  24. // to stop the succeed ones.
  25. //
  26. // Usage:
  27. // publicAddr, err := tunnel.Start(Configuration{...})
  28. // if err != nil {
  29. // if startErr, ok := err.(tunnel.Error);ok {
  30. // startErr.Failed tunnels...
  31. // startErr.Succeed tunnels...
  32. // startErr.Err error...
  33. // }
  34. // }
  35. //
  36. // See `Start` package-level function.
  37. type StartError struct {
  38. Succeed []Tunnel
  39. Failed []Tunnel
  40. Err error
  41. }
  42. // Error returns the underline error's message.
  43. func (e StartError) Error() string {
  44. if e.Err == nil {
  45. return ""
  46. }
  47. return e.Err.Error()
  48. }
  49. // Start creates a localhost ngrok tunnel based on the given Configuration.
  50. // that's why it may return a non empty list among with a non-nil error.
  51. //
  52. // The ngrok instance may be running or not. Meaning that
  53. // if the ngrok binary instance is not already running then
  54. // this function will try to start it first.
  55. func Start(c Configurator) (publicAddrs []string, err error) {
  56. cfg := getConfiguration(c)
  57. for tunnIdx, t := range cfg.Tunnels {
  58. if t.Name == "" {
  59. if DefaultNameGenerator != nil {
  60. t.Name = DefaultNameGenerator(tunnIdx)
  61. }
  62. }
  63. if t.Addr == "" {
  64. t.Addr = DefaultAddr
  65. }
  66. var publicAddr string
  67. err = cfg.StartTunnel(t, &publicAddr)
  68. if err != nil {
  69. err = StartError{
  70. Succeed: cfg.Tunnels[:tunnIdx],
  71. Failed: cfg.Tunnels[tunnIdx:],
  72. Err: err,
  73. }
  74. break
  75. }
  76. publicAddrs = append(publicAddrs, publicAddr)
  77. }
  78. // strings.Join(publicAddrs, ", ")
  79. return publicAddrs, err
  80. }
  81. // MustStart same as Start package-level function but it panics on error.
  82. func MustStart(c Configurator) []string {
  83. publicAddrs, err := Start(c)
  84. if err != nil {
  85. panic(err)
  86. }
  87. return publicAddrs
  88. }
  89. // StopTunnel deletes a tunnel from a running ngrok instance.
  90. // If the tunnelName is "*" then it stops all registered tunnels.
  91. // The tunnelName can be also the server's original addr.
  92. // Exits on first error.
  93. func StopTunnel(c Configurator, tunnelName string) error {
  94. cfg := getConfiguration(c)
  95. for _, tunnel := range cfg.Tunnels {
  96. if tunnelName == "*" {
  97. if err := cfg.StopTunnel(tunnel); err != nil {
  98. return err
  99. }
  100. } else if tunnel.Name == tunnelName || tunnel.Addr == tunnelName {
  101. return cfg.StopTunnel(tunnel)
  102. }
  103. }
  104. return nil
  105. }