iris.go 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222
  1. package iris
  2. import (
  3. "bytes"
  4. stdContext "context"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "log"
  9. "math"
  10. "net"
  11. "net/http"
  12. "os"
  13. "regexp"
  14. "strings"
  15. "sync"
  16. "time"
  17. "github.com/kataras/iris/v12/context"
  18. "github.com/kataras/iris/v12/core/host"
  19. "github.com/kataras/iris/v12/core/netutil"
  20. "github.com/kataras/iris/v12/core/router"
  21. "github.com/kataras/iris/v12/i18n"
  22. "github.com/kataras/iris/v12/middleware/cors"
  23. "github.com/kataras/iris/v12/middleware/recover"
  24. "github.com/kataras/iris/v12/middleware/requestid"
  25. "github.com/kataras/iris/v12/view"
  26. "github.com/kataras/golog"
  27. "github.com/kataras/tunnel"
  28. "github.com/tdewolff/minify/v2"
  29. "github.com/tdewolff/minify/v2/css"
  30. "github.com/tdewolff/minify/v2/html"
  31. "github.com/tdewolff/minify/v2/js"
  32. "github.com/tdewolff/minify/v2/json"
  33. "github.com/tdewolff/minify/v2/svg"
  34. "github.com/tdewolff/minify/v2/xml"
  35. )
  36. // Version is the current version of the Iris Web Framework.
  37. const Version = "12.2.10"
  38. // Byte unit helpers.
  39. const (
  40. B = 1 << (10 * iota)
  41. KB
  42. MB
  43. GB
  44. TB
  45. PB
  46. EB
  47. )
  48. // Application is responsible to manage the state of the application.
  49. // It contains and handles all the necessary parts to create a fast web server.
  50. type Application struct {
  51. // routing embedded | exposing APIBuilder's and Router's public API.
  52. *router.APIBuilder
  53. *router.Router
  54. router.HTTPErrorHandler // if Router is Downgraded this is nil.
  55. ContextPool *context.Pool
  56. // See SetContextErrorHandler, defaults to nil.
  57. contextErrorHandler context.ErrorHandler
  58. // config contains the configuration fields
  59. // all fields defaults to something that is working, developers don't have to set it.
  60. config *Configuration
  61. // the golog logger instance, defaults to "Info" level messages (all except "Debug")
  62. logger *golog.Logger
  63. // I18n contains localization and internationalization support.
  64. // Use the `Load` or `LoadAssets` to locale language files.
  65. //
  66. // See `Context#Tr` method for request-based translations.
  67. I18n *i18n.I18n
  68. // Validator is the request body validator, defaults to nil.
  69. Validator context.Validator
  70. // Minifier to minify responses.
  71. minifier *minify.M
  72. // view engine
  73. view *view.View
  74. // used for build
  75. builded bool
  76. defaultMode bool
  77. // OnBuild is a single function which
  78. // is fired on the first `Build` method call.
  79. // If reports an error then the execution
  80. // is stopped and the error is logged.
  81. // It's nil by default except when `Switch` instead of `New` or `Default`
  82. // is used to initialize the Application.
  83. // Users can wrap it to accept more events.
  84. OnBuild func() error
  85. mu sync.RWMutex
  86. // name is the application name and the log prefix for
  87. // that Application instance's Logger. See `SetName` and `String`.
  88. // Defaults to IRIS_APP_NAME envrinoment variable otherwise empty.
  89. name string
  90. // Hosts contains a list of all servers (Host Supervisors) that this app is running on.
  91. //
  92. // Hosts may be empty only if application ran(`app.Run`) with `iris.Raw` option runner,
  93. // otherwise it contains a single host (`app.Hosts[0]`).
  94. //
  95. // Additional Host Supervisors can be added to that list by calling the `app.NewHost` manually.
  96. //
  97. // Hosts field is available after `Run` or `NewHost`.
  98. Hosts []*host.Supervisor
  99. hostConfigurators []host.Configurator
  100. runError error
  101. runErrorMu sync.RWMutex
  102. }
  103. // New creates and returns a fresh empty iris *Application instance.
  104. func New() *Application {
  105. config := DefaultConfiguration()
  106. app := &Application{
  107. config: &config,
  108. Router: router.NewRouter(),
  109. I18n: i18n.New(),
  110. minifier: newMinifier(),
  111. view: new(view.View),
  112. }
  113. logger := newLogger(app)
  114. app.logger = logger
  115. app.APIBuilder = router.NewAPIBuilder(logger)
  116. app.ContextPool = context.New(func() interface{} {
  117. return context.NewContext(app)
  118. })
  119. context.RegisterApplication(app)
  120. return app
  121. }
  122. // Default returns a new Application.
  123. // Default with "debug" Logger Level.
  124. // Localization enabled on "./locales" directory
  125. // and HTML templates on "./views" or "./templates" directory.
  126. // CORS (allow all), Recovery and
  127. // Request ID middleware already registered.
  128. func Default() *Application {
  129. app := New()
  130. // Set default log level.
  131. app.logger.SetLevel("debug")
  132. app.logger.Debugf(`Log level set to "debug"`)
  133. /* #2046.
  134. // Register the accesslog middleware.
  135. logFile, err := os.OpenFile("./access.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
  136. if err == nil {
  137. // Close the file on shutdown.
  138. app.ConfigureHost(func(su *Supervisor) {
  139. su.RegisterOnShutdown(func() {
  140. logFile.Close()
  141. })
  142. })
  143. ac := accesslog.New(logFile)
  144. ac.AddOutput(app.logger.Printer)
  145. app.UseRouter(ac.Handler)
  146. app.logger.Debugf("Using <%s> to log requests", logFile.Name())
  147. }
  148. */
  149. // Register the requestid middleware
  150. // before recover so current Context.GetID() contains the info on panic logs.
  151. app.UseRouter(requestid.New())
  152. app.logger.Debugf("Using <UUID4> to identify requests")
  153. // Register the recovery, after accesslog and recover,
  154. // before end-developer's middleware.
  155. app.UseRouter(recover.New())
  156. // Register CORS (allow any origin to pass through) middleware.
  157. app.UseRouter(cors.New().
  158. ExtractOriginFunc(cors.DefaultOriginExtractor).
  159. ReferrerPolicy(cors.NoReferrerWhenDowngrade).
  160. AllowOriginFunc(cors.AllowAnyOrigin).
  161. Handler())
  162. app.defaultMode = true
  163. return app
  164. }
  165. func newLogger(app *Application) *golog.Logger {
  166. logger := golog.Default.Child(app)
  167. if name := os.Getenv("IRIS_APP_NAME"); name != "" {
  168. app.name = name
  169. logger.SetChildPrefix(name)
  170. }
  171. return logger
  172. }
  173. // SetName sets a unique name to this Iris Application.
  174. // It sets a child prefix for the current Application's Logger.
  175. // Look `String` method too.
  176. //
  177. // It returns this Application.
  178. func (app *Application) SetName(appName string) *Application {
  179. app.mu.Lock()
  180. defer app.mu.Unlock()
  181. if app.name == "" {
  182. app.logger.SetChildPrefix(appName)
  183. }
  184. app.name = appName
  185. return app
  186. }
  187. // String completes the fmt.Stringer interface and it returns
  188. // the application's name.
  189. // If name was not set by `SetName` or `IRIS_APP_NAME` environment variable
  190. // then this will return an empty string.
  191. func (app *Application) String() string {
  192. return app.name
  193. }
  194. // WWW creates and returns a "www." subdomain.
  195. // The difference from `app.Subdomain("www")` or `app.Party("www.")` is that the `app.WWW()` method
  196. // wraps the router so all http(s)://mydomain.com will be redirect to http(s)://www.mydomain.com.
  197. // Other subdomains can be registered using the app: `sub := app.Subdomain("mysubdomain")`,
  198. // child subdomains can be registered using the www := app.WWW(); www.Subdomain("wwwchildSubdomain").
  199. func (app *Application) WWW() router.Party {
  200. return app.SubdomainRedirect(app, app.Subdomain("www"))
  201. }
  202. // SubdomainRedirect registers a router wrapper which
  203. // redirects(StatusMovedPermanently) a (sub)domain to another subdomain or to the root domain as fast as possible,
  204. // before the router's try to execute route's handler(s).
  205. //
  206. // It receives two arguments, they are the from and to/target locations,
  207. // 'from' can be a wildcard subdomain as well (app.WildcardSubdomain())
  208. // 'to' is not allowed to be a wildcard for obvious reasons,
  209. // 'from' can be the root domain(app) when the 'to' is not the root domain and visa-versa.
  210. //
  211. // Usage:
  212. // www := app.Subdomain("www") <- same as app.Party("www.")
  213. // app.SubdomainRedirect(app, www)
  214. // This will redirect all http(s)://mydomain.com/%anypath% to http(s)://www.mydomain.com/%anypath%.
  215. //
  216. // One or more subdomain redirects can be used to the same app instance.
  217. //
  218. // If you need more information about this implementation then you have to navigate through
  219. // the `core/router#NewSubdomainRedirectWrapper` function instead.
  220. //
  221. // Example: https://github.com/kataras/iris/tree/main/_examples/routing/subdomains/redirect
  222. func (app *Application) SubdomainRedirect(from, to router.Party) router.Party {
  223. sd := router.NewSubdomainRedirectWrapper(app.ConfigurationReadOnly().GetVHost, from.GetRelPath(), to.GetRelPath())
  224. app.Router.AddRouterWrapper(sd)
  225. return to
  226. }
  227. // Configure can called when modifications to the framework instance needed.
  228. // It accepts the framework instance
  229. // and returns an error which if it's not nil it's printed to the logger.
  230. // See configuration.go for more.
  231. //
  232. // Returns itself in order to be used like `app:= New().Configure(...)`
  233. func (app *Application) Configure(configurators ...Configurator) *Application {
  234. for _, cfg := range configurators {
  235. if cfg != nil {
  236. cfg(app)
  237. }
  238. }
  239. return app
  240. }
  241. // ConfigurationReadOnly returns an object which doesn't allow field writing.
  242. func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly {
  243. return app.config
  244. }
  245. // Logger returns the golog logger instance(pointer) that is being used inside the "app".
  246. //
  247. // Available levels:
  248. // - "disable"
  249. // - "fatal"
  250. // - "error"
  251. // - "warn"
  252. // - "info"
  253. // - "debug"
  254. // Usage: app.Logger().SetLevel("error")
  255. // Or set the level through Configurartion's LogLevel or WithLogLevel functional option.
  256. // Defaults to "info" level.
  257. //
  258. // Callers can use the application's logger which is
  259. // the same `golog.Default.LastChild()` logger,
  260. // to print custom logs too.
  261. // Usage:
  262. // app.Logger().Error/Errorf("...")
  263. // app.Logger().Warn/Warnf("...")
  264. // app.Logger().Info/Infof("...")
  265. // app.Logger().Debug/Debugf("...")
  266. //
  267. // Setting one or more outputs: app.Logger().SetOutput(io.Writer...)
  268. // Adding one or more outputs : app.Logger().AddOutput(io.Writer...)
  269. //
  270. // Adding custom levels requires import of the `github.com/kataras/golog` package:
  271. //
  272. // First we create our level to a golog.Level
  273. // in order to be used in the Log functions.
  274. // var SuccessLevel golog.Level = 6
  275. // Register our level, just three fields.
  276. // golog.Levels[SuccessLevel] = &golog.LevelMetadata{
  277. // Name: "success",
  278. // RawText: "[SUCC]",
  279. // // ColorfulText (Green Color[SUCC])
  280. // ColorfulText: "\x1b[32m[SUCC]\x1b[0m",
  281. // }
  282. //
  283. // Usage:
  284. // app.Logger().SetLevel("success")
  285. // app.Logger().Logf(SuccessLevel, "a custom leveled log message")
  286. func (app *Application) Logger() *golog.Logger {
  287. return app.logger
  288. }
  289. // IsDebug reports whether the application is running
  290. // under debug/development mode.
  291. // It's just a shortcut of Logger().Level >= golog.DebugLevel.
  292. // The same method existss as Context.IsDebug() too.
  293. func (app *Application) IsDebug() bool {
  294. return app.logger.Level >= golog.DebugLevel
  295. }
  296. // I18nReadOnly returns the i18n's read-only features.
  297. // See `I18n` method for more.
  298. func (app *Application) I18nReadOnly() context.I18nReadOnly {
  299. return app.I18n
  300. }
  301. // Validate validates a value and returns nil if passed or
  302. // the failure reason if does not.
  303. func (app *Application) Validate(v interface{}) error {
  304. if app.Validator == nil {
  305. return nil
  306. }
  307. // val := reflect.ValueOf(v)
  308. // if val.Kind() == reflect.Ptr && !val.IsNil() {
  309. // val = val.Elem()
  310. // }
  311. // if val.Kind() == reflect.Struct && val.Type() != timeType {
  312. // return app.Validator.Struct(v)
  313. // }
  314. // no need to check the kind, underline lib does it but in the future this may change (look above).
  315. err := app.Validator.Struct(v)
  316. if err != nil {
  317. if !strings.HasPrefix(err.Error(), "validator: ") {
  318. return err
  319. }
  320. }
  321. return nil
  322. }
  323. func newMinifier() *minify.M {
  324. m := minify.New()
  325. m.AddFunc("text/css", css.Minify)
  326. m.AddFunc("text/html", html.Minify)
  327. m.AddFunc("image/svg+xml", svg.Minify)
  328. m.AddFuncRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), js.Minify)
  329. m.AddFuncRegexp(regexp.MustCompile("[/+]json$"), json.Minify)
  330. m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), xml.Minify)
  331. return m
  332. }
  333. // Minify is a middleware which minifies the responses
  334. // based on the response content type.
  335. // Note that minification might be slower, caching is advised.
  336. // Customize the minifier through `Application.Minifier()`.
  337. // Usage:
  338. // app.Use(iris.Minify)
  339. func Minify(ctx Context) {
  340. w := ctx.Application().Minifier().ResponseWriter(ctx.ResponseWriter().Naive(), ctx.Request())
  341. // Note(@kataras):
  342. // We don't use defer w.Close()
  343. // because this response writer holds a sync.WaitGroup under the hoods
  344. // and we MUST be sure that its wg.Wait is called on request cancelation
  345. // and not in the end of handlers chain execution
  346. // (which if running a time-consuming task it will delay its resource release).
  347. ctx.OnCloseErr(w.Close)
  348. ctx.ResponseWriter().SetWriter(w)
  349. ctx.Next()
  350. }
  351. // Minifier returns the minifier instance.
  352. // By default it can minifies:
  353. // - text/html
  354. // - text/css
  355. // - image/svg+xml
  356. // - application/text(javascript, ecmascript, json, xml).
  357. // Use that instance to add custom Minifiers before server ran.
  358. func (app *Application) Minifier() *minify.M {
  359. return app.minifier
  360. }
  361. // RegisterView registers a view engine for the application.
  362. // Children can register their own too. If no Party view Engine is registered
  363. // then this one will be used to render the templates instead.
  364. func (app *Application) RegisterView(viewEngine view.Engine) {
  365. app.view.Register(viewEngine)
  366. }
  367. // View executes and writes the result of a template file to the writer.
  368. //
  369. // First parameter is the writer to write the parsed template.
  370. // Second parameter is the relative, to templates directory, template filename, including extension.
  371. // Third parameter is the layout, can be empty string.
  372. // Forth parameter is the bindable data to the template, can be nil.
  373. //
  374. // Use context.View to render templates to the client instead.
  375. // Returns an error on failure, otherwise nil.
  376. func (app *Application) View(writer io.Writer, filename string, layout string, bindingData interface{}) error {
  377. if !app.view.Registered() {
  378. err := errors.New("view engine is missing, use `RegisterView`")
  379. app.logger.Error(err)
  380. return err
  381. }
  382. return app.view.ExecuteWriter(writer, filename, layout, bindingData)
  383. }
  384. // GetContextPool returns the Iris sync.Pool which holds the contexts values.
  385. // Iris automatically releases the request context, so you don't have to use it.
  386. // It's only useful to manually release the context on cases that connection
  387. // is hijacked by a third-party middleware and the http handler return too fast.
  388. func (app *Application) GetContextPool() *context.Pool {
  389. return app.ContextPool
  390. }
  391. // SetContextErrorHandler can optionally register a handler to handle
  392. // and fire a customized error body to the client on JSON write failures.
  393. //
  394. // ExampleCode:
  395. //
  396. // type contextErrorHandler struct{}
  397. // func (e *contextErrorHandler) HandleContextError(ctx iris.Context, err error) {
  398. // errors.HandleError(ctx, err)
  399. // }
  400. // ...
  401. // app.SetContextErrorHandler(new(contextErrorHandler))
  402. func (app *Application) SetContextErrorHandler(errHandler context.ErrorHandler) *Application {
  403. app.contextErrorHandler = errHandler
  404. return app
  405. }
  406. // GetContextErrorHandler returns the handler which handles errors
  407. // on JSON write failures.
  408. func (app *Application) GetContextErrorHandler() context.ErrorHandler {
  409. return app.contextErrorHandler
  410. }
  411. // ConfigureHost accepts one or more `host#Configuration`, these configurators functions
  412. // can access the host created by `app.Run` or `app.Listen`,
  413. // they're being executed when application is ready to being served to the public.
  414. //
  415. // It's an alternative way to interact with a host that is automatically created by
  416. // `app.Run`.
  417. //
  418. // These "configurators" can work side-by-side with the `iris#Addr, iris#Server, iris#TLS, iris#AutoTLS, iris#Listener`
  419. // final arguments("hostConfigs") too.
  420. //
  421. // Note that these application's host "configurators" will be shared with the rest of
  422. // the hosts that this app will may create (using `app.NewHost`), meaning that
  423. // `app.NewHost` will execute these "configurators" everytime that is being called as well.
  424. //
  425. // These "configurators" should be registered before the `app.Run` or `host.Serve/Listen` functions.
  426. func (app *Application) ConfigureHost(configurators ...host.Configurator) *Application {
  427. app.mu.Lock()
  428. app.hostConfigurators = append(app.hostConfigurators, configurators...)
  429. app.mu.Unlock()
  430. return app
  431. }
  432. const serverLoggerPrefix = "[HTTP Server] "
  433. type customHostServerLogger struct { // see #1875
  434. parent io.Writer
  435. ignoreLogs [][]byte
  436. }
  437. var newLineBytes = []byte("\n")
  438. func newCustomHostServerLogger(w io.Writer, ignoreLogs []string) *customHostServerLogger {
  439. prefixAsByteSlice := []byte(serverLoggerPrefix)
  440. // build the ignore lines.
  441. ignoreLogsAsByteSlice := make([][]byte, 0, len(ignoreLogs))
  442. for _, s := range ignoreLogs {
  443. ignoreLogsAsByteSlice = append(ignoreLogsAsByteSlice, append(prefixAsByteSlice, []byte(s)...)) // append([]byte(s), newLineBytes...)
  444. }
  445. return &customHostServerLogger{
  446. parent: w,
  447. ignoreLogs: ignoreLogsAsByteSlice,
  448. }
  449. }
  450. func (l *customHostServerLogger) Write(p []byte) (int, error) {
  451. for _, ignoredLogBytes := range l.ignoreLogs {
  452. if bytes.Equal(bytes.TrimSuffix(p, newLineBytes), ignoredLogBytes) {
  453. return 0, nil
  454. }
  455. }
  456. return l.parent.Write(p)
  457. }
  458. // this may change during parallel jobs (see Application.NonBlocking & Wait).
  459. func (app *Application) getVHost() string {
  460. app.mu.RLock()
  461. vhost := app.config.VHost
  462. app.mu.RUnlock()
  463. return vhost
  464. }
  465. func (app *Application) setVHost(vhost string) {
  466. app.mu.Lock()
  467. app.config.VHost = vhost
  468. app.mu.Unlock()
  469. }
  470. // NewHost accepts a standard *http.Server object,
  471. // completes the necessary missing parts of that "srv"
  472. // and returns a new, ready-to-use, host (supervisor).
  473. func (app *Application) NewHost(srv *http.Server) *host.Supervisor {
  474. if app.getVHost() == "" { // vhost now is useful for router subdomain on wildcard subdomains,
  475. // in order to correct decide what to do on:
  476. // mydomain.com -> invalid
  477. // localhost -> invalid
  478. // sub.mydomain.com -> valid
  479. // sub.localhost -> valid
  480. // we need the host (without port if 80 or 443) in order to validate these, so:
  481. app.setVHost(netutil.ResolveVHost(srv.Addr))
  482. } else {
  483. context.GetDomain = func(_ string) string { // #1886
  484. return app.config.VHost // GetVHost: here we don't need mutex protection as it's request-time and all modifications are already made.
  485. }
  486. } // before lock.
  487. app.mu.Lock()
  488. defer app.mu.Unlock()
  489. // set the server's handler to the framework's router
  490. if srv.Handler == nil {
  491. srv.Handler = app.Router
  492. }
  493. // check if different ErrorLog provided, if not bind it with the framework's logger.
  494. if srv.ErrorLog == nil {
  495. serverLogger := newCustomHostServerLogger(app.logger.Printer.Output, app.config.IgnoreServerErrors)
  496. srv.ErrorLog = log.New(serverLogger, serverLoggerPrefix, 0)
  497. }
  498. if addr := srv.Addr; addr == "" {
  499. addr = ":8080"
  500. if len(app.Hosts) > 0 {
  501. if v := app.Hosts[0].Server.Addr; v != "" {
  502. addr = v
  503. }
  504. }
  505. srv.Addr = addr
  506. }
  507. // app.logger.Debugf("Host: addr is %s", srv.Addr)
  508. // create the new host supervisor
  509. // bind the constructed server and return it
  510. su := host.New(srv)
  511. // app.logger.Debugf("Host: virtual host is %s", app.config.VHost)
  512. // the below schedules some tasks that will run among the server
  513. if !app.config.DisableStartupLog {
  514. printer := app.logger.Printer.Output
  515. hostPrinter := host.WriteStartupLogOnServe(printer)
  516. if len(app.Hosts) == 0 { // print the version info on the first running host.
  517. su.RegisterOnServe(func(h host.TaskHost) {
  518. hasBuildInfo := BuildTime != "" && BuildRevision != ""
  519. tab := " "
  520. if hasBuildInfo {
  521. tab = " "
  522. }
  523. fmt.Fprintf(printer, "Iris Version:%s%s\n", tab, Version)
  524. if hasBuildInfo {
  525. fmt.Fprintf(printer, "Build Time: %s\nBuild Revision: %s\n", BuildTime, BuildRevision)
  526. }
  527. fmt.Fprintln(printer)
  528. hostPrinter(h)
  529. })
  530. } else {
  531. su.RegisterOnServe(hostPrinter)
  532. }
  533. // app.logger.Debugf("Host: register startup notifier")
  534. }
  535. if !app.config.DisableInterruptHandler {
  536. // when CTRL/CMD+C pressed.
  537. shutdownTimeout := 10 * time.Second
  538. RegisterOnInterrupt(host.ShutdownOnInterrupt(su, shutdownTimeout))
  539. // app.logger.Debugf("Host: register server shutdown on interrupt(CTRL+C/CMD+C)")
  540. }
  541. su.IgnoredErrors = append(su.IgnoredErrors, app.config.IgnoreServerErrors...)
  542. if len(su.IgnoredErrors) > 0 {
  543. app.logger.Debugf("Host: server will ignore the following errors: %s", su.IgnoredErrors)
  544. }
  545. su.Configure(app.hostConfigurators...)
  546. app.Hosts = append(app.Hosts, su)
  547. return su
  548. }
  549. // func (app *Application) OnShutdown(closers ...func()) {
  550. // for _,cb := range closers {
  551. // if cb == nil {
  552. // continue
  553. // }
  554. // RegisterOnInterrupt(cb)
  555. // }
  556. // }
  557. // Shutdown gracefully terminates all the application's server hosts and any tunnels.
  558. // Returns an error on the first failure, otherwise nil.
  559. func (app *Application) Shutdown(ctx stdContext.Context) error {
  560. app.mu.Lock()
  561. defer app.mu.Unlock()
  562. defer app.setRunError(ErrServerClosed) // make sure to set the error so any .Wait calls return.
  563. for i, su := range app.Hosts {
  564. app.logger.Debugf("Host[%d]: Shutdown now", i)
  565. if err := su.Shutdown(ctx); err != nil {
  566. app.logger.Debugf("Host[%d]: Error while trying to shutdown", i)
  567. return err
  568. }
  569. }
  570. for _, t := range app.config.Tunneling.Tunnels {
  571. if t.Name == "" {
  572. continue
  573. }
  574. if err := app.config.Tunneling.StopTunnel(t); err != nil {
  575. return err
  576. }
  577. }
  578. return nil
  579. }
  580. // Build sets up, once, the framework.
  581. // It builds the default router with its default macros
  582. // and the template functions that are very-closed to iris.
  583. //
  584. // If error occurred while building the Application, the returns type of error will be an *errgroup.Group
  585. // which let the callers to inspect the errors and cause, usage:
  586. //
  587. // import "github.com/kataras/iris/v12/core/errgroup"
  588. //
  589. // errgroup.Walk(app.Build(), func(typ interface{}, err error) {
  590. // app.Logger().Errorf("%s: %s", typ, err)
  591. // })
  592. func (app *Application) Build() error {
  593. if app.builded {
  594. return nil
  595. }
  596. if cb := app.OnBuild; cb != nil {
  597. if err := cb(); err != nil {
  598. return fmt.Errorf("build: %w", err)
  599. }
  600. }
  601. // start := time.Now()
  602. app.builded = true // even if fails.
  603. // check if a prior app.Logger().SetLevel called and if not
  604. // then set the defined configuration's log level.
  605. if app.logger.Level == golog.InfoLevel /* the default level */ {
  606. app.logger.SetLevel(app.config.LogLevel)
  607. }
  608. if app.defaultMode { // the app.I18n and app.View will be not available until Build.
  609. if !app.I18n.Loaded() {
  610. for _, s := range []string{"./locales/*/*", "./locales/*", "./translations"} {
  611. if _, err := os.Stat(s); err != nil {
  612. continue
  613. }
  614. if err := app.I18n.Load(s); err != nil {
  615. continue
  616. }
  617. app.I18n.SetDefault("en-US")
  618. break
  619. }
  620. }
  621. if !app.view.Registered() {
  622. for _, s := range []string{"./views", "./templates", "./web/views"} {
  623. if _, err := os.Stat(s); err != nil {
  624. continue
  625. }
  626. app.RegisterView(HTML(s, ".html"))
  627. break
  628. }
  629. }
  630. }
  631. if app.I18n.Loaded() {
  632. // {{ tr "lang" "key" arg1 arg2 }}
  633. app.view.AddFunc("tr", app.I18n.Tr)
  634. app.Router.PrependRouterWrapper(app.I18n.Wrapper())
  635. }
  636. if app.view.Registered() {
  637. app.logger.Debugf("Application: view engine %q is registered", app.view.Name())
  638. // view engine
  639. // here is where we declare the closed-relative framework functions.
  640. // Each engine has their defaults, i.e yield,render,render_r,partial, params...
  641. rv := router.NewRoutePathReverser(app.APIBuilder)
  642. app.view.AddFunc("urlpath", rv.Path)
  643. // app.view.AddFunc("url", rv.URL)
  644. if err := app.view.Load(); err != nil {
  645. return fmt.Errorf("build: view engine: %v", err)
  646. }
  647. }
  648. if !app.Router.Downgraded() {
  649. // router
  650. if _, err := injectLiveReload(app); err != nil {
  651. return fmt.Errorf("build: inject live reload: failed: %v", err)
  652. }
  653. if app.config.ForceLowercaseRouting {
  654. // This should always be executed first.
  655. app.Router.PrependRouterWrapper(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
  656. r.Host = strings.ToLower(r.Host)
  657. r.URL.Host = strings.ToLower(r.URL.Host)
  658. r.URL.Path = strings.ToLower(r.URL.Path)
  659. next(w, r)
  660. })
  661. }
  662. // create the request handler, the default routing handler
  663. routerHandler := router.NewDefaultHandler(app.config, app.logger)
  664. err := app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder, false)
  665. if err != nil {
  666. return fmt.Errorf("build: router: %w", err)
  667. }
  668. app.HTTPErrorHandler = routerHandler
  669. if app.config.Timeout > 0 {
  670. app.Router.SetTimeoutHandler(app.config.Timeout, app.config.TimeoutMessage)
  671. app.ConfigureHost(func(su *Supervisor) {
  672. if su.Server.ReadHeaderTimeout == 0 {
  673. su.Server.ReadHeaderTimeout = app.config.Timeout + 5*time.Second
  674. }
  675. if su.Server.ReadTimeout == 0 {
  676. su.Server.ReadTimeout = app.config.Timeout + 10*time.Second
  677. }
  678. if su.Server.WriteTimeout == 0 {
  679. su.Server.WriteTimeout = app.config.Timeout + 15*time.Second
  680. }
  681. if su.Server.IdleTimeout == 0 {
  682. su.Server.IdleTimeout = app.config.Timeout + 25*time.Second
  683. }
  684. })
  685. }
  686. // re-build of the router from outside can be done with
  687. // app.RefreshRouter()
  688. }
  689. // if end := time.Since(start); end.Seconds() > 5 {
  690. // app.logger.Debugf("Application: build took %s", time.Since(start))
  691. return nil
  692. }
  693. // Runner is just an interface which accepts the framework instance
  694. // and returns an error.
  695. //
  696. // It can be used to register a custom runner with `Run` in order
  697. // to set the framework's server listen action.
  698. //
  699. // Currently `Runner` is being used to declare the builtin server listeners.
  700. //
  701. // See `Run` for more.
  702. type Runner func(*Application) error
  703. // Listener can be used as an argument for the `Run` method.
  704. // It can start a server with a custom net.Listener via server's `Serve`.
  705. //
  706. // Second argument is optional, it accepts one or more
  707. // `func(*host.Configurator)` that are being executed
  708. // on that specific host that this function will create to start the server.
  709. // Via host configurators you can configure the back-end host supervisor,
  710. // i.e to add events for shutdown, serve or error.
  711. // An example of this use case can be found at:
  712. // https://github.com/kataras/iris/blob/main/_examples/http-server/notify-on-shutdown/main.go
  713. // Look at the `ConfigureHost` too.
  714. //
  715. // See `Run` for more.
  716. func Listener(l net.Listener, hostConfigs ...host.Configurator) Runner {
  717. return func(app *Application) error {
  718. app.config.SetVHost(netutil.ResolveVHost(l.Addr().String()))
  719. return app.NewHost(&http.Server{Addr: l.Addr().String()}).
  720. Configure(hostConfigs...).
  721. Serve(l)
  722. }
  723. }
  724. // Server can be used as an argument for the `Run` method.
  725. // It can start a server with a *http.Server.
  726. //
  727. // Second argument is optional, it accepts one or more
  728. // `func(*host.Configurator)` that are being executed
  729. // on that specific host that this function will create to start the server.
  730. // Via host configurators you can configure the back-end host supervisor,
  731. // i.e to add events for shutdown, serve or error.
  732. // An example of this use case can be found at:
  733. // https://github.com/kataras/iris/blob/main/_examples/http-server/notify-on-shutdown/main.go
  734. // Look at the `ConfigureHost` too.
  735. //
  736. // See `Run` for more.
  737. func Server(srv *http.Server, hostConfigs ...host.Configurator) Runner {
  738. return func(app *Application) error {
  739. return app.NewHost(srv).
  740. Configure(hostConfigs...).
  741. ListenAndServe()
  742. }
  743. }
  744. // Addr can be used as an argument for the `Run` method.
  745. // It accepts a host address which is used to build a server
  746. // and a listener which listens on that host and port.
  747. //
  748. // Addr should have the form of [host]:port, i.e localhost:8080 or :8080.
  749. //
  750. // Second argument is optional, it accepts one or more
  751. // `func(*host.Configurator)` that are being executed
  752. // on that specific host that this function will create to start the server.
  753. // Via host configurators you can configure the back-end host supervisor,
  754. // i.e to add events for shutdown, serve or error.
  755. // An example of this use case can be found at:
  756. // https://github.com/kataras/iris/blob/main/_examples/http-server/notify-on-shutdown/main.go
  757. // Look at the `ConfigureHost` too.
  758. //
  759. // See `Run` for more.
  760. func Addr(addr string, hostConfigs ...host.Configurator) Runner {
  761. return func(app *Application) error {
  762. return app.NewHost(&http.Server{Addr: addr}).
  763. Configure(hostConfigs...).
  764. ListenAndServe()
  765. }
  766. }
  767. var (
  768. // TLSNoRedirect is a `host.Configurator` which can be passed as last argument
  769. // to the `TLS` runner function. It disables the automatic
  770. // registration of redirection from "http://" to "https://" requests.
  771. // Applies only to the `TLS` runner.
  772. // See `AutoTLSNoRedirect` to register a custom fallback server for `AutoTLS` runner.
  773. TLSNoRedirect = func(su *host.Supervisor) { su.NoRedirect() }
  774. // AutoTLSNoRedirect is a `host.Configurator`.
  775. // It registers a fallback HTTP/1.1 server for the `AutoTLS` one.
  776. // The function accepts the letsencrypt wrapper and it
  777. // should return a valid instance of http.Server which its handler should be the result
  778. // of the "acmeHandler" wrapper.
  779. // Usage:
  780. // getServer := func(acme func(http.Handler) http.Handler) *http.Server {
  781. // srv := &http.Server{Handler: acme(yourCustomHandler), ...otherOptions}
  782. // go srv.ListenAndServe()
  783. // return srv
  784. // }
  785. // app.Run(iris.AutoTLS(":443", "example.com example2.com", "mail@example.com", getServer))
  786. //
  787. // Note that if Server.Handler is nil then the server is automatically ran
  788. // by the framework and the handler set to automatic redirection, it's still
  789. // a valid option when the caller wants just to customize the server's fields (except Addr).
  790. // With this host configurator the caller can customize the server
  791. // that letsencrypt relies to perform the challenge.
  792. // LetsEncrypt Certification Manager relies on http://example.com/.well-known/acme-challenge/<TOKEN>.
  793. AutoTLSNoRedirect = func(getFallbackServer func(acmeHandler func(fallback http.Handler) http.Handler) *http.Server) host.Configurator {
  794. return func(su *host.Supervisor) {
  795. su.NoRedirect()
  796. su.Fallback = getFallbackServer
  797. }
  798. }
  799. )
  800. // TLS can be used as an argument for the `Run` method.
  801. // It will start the Application's secure server.
  802. //
  803. // Use it like you used to use the http.ListenAndServeTLS function.
  804. //
  805. // Addr should have the form of [host]:port, i.e localhost:443 or :443.
  806. // "certFileOrContents" & "keyFileOrContents" should be filenames with their extensions
  807. // or raw contents of the certificate and the private key.
  808. //
  809. // Last argument is optional, it accepts one or more
  810. // `func(*host.Configurator)` that are being executed
  811. // on that specific host that this function will create to start the server.
  812. // Via host configurators you can configure the back-end host supervisor,
  813. // i.e to add events for shutdown, serve or error.
  814. // An example of this use case can be found at:
  815. // https://github.com/kataras/iris/blob/main/_examples/http-server/notify-on-shutdown/main.go
  816. // Look at the `ConfigureHost` too.
  817. //
  818. // See `Run` for more.
  819. func TLS(addr string, certFileOrContents, keyFileOrContents string, hostConfigs ...host.Configurator) Runner {
  820. return func(app *Application) error {
  821. return app.NewHost(&http.Server{Addr: addr}).
  822. Configure(hostConfigs...).
  823. ListenAndServeTLS(certFileOrContents, keyFileOrContents)
  824. }
  825. }
  826. // AutoTLS can be used as an argument for the `Run` method.
  827. // It will start the Application's secure server using
  828. // certifications created on the fly by the "autocert" golang/x package,
  829. // so localhost may not be working, use it at "production" machine.
  830. //
  831. // Addr should have the form of [host]:port, i.e mydomain.com:443.
  832. //
  833. // The whitelisted domains are separated by whitespace in "domain" argument,
  834. // i.e "iris-go.com", can be different than "addr".
  835. // If empty, all hosts are currently allowed. This is not recommended,
  836. // as it opens a potential attack where clients connect to a server
  837. // by IP address and pretend to be asking for an incorrect host name.
  838. // Manager will attempt to obtain a certificate for that host, incorrectly,
  839. // eventually reaching the CA's rate limit for certificate requests
  840. // and making it impossible to obtain actual certificates.
  841. //
  842. // For an "e-mail" use a non-public one, letsencrypt needs that for your own security.
  843. //
  844. // Note: `AutoTLS` will start a new server for you
  845. // which will redirect all http versions to their https, including subdomains as well.
  846. //
  847. // Last argument is optional, it accepts one or more
  848. // `func(*host.Configurator)` that are being executed
  849. // on that specific host that this function will create to start the server.
  850. // Via host configurators you can configure the back-end host supervisor,
  851. // i.e to add events for shutdown, serve or error.
  852. // An example of this use case can be found at:
  853. // https://github.com/kataras/iris/blob/main/_examples/http-server/notify-on-shutdown/main.go
  854. // Look at the `ConfigureHost` too.
  855. //
  856. // Usage:
  857. // app.Run(iris.AutoTLS("iris-go.com:443", "iris-go.com www.iris-go.com", "mail@example.com"))
  858. //
  859. // See `Run` and `core/host/Supervisor#ListenAndServeAutoTLS` for more.
  860. func AutoTLS(
  861. addr string,
  862. domain string, email string,
  863. hostConfigs ...host.Configurator,
  864. ) Runner {
  865. return func(app *Application) error {
  866. return app.NewHost(&http.Server{Addr: addr}).
  867. Configure(hostConfigs...).
  868. ListenAndServeAutoTLS(domain, email, "letscache")
  869. }
  870. }
  871. // Raw can be used as an argument for the `Run` method.
  872. // It accepts any (listen) function that returns an error,
  873. // this function should be block and return an error
  874. // only when the server exited or a fatal error caused.
  875. //
  876. // With this option you're not limited to the servers
  877. // that iris can run by-default.
  878. //
  879. // See `Run` for more.
  880. func Raw(f func() error) Runner {
  881. return func(app *Application) error {
  882. app.logger.Debugf("HTTP Server will start from unknown, external function")
  883. return f()
  884. }
  885. }
  886. var (
  887. // ErrServerClosed is logged by the standard net/http server when the server is terminated.
  888. // Ignore it by passing this error to the `iris.WithoutServerError` configurator
  889. // on `Application.Run/Listen` method.
  890. //
  891. // An alias of the `http#ErrServerClosed`.
  892. ErrServerClosed = http.ErrServerClosed
  893. // ErrURLQuerySemicolon is logged by the standard net/http server when
  894. // the request contains a semicolon (;) wihch, after go1.17 it's not used as a key-value separator character.
  895. //
  896. // Ignore it by passing this error to the `iris.WithoutServerError` configurator
  897. // on `Application.Run/Listen` method.
  898. //
  899. // An alias of the `http#ErrServerClosed`.
  900. ErrURLQuerySemicolon = errors.New("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192")
  901. )
  902. // Listen builds the application and starts the server
  903. // on the TCP network address "host:port" which
  904. // handles requests on incoming connections.
  905. //
  906. // Listen always returns a non-nil error except
  907. // when NonBlocking option is being passed, so the error goes to the Wait method.
  908. // Ignore specific errors by using an `iris.WithoutServerError(iris.ErrServerClosed)`
  909. // as a second input argument.
  910. //
  911. // Listen is a shortcut of `app.Run(iris.Addr(hostPort, withOrWithout...))`.
  912. // See `Run` for details.
  913. func (app *Application) Listen(hostPort string, withOrWithout ...Configurator) error {
  914. return app.Run(Addr(hostPort), withOrWithout...)
  915. }
  916. // Run builds the framework and starts the desired `Runner` with or without configuration edits.
  917. //
  918. // Run should be called only once per Application instance, it blocks like http.Server.
  919. //
  920. // If more than one server needed to run on the same iris instance
  921. // then create a new host and run it manually by `go NewHost(*http.Server).Serve/ListenAndServe` etc...
  922. // or use an already created host:
  923. // h := NewHost(*http.Server)
  924. // Run(Raw(h.ListenAndServe), WithCharset("utf-8"), WithRemoteAddrHeader("CF-Connecting-IP"))
  925. //
  926. // The Application can go online with any type of server or iris's host with the help of
  927. // the following runners:
  928. // `Listener`, `Server`, `Addr`, `TLS`, `AutoTLS` and `Raw`.
  929. func (app *Application) Run(serve Runner, withOrWithout ...Configurator) error {
  930. app.Configure(withOrWithout...)
  931. if err := app.Build(); err != nil {
  932. app.logger.Error(err)
  933. return err
  934. }
  935. app.ConfigureHost(func(host *Supervisor) {
  936. host.SocketSharding = app.config.SocketSharding
  937. host.KeepAlive = app.config.KeepAlive
  938. })
  939. app.tryStartTunneling()
  940. if len(app.Hosts) > 0 {
  941. app.logger.Debugf("Application: running using %d host(s)", len(app.Hosts)+1 /* +1 the current */)
  942. }
  943. if app.config.NonBlocking {
  944. go func() {
  945. err := app.serve(serve)
  946. if err != nil {
  947. app.setRunError(err)
  948. }
  949. }()
  950. return nil
  951. }
  952. // this will block until an error(unless supervisor's DeferFlow called from a Task)
  953. // or NonBlocking was passed (see above).
  954. return app.serve(serve)
  955. }
  956. func (app *Application) serve(serve Runner) error {
  957. err := serve(app)
  958. if err != nil {
  959. app.logger.Error(err)
  960. }
  961. return err
  962. }
  963. func (app *Application) setRunError(err error) {
  964. app.runErrorMu.Lock()
  965. app.runError = err
  966. app.runErrorMu.Unlock()
  967. }
  968. func (app *Application) getRunError() error {
  969. app.runErrorMu.RLock()
  970. err := app.runError
  971. app.runErrorMu.RUnlock()
  972. return err
  973. }
  974. // Wait blocks the main goroutine until the server application is up and running.
  975. // Useful only when `Run` is called with `iris.NonBlocking()` option.
  976. func (app *Application) Wait(ctx stdContext.Context) error {
  977. if !app.config.NonBlocking {
  978. return nil
  979. }
  980. // First check if there is an error already from the app.Run.
  981. if err := app.getRunError(); err != nil {
  982. return err
  983. }
  984. // Set the base for exponential backoff.
  985. base := 2.0
  986. // Get the maximum number of retries by context or force to 7 retries.
  987. var maxRetries int
  988. // Get the deadline of the context.
  989. if deadline, ok := ctx.Deadline(); ok {
  990. now := time.Now()
  991. timeout := deadline.Sub(now)
  992. maxRetries = getMaxRetries(timeout, base)
  993. } else {
  994. maxRetries = 7 // 256 seconds max.
  995. }
  996. // Set the initial retry interval.
  997. retryInterval := time.Second
  998. return app.tryConnect(ctx, maxRetries, retryInterval, base)
  999. }
  1000. // getMaxRetries calculates the maximum number of retries from the retry interval and the base.
  1001. func getMaxRetries(retryInterval time.Duration, base float64) int {
  1002. // Convert the retry interval to seconds.
  1003. seconds := retryInterval.Seconds()
  1004. // Apply the inverse formula.
  1005. retries := math.Log(seconds)/math.Log(base) - 1
  1006. return int(math.Round(retries))
  1007. }
  1008. // tryConnect tries to connect to the server with the given context and retry parameters.
  1009. func (app *Application) tryConnect(ctx stdContext.Context, maxRetries int, retryInterval time.Duration, base float64) error {
  1010. // Try to connect to the server in a loop.
  1011. for i := 0; i < maxRetries; i++ {
  1012. // Check the context before each attempt.
  1013. select {
  1014. case <-ctx.Done():
  1015. // Context is canceled, return the context error.
  1016. return ctx.Err()
  1017. default:
  1018. address := app.getVHost() // Get this server's listening address.
  1019. if address == "" {
  1020. i-- // Note that this may be modified at another go routine of the serve method. So it may be empty at first chance. So retry fetching the VHost every 1 second.
  1021. time.Sleep(time.Second)
  1022. continue
  1023. }
  1024. // Context is not canceled, proceed with the attempt.
  1025. conn, err := net.Dial("tcp", address)
  1026. if err == nil {
  1027. // Connection successful, close the connection and return nil.
  1028. conn.Close()
  1029. return nil // exit.
  1030. } // ignore error.
  1031. // Connection failed, wait for the retry interval and try again.
  1032. time.Sleep(retryInterval)
  1033. // After each failed attempt, check the server Run's error again.
  1034. if err := app.getRunError(); err != nil {
  1035. return err
  1036. }
  1037. // Increase the retry interval by the base raised to the power of the number of attempts.
  1038. /*
  1039. 0 2 seconds
  1040. 1 4 seconds
  1041. 2 8 seconds
  1042. 3 ~16 seconds
  1043. 4 ~32 seconds
  1044. 5 ~64 seconds
  1045. 6 ~128 seconds
  1046. 7 ~256 seconds
  1047. 8 ~512 seconds
  1048. ...
  1049. */
  1050. retryInterval = time.Duration(math.Pow(base, float64(i+1))) * time.Second
  1051. }
  1052. }
  1053. // All attempts failed, return an error.
  1054. return fmt.Errorf("failed to connect to the server after %d retries", maxRetries)
  1055. }
  1056. // https://ngrok.com/docs
  1057. func (app *Application) tryStartTunneling() {
  1058. if len(app.config.Tunneling.Tunnels) == 0 {
  1059. return
  1060. }
  1061. app.ConfigureHost(func(su *host.Supervisor) {
  1062. su.RegisterOnServe(func(h host.TaskHost) {
  1063. publicAddrs, err := tunnel.Start(app.config.Tunneling)
  1064. if err != nil {
  1065. app.logger.Errorf("Host: tunneling error: %v", err)
  1066. return
  1067. }
  1068. publicAddr := publicAddrs[0]
  1069. // to make subdomains resolution still based on this new remote, public addresses.
  1070. app.setVHost(publicAddr[strings.Index(publicAddr, "://")+3:])
  1071. directLog := []byte(fmt.Sprintf("• Public Address: %s\n", publicAddr))
  1072. app.logger.Printer.Write(directLog) // nolint:errcheck
  1073. })
  1074. })
  1075. }