mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
New feature: automatic public domain using tunneling: https://github.com/kataras/iris/issues/1305
Former-commit-id: 54844edae9e5eed9bd6b17a06ec8d868923d3681
This commit is contained in:
parent
94f67f7fbb
commit
4c4b2f5d00
|
@ -78,6 +78,7 @@ Structuring depends on your own needs. We can't tell you how to design your own
|
||||||
### HTTP Listening
|
### HTTP Listening
|
||||||
|
|
||||||
- [Common, with address](http-listening/listen-addr/main.go)
|
- [Common, with address](http-listening/listen-addr/main.go)
|
||||||
|
* [public domain address](http-listening/listen-addr-public/main.go) **NEW**
|
||||||
* [omit server errors](http-listening/listen-addr/omit-server-errors/main.go)
|
* [omit server errors](http-listening/listen-addr/omit-server-errors/main.go)
|
||||||
- [UNIX socket file](http-listening/listen-unix/main.go)
|
- [UNIX socket file](http-listening/listen-unix/main.go)
|
||||||
- [TLS](http-listening/listen-tls/main.go)
|
- [TLS](http-listening/listen-tls/main.go)
|
||||||
|
|
1
_examples/http-listening/listen-addr-public/README.md
Normal file
1
_examples/http-listening/listen-addr-public/README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
![tunneling_screenshot.png](tunneling_screenshot.png)
|
36
_examples/http-listening/listen-addr-public/main.go
Normal file
36
_examples/http-listening/listen-addr-public/main.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/kataras/iris"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := iris.New()
|
||||||
|
|
||||||
|
app.Get("/", func(ctx iris.Context) {
|
||||||
|
ctx.HTML("<h1>Hello World!</h1>")
|
||||||
|
|
||||||
|
// Will print the ngrok public domain
|
||||||
|
// that your app is using to be served online.
|
||||||
|
ctx.Writef("From: %s",
|
||||||
|
ctx.Application().ConfigurationReadOnly().GetVHost())
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Run(iris.Addr(":8080"), iris.WithTunneling)
|
||||||
|
|
||||||
|
/* The full configuration can be set as:
|
||||||
|
app.Run(iris.Addr(":8080"), iris.WithConfiguration(
|
||||||
|
iris.Configuration{
|
||||||
|
Tunneling: iris.TunnelingConfiguration{
|
||||||
|
AuthToken: "my-ngrok-auth-client-token",
|
||||||
|
Bin: "/bin/path/for/ngrok",
|
||||||
|
Region: "eu",
|
||||||
|
WebInterface: "127.0.0.1:4040",
|
||||||
|
Tunnels: []iris.Tunnel{
|
||||||
|
{
|
||||||
|
Name: "MyApp",
|
||||||
|
Addr: ":8080",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
*/
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 164 KiB |
240
configuration.go
240
configuration.go
|
@ -1,8 +1,13 @@
|
||||||
package iris
|
package iris
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -346,7 +351,7 @@ func WithoutRemoteAddrHeader(headerName string) Configurator {
|
||||||
|
|
||||||
// WithOtherValue adds a value based on a key to the Other setting.
|
// WithOtherValue adds a value based on a key to the Other setting.
|
||||||
//
|
//
|
||||||
// See `Configuration`.
|
// See `Configuration.Other`.
|
||||||
func WithOtherValue(key string, val interface{}) Configurator {
|
func WithOtherValue(key string, val interface{}) Configurator {
|
||||||
return func(app *Application) {
|
return func(app *Application) {
|
||||||
if app.config.Other == nil {
|
if app.config.Other == nil {
|
||||||
|
@ -356,61 +361,216 @@ func WithOtherValue(key string, val interface{}) Configurator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTunnel is the `iris.Configurator` for the `iris.Configuration.Tunnel` field.
|
// WithTunneling is the `iris.Configurator` for the `iris.Configuration.Tunneling` field.
|
||||||
// It requires the "name" which is used to create on server ran and terminate on server shutdown
|
// It's used to enable http tunneling for an Iris Application, per registered host
|
||||||
// the ngrok http(s) tunnel for an Iris app.
|
//
|
||||||
// Its second variadic input argument can accept one or more functions
|
// Alternatively use the `iris.WithConfiguration(iris.Configuration{Tunneling: iris.TunnelingConfiguration{ ...}}}`.
|
||||||
// that accept a pointer to TunnelConfiguration value which can be used
|
func WithTunneling(app *Application) {
|
||||||
// for further customization of the tunnel configuration one.
|
conf := TunnelingConfiguration{
|
||||||
// Alternatively use the `iris.WithConfiguration(iris.Configuration{Tunnel: iris.TunnelConfiguration{ ...}}}`.
|
Tunnels: []Tunnel{{}}, // create empty tunnel, its addr and name are set right before host serve.
|
||||||
func WithTunnel(name string, tunnelConfigurator ...func(*TunnelConfiguration)) Configurator {
|
|
||||||
conf := &TunnelConfiguration{Name: name}
|
|
||||||
|
|
||||||
for _, tc := range tunnelConfigurator {
|
|
||||||
if tc != nil {
|
|
||||||
tc(conf)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(app *Application) {
|
app.config.Tunneling = conf
|
||||||
app.config.Tunnel = *conf
|
|
||||||
// TODO: do the work here if the vhost is set (when this configurator is set through app.Run)
|
|
||||||
// or find a way to do it when `app.Configure` is used instead, probably it will go inside app.Run though.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TunnelConfiguration contains configuration
|
// Tunnel is the Tunnels field of the TunnelingConfiguration structure.
|
||||||
// for the optional tunneling through ngrok feature.
|
type Tunnel struct {
|
||||||
type TunnelConfiguration struct {
|
|
||||||
// Name is the only one required field,
|
// Name is the only one required field,
|
||||||
// it is used to create and close tunnels, e.g. "MyApp".
|
// it is used to create and close tunnels, e.g. "MyApp".
|
||||||
// If this field is not empty then ngrok tunnels will be created
|
// If this field is not empty then ngrok tunnels will be created
|
||||||
// when the iris app is up and running.
|
// when the iris app is up and running.
|
||||||
Name string `json:"name" yaml:"Name" toml:"Name"`
|
Name string `json:"name" yaml:"Name" toml:"Name"`
|
||||||
// Usernamem and Password fields are optionally and are used
|
|
||||||
// to authenticate the ngrok tunnel access.
|
|
||||||
Username string `json:"username,omitempty" yaml:"Username" toml:"Username"`
|
|
||||||
Password string `json:"password,omitemmpty" yaml:"Password" toml:"Password"`
|
|
||||||
|
|
||||||
// Bin is the system binary path of the ngrok executable file.
|
|
||||||
// If it's empty then the framework will try to find it through system env variables.
|
|
||||||
Bin string `json:"bin,omitempty" yaml:"Bin" toml:"Bin"`
|
|
||||||
// Addr is basically optionally as it will be set through
|
// Addr is basically optionally as it will be set through
|
||||||
// Iris built-in Runners, however, if `iris.Raw` is used
|
// Iris built-in Runners, however, if `iris.Raw` is used
|
||||||
// then this field should be set of form 'hostname:port'
|
// then this field should be set of form 'hostname:port'
|
||||||
// because framework cannot be aware
|
// because framework cannot be aware
|
||||||
// of the address you used to run the server on this custom runner.
|
// of the address you used to run the server on this custom runner.
|
||||||
Addr string `json:"addr,omitempty" yaml:"Addr" toml:"Addr"`
|
Addr string `json:"addr,omitempty" yaml:"Addr" toml:"Addr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TunnelingConfiguration contains configuration
|
||||||
|
// for the optional tunneling through ngrok feature.
|
||||||
|
// Note that the ngrok should be already installed at the host machine.
|
||||||
|
type TunnelingConfiguration struct {
|
||||||
|
// AuthToken field is optionally and can be used
|
||||||
|
// to authenticate the ngrok access.
|
||||||
|
// ngrok authtoken <YOUR_AUTHTOKEN>
|
||||||
|
AuthToken string `json:"authToken,omitempty" yaml:"AuthToken" toml:"AuthToken"`
|
||||||
|
|
||||||
|
// No...
|
||||||
|
// Config is optionally and can be used
|
||||||
|
// to load ngrok configuration from file system path.
|
||||||
|
//
|
||||||
|
// If you don't specify a location for a configuration file,
|
||||||
|
// ngrok tries to read one from the default location $HOME/.ngrok2/ngrok.yml.
|
||||||
|
// The configuration file is optional; no error is emitted if that path does not exist.
|
||||||
|
// Config string `json:"config,omitempty" yaml:"Config" toml:"Config"`
|
||||||
|
|
||||||
|
// Bin is the system binary path of the ngrok executable file.
|
||||||
|
// If it's empty then the framework will try to find it through system env variables.
|
||||||
|
Bin string `json:"bin,omitempty" yaml:"Bin" toml:"Bin"`
|
||||||
|
|
||||||
// WebUIAddr is the web interface address of an already-running ngrok instance.
|
// WebUIAddr is the web interface address of an already-running ngrok instance.
|
||||||
// Iris will try to fetch the default web interface address(http://127.0.0.1:4040)
|
// Iris will try to fetch the default web interface address(http://127.0.0.1:4040)
|
||||||
// to determinate if a ngrok instance is running before try to start it manually.
|
// to determinate if a ngrok instance is running before try to start it manually.
|
||||||
// However if a custom web interface address is used,
|
// However if a custom web interface address is used,
|
||||||
// this field must be set e.g. http://127.0.0.1:5050.
|
// this field must be set e.g. http://127.0.0.1:5050.
|
||||||
WebInterface string `json:"webInterface" yaml:"WebInterface" toml:"WebInterface"`
|
WebInterface string `json:"webInterface,omitempty" yaml:"WebInterface" toml:"WebInterface"`
|
||||||
|
|
||||||
|
// Region is optionally, can be used to set the region which defaults to "us".
|
||||||
|
// Available values are:
|
||||||
|
// "us" for United States
|
||||||
|
// "eu" for Europe
|
||||||
|
// "ap" for Asia/Pacific
|
||||||
|
// "au" for Australia
|
||||||
|
// "sa" for South America
|
||||||
|
// "jp" forJapan
|
||||||
|
// "in" for India
|
||||||
|
Region string `json:"region,omitempty" yaml:"Region" toml:"Region"`
|
||||||
|
|
||||||
|
// Tunnels the collection of the tunnels.
|
||||||
|
// One tunnel per Iris Host per Application, usually you only need one.
|
||||||
|
Tunnels []Tunnel `json:"tunnels" yaml:"Tunnels" toml:"Tunnels"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *TunnelConfiguration) isEnabled() bool {
|
func (tc *TunnelingConfiguration) isEnabled() bool {
|
||||||
return tc != nil && tc.Name != ""
|
return tc != nil && len(tc.Tunnels) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TunnelingConfiguration) isNgrokRunning() bool {
|
||||||
|
_, err := http.Get(tc.WebInterface)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://ngrok.com/docs
|
||||||
|
type ngrokTunnel struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Addr string `json:"addr"`
|
||||||
|
Proto string `json:"proto"`
|
||||||
|
Auth string `json:"auth"`
|
||||||
|
BindTLS bool `json:"bind_tls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TunnelingConfiguration) startTunnel(t Tunnel, publicAddr *string) error {
|
||||||
|
tunnelAPIRequest := ngrokTunnel{
|
||||||
|
Name: t.Name,
|
||||||
|
Addr: t.Addr,
|
||||||
|
Proto: "http",
|
||||||
|
BindTLS: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.isNgrokRunning() {
|
||||||
|
ngrokBin := "ngrok" // environment binary.
|
||||||
|
if tc.Bin != "" {
|
||||||
|
ngrokBin = tc.Bin
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.AuthToken != "" {
|
||||||
|
cmd := exec.Command(ngrokBin, "authtoken", tc.AuthToken)
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start -none, start without tunnels.
|
||||||
|
// and finally the -log stdout logs to the stdout otherwise the pipe will never be able to read from, spent a lot of time on this lol.
|
||||||
|
cmd := exec.Command(ngrokBin, "start", "-none", "-log", "stdout")
|
||||||
|
|
||||||
|
// if tc.Config != "" {
|
||||||
|
// cmd.Args = append(cmd.Args, []string{"-config", tc.Config}...)
|
||||||
|
// }
|
||||||
|
if tc.Region != "" {
|
||||||
|
cmd.Args = append(cmd.Args, []string{"-region", tc.Region}...)
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cmd.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p := make([]byte, 256)
|
||||||
|
okText := []byte("client session established")
|
||||||
|
for {
|
||||||
|
n, err := stdout.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need this one:
|
||||||
|
// msg="client session established"
|
||||||
|
// note that this will block if something terrible happens
|
||||||
|
// but ngrok's errors are strong so the error is easy to be resolved without any logs.
|
||||||
|
if bytes.Contains(p[:n], okText) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tc.createTunnel(tunnelAPIRequest, publicAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TunnelingConfiguration) stopTunnel(t Tunnel) error {
|
||||||
|
url := fmt.Sprintf("%s/api/tunnels/%s", tc.WebInterface, t.Name)
|
||||||
|
req, err := http.NewRequest(http.MethodDelete, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != StatusNoContent {
|
||||||
|
return fmt.Errorf("stop return an unexpected status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc TunnelingConfiguration) createTunnel(tunnelAPIRequest ngrokTunnel, publicAddr *string) error {
|
||||||
|
url := fmt.Sprintf("%s/api/tunnels", tc.WebInterface)
|
||||||
|
requestData, err := json.Marshal(tunnelAPIRequest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Post(url, context.ContentJSONHeaderValue, bytes.NewBuffer(requestData))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
type publicAddrOrErrResp struct {
|
||||||
|
PublicAddr string `json:"public_url"`
|
||||||
|
Details struct {
|
||||||
|
ErrorText string `json:"err"` // when can't bind more addresses, status code was successful.
|
||||||
|
} `json:"details"`
|
||||||
|
ErrMsg string `json:"msg"` // when ngrok is not yet ready, status code was unsuccessful.
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiResponse publicAddrOrErrResp
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&apiResponse)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if errText := apiResponse.ErrMsg; errText != "" {
|
||||||
|
return errors.New(errText)
|
||||||
|
}
|
||||||
|
|
||||||
|
if errText := apiResponse.Details.ErrorText; errText != "" {
|
||||||
|
return errors.New(errText)
|
||||||
|
}
|
||||||
|
|
||||||
|
*publicAddr = apiResponse.PublicAddr
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration the whole configuration for an iris instance
|
// Configuration the whole configuration for an iris instance
|
||||||
|
@ -421,11 +581,9 @@ type Configuration struct {
|
||||||
// It can be retrieved by the context if needed (i.e router for subdomains)
|
// It can be retrieved by the context if needed (i.e router for subdomains)
|
||||||
vhost string
|
vhost string
|
||||||
|
|
||||||
// TunnelConfiguration can be optionally set
|
// Tunneling can be optionally set to enable ngrok http(s) tunneling for this Iris app instance.
|
||||||
// to enable ngrok http(s) tunneling for this Iris app instance.
|
// See the `WithTunneling` Configurator too.
|
||||||
// If iris logger's level is set to "debug" then it will print out
|
Tunneling TunnelingConfiguration `json:"tunneling,omitempty" yaml:"Tunneling" toml"Tunneling"`
|
||||||
// ngrok tunneling info messages too.
|
|
||||||
Tunnel TunnelConfiguration `json:"tunnel,omitempty" yaml:"Tunnel" toml"Tunnel"`
|
|
||||||
|
|
||||||
// IgnoreServerErrors will cause to ignore the matched "errors"
|
// IgnoreServerErrors will cause to ignore the matched "errors"
|
||||||
// from the main application's `Run` function.
|
// from the main application's `Run` function.
|
||||||
|
@ -740,8 +898,8 @@ func WithConfiguration(c Configuration) Configurator {
|
||||||
return func(app *Application) {
|
return func(app *Application) {
|
||||||
main := app.config
|
main := app.config
|
||||||
|
|
||||||
if c.Tunnel.isEnabled() {
|
if c.Tunneling.isEnabled() {
|
||||||
main.Tunnel = c.Tunnel
|
main.Tunneling = c.Tunneling
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := c.IgnoreServerErrors; len(v) > 0 {
|
if v := c.IgnoreServerErrors; len(v) > 0 {
|
||||||
|
|
5
go19.go
5
go19.go
|
@ -62,7 +62,10 @@ type (
|
||||||
// Look the `core/router#APIBuilder` for its implementation.
|
// Look the `core/router#APIBuilder` for its implementation.
|
||||||
//
|
//
|
||||||
// A shortcut for the `core/router#Party`, useful when `PartyFunc` is being used.
|
// A shortcut for the `core/router#Party`, useful when `PartyFunc` is being used.
|
||||||
Party = router.Party
|
Party = router.Party
|
||||||
|
// DirOptions contains the optional settings that
|
||||||
|
// `FileServer` and `Party#HandleDir` can use to serve files and assets.
|
||||||
|
// A shortcut for the `router.DirOptions`, useful when `FileServer` or `HandleDir` is being used.
|
||||||
DirOptions = router.DirOptions
|
DirOptions = router.DirOptions
|
||||||
// ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves.
|
// ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves.
|
||||||
// Usage:
|
// Usage:
|
||||||
|
|
74
iris.go
74
iris.go
|
@ -2,11 +2,14 @@ package iris
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// std packages
|
// std packages
|
||||||
|
|
||||||
stdContext "context"
|
stdContext "context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -365,7 +368,14 @@ var (
|
||||||
//
|
//
|
||||||
// A shortcut for the `context#NewConditionalHandler`.
|
// A shortcut for the `context#NewConditionalHandler`.
|
||||||
NewConditionalHandler = context.NewConditionalHandler
|
NewConditionalHandler = context.NewConditionalHandler
|
||||||
|
// FileServer returns a Handler which serves files from a specific system, phyisical, directory
|
||||||
|
// or an embedded one.
|
||||||
|
// The first parameter is the directory, relative to the executable program.
|
||||||
|
// The second optional parameter is any optional settings that the caller can use.
|
||||||
|
//
|
||||||
|
// See `Party#HandleDir` too.
|
||||||
|
// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/file-server
|
||||||
|
// A shortcut for the `router.FileServer`.
|
||||||
FileServer = router.FileServer
|
FileServer = router.FileServer
|
||||||
// StripPrefix returns a handler that serves HTTP requests
|
// StripPrefix returns a handler that serves HTTP requests
|
||||||
// by removing the given prefix from the request URL's Path
|
// by removing the given prefix from the request URL's Path
|
||||||
|
@ -586,6 +596,14 @@ var RegisterOnInterrupt = host.RegisterOnInterrupt
|
||||||
// Shutdown gracefully terminates all the application's server hosts.
|
// Shutdown gracefully terminates all the application's server hosts.
|
||||||
// Returns an error on the first failure, otherwise nil.
|
// Returns an error on the first failure, otherwise nil.
|
||||||
func (app *Application) Shutdown(ctx stdContext.Context) error {
|
func (app *Application) Shutdown(ctx stdContext.Context) error {
|
||||||
|
for _, t := range app.config.Tunneling.Tunnels {
|
||||||
|
if t.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
app.config.Tunneling.stopTunnel(t)
|
||||||
|
}
|
||||||
|
|
||||||
for i, su := range app.Hosts {
|
for i, su := range app.Hosts {
|
||||||
app.logger.Debugf("Host[%d]: Shutdown now", i)
|
app.logger.Debugf("Host[%d]: Shutdown now", i)
|
||||||
if err := su.Shutdown(ctx); err != nil {
|
if err := su.Shutdown(ctx); err != nil {
|
||||||
|
@ -822,6 +840,8 @@ func (app *Application) Run(serve Runner, withOrWithout ...Configurator) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Configure(withOrWithout...)
|
app.Configure(withOrWithout...)
|
||||||
|
app.tryStartTunneling()
|
||||||
|
|
||||||
app.logger.Debugf("Application: running using %d host(s)", len(app.Hosts)+1)
|
app.logger.Debugf("Application: running using %d host(s)", len(app.Hosts)+1)
|
||||||
|
|
||||||
// this will block until an error(unless supervisor's DeferFlow called from a Task).
|
// this will block until an error(unless supervisor's DeferFlow called from a Task).
|
||||||
|
@ -832,3 +852,55 @@ func (app *Application) Run(serve Runner, withOrWithout ...Configurator) error {
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://ngrok.com/docs
|
||||||
|
func (app *Application) tryStartTunneling() {
|
||||||
|
if !app.config.Tunneling.isEnabled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hostIndex := 0
|
||||||
|
|
||||||
|
app.ConfigureHost(func(su *host.Supervisor) {
|
||||||
|
su.RegisterOnServe(func(h host.TaskHost) {
|
||||||
|
tc := app.config.Tunneling
|
||||||
|
if tc.WebInterface == "" {
|
||||||
|
tc.WebInterface = "http://127.0.0.1:4040"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tc.Tunnels) > hostIndex {
|
||||||
|
t := tc.Tunnels[hostIndex]
|
||||||
|
if t.Name == "" {
|
||||||
|
t.Name = fmt.Sprintf("iris-app-%d-%s", hostIndex+1, time.Now().Format(app.config.TimeFormat))
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Addr == "" {
|
||||||
|
t.Addr = su.Server.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
var publicAddr string
|
||||||
|
err := tc.startTunnel(t, &publicAddr)
|
||||||
|
if err != nil {
|
||||||
|
app.Logger().Errorf("Host: tunneling error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// to make subdomains resolution still based on this new remote, public addresses.
|
||||||
|
app.config.vhost = publicAddr[strings.Index(publicAddr, "://")+3:]
|
||||||
|
|
||||||
|
// app.logger.Debugf("Host: new virtual host is %s", app.config.vhost)
|
||||||
|
// app.Logger().Printer.Output.Write([]byte("))
|
||||||
|
|
||||||
|
// directLog := []byte(fmt.Sprintf("| Public Address: %s |\n", publicAddr))
|
||||||
|
// box := bytes.Repeat([]byte("="), len(directLog))
|
||||||
|
// directLog = append(append(box, []byte("\n")...), append(directLog, append(box, []byte("\n")...)...)...)
|
||||||
|
directLog := []byte(fmt.Sprintf("⬝ Public Address: %s\n", publicAddr))
|
||||||
|
app.Logger().Printer.Output.Write(directLog)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
hostIndex++
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user